astar_collator/parachain/
shell_upgrade.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19//! Utility for the upgrade from shell to a parachain runtime that implements Aura.
20use astar_primitives::*;
21use cumulus_primitives_core::relay_chain::PersistedValidationData;
22use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
23use fc_rpc::pending::ConsensusDataProvider;
24use sc_client_api::{AuxStore, UsageProvider};
25use sc_consensus::{import_queue::Verifier as VerifierT, BlockImportParams};
26use sp_api::{ApiExt, ProvideRuntimeApi};
27use sp_consensus_aura::{
28    digests::CompatibleDigestItem,
29    sr25519::{AuthorityId as AuraId, AuthoritySignature},
30    AuraApi,
31};
32use sp_inherents::{CreateInherentDataProviders, Error, InherentData};
33use sp_runtime::{
34    traits::{Block as BlockT, Header as HeaderT},
35    Digest, DigestItem,
36};
37use sp_timestamp::TimestampInherentData;
38use std::{marker::PhantomData, sync::Arc};
39
40pub struct Verifier<Client> {
41    pub client: Arc<Client>,
42    pub aura_verifier: Box<dyn VerifierT<Block>>,
43    pub relay_chain_verifier: Box<dyn VerifierT<Block>>,
44}
45
46#[async_trait::async_trait]
47impl<Client> VerifierT<Block> for Verifier<Client>
48where
49    Client: ProvideRuntimeApi<Block> + Send + Sync,
50    Client::Api: AuraApi<Block, AuraId>,
51{
52    async fn verify(
53        &self,
54        block_import: BlockImportParams<Block>,
55    ) -> Result<BlockImportParams<Block>, String> {
56        if self
57            .client
58            .runtime_api()
59            .has_api::<dyn AuraApi<Block, AuraId>>(*block_import.header.parent_hash())
60            .unwrap_or(false)
61        {
62            self.aura_verifier.verify(block_import).await
63        } else {
64            self.relay_chain_verifier.verify(block_import).await
65        }
66    }
67}
68
69/// AuraConsensusDataProvider custom implementation which awaits for AuraApi to become available,
70/// until then it will return error. Shiden genesis did not start with AuraApi, therefore this
71/// implementation makes sure to return digest after AuraApi becomes available.
72/// This is currently required by EVM RPC.
73pub struct AuraConsensusDataProviderFallback<B, C> {
74    client: Arc<C>,
75    phantom_data: PhantomData<B>,
76}
77
78impl<B, C> AuraConsensusDataProviderFallback<B, C>
79where
80    B: BlockT,
81    C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B> + Send + Sync,
82    C::Api: AuraApi<B, AuraId>,
83{
84    pub fn new(client: Arc<C>) -> Self {
85        Self {
86            client,
87            phantom_data: Default::default(),
88        }
89    }
90}
91
92impl<B, C> ConsensusDataProvider<B> for AuraConsensusDataProviderFallback<B, C>
93where
94    B: BlockT,
95    C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B> + Send + Sync,
96    C::Api: AuraApi<B, AuraId>,
97{
98    fn create_digest(&self, parent: &B::Header, data: &InherentData) -> Result<Digest, Error> {
99        if self
100            .client
101            .runtime_api()
102            .has_api::<dyn AuraApi<Block, AuraId>>(parent.hash())
103            .unwrap_or_default()
104        {
105            let slot_duration = sc_consensus_aura::slot_duration(&*self.client)
106                .expect("slot_duration should be present at this point; qed.");
107            let timestamp = data
108                .timestamp_inherent_data()?
109                .expect("Timestamp is always present; qed");
110
111            let digest_item =
112                <DigestItem as CompatibleDigestItem<AuthoritySignature>>::aura_pre_digest(
113                    sp_consensus_aura::Slot::from_timestamp(timestamp, slot_duration),
114                );
115
116            return Ok(Digest {
117                logs: vec![digest_item],
118            });
119        }
120        Err(Error::Application("AuraApi is not present".into()))
121    }
122}
123
124/// Shiden genesis did not start with AuraApi, therefore this implementation makes sure to return
125/// inherent data after AuraApi becomes available.
126/// This is currently required by EVM RPC.
127pub struct PendingCrateInherentDataProvider<B, C> {
128    client: Arc<C>,
129    phantom_data: PhantomData<B>,
130}
131
132impl<B, C> PendingCrateInherentDataProvider<B, C>
133where
134    B: BlockT,
135    C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B> + Send + Sync,
136    C::Api: AuraApi<B, AuraId>,
137{
138    pub fn new(client: Arc<C>) -> Self {
139        Self {
140            client,
141            phantom_data: Default::default(),
142        }
143    }
144}
145
146#[async_trait::async_trait]
147impl<B, C> CreateInherentDataProviders<B, ()> for PendingCrateInherentDataProvider<B, C>
148where
149    B: BlockT,
150    C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B> + Send + Sync,
151    C::Api: AuraApi<B, AuraId>,
152{
153    type InherentDataProviders = (
154        sp_consensus_aura::inherents::InherentDataProvider,
155        sp_timestamp::InherentDataProvider,
156        cumulus_primitives_parachain_inherent::ParachainInherentData,
157    );
158
159    async fn create_inherent_data_providers(
160        &self,
161        parent: B::Hash,
162        _extra_args: (),
163    ) -> Result<Self::InherentDataProviders, Box<dyn std::error::Error + Send + Sync>> {
164        if !self
165            .client
166            .runtime_api()
167            .has_api::<dyn AuraApi<Block, AuraId>>(parent)
168            .unwrap_or_default()
169        {
170            return Err("AuraApi is not present".into());
171        }
172
173        let slot_duration = sc_consensus_aura::slot_duration(&*self.client)
174            .expect("slot_duration should be present at this point; qed.");
175        let current = sp_timestamp::InherentDataProvider::from_system_time();
176        let next_slot = current.timestamp().as_millis() + slot_duration.as_millis();
177        let timestamp = sp_timestamp::InherentDataProvider::new(next_slot.into());
178        let slot =
179            sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration(
180                *timestamp,
181                slot_duration,
182            );
183        // Create a dummy parachain inherent data provider which is required to pass
184        // the checks by the para chain system. We use dummy values because in the 'pending context'
185        // neither do we have access to the real values nor do we need them.
186        let (relay_parent_storage_root, relay_chain_state) =
187            RelayStateSproofBuilder::default().into_state_root_and_proof();
188        let vfp = PersistedValidationData {
189            // This is a hack to make `cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases`
190            // happy. Relay parent number can't be bigger than u32::MAX.
191            relay_parent_number: u32::MAX,
192            relay_parent_storage_root,
193            ..Default::default()
194        };
195        let parachain_inherent_data =
196            cumulus_primitives_parachain_inherent::ParachainInherentData {
197                validation_data: vfp,
198                relay_chain_state,
199                downward_messages: Default::default(),
200                horizontal_messages: Default::default(),
201                relay_parent_descendants: Default::default(),
202                collator_peer_id: Default::default(),
203            };
204        Ok((slot, timestamp, parachain_inherent_data))
205    }
206}