domain_block_preprocessor/
inherents.rs

1//! Provides functionality of adding inherent extrinsics to the Domain.
2//!
3//! Unlike Primary chain where inherent data is first derived the block author
4//! and the data is verified by the on primary runtime, domains inherents
5//! short circuit the derivation and verification of inherent data
6//! as the inherent data is directly taken from the primary block from which
7//! domain block is being built.
8//!
9//! One of the first use case for this is passing Timestamp data. Before building a
10//! domain block using a primary block, we take the current time from the primary runtime
11//! and then create an unsigned extrinsic that is put on top the bundle extrinsics.
12//!
13//! Deriving these extrinsics during fraud proof verification should be possible since
14//! verification environment will have access to consensus chain.
15
16use sp_api::ProvideRuntimeApi;
17use sp_blockchain::HeaderBackend;
18use sp_domains::{DomainId, DomainsApi};
19use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
20use sp_messenger::MessengerApi;
21use sp_runtime::traits::{Block as BlockT, NumberFor};
22use sp_timestamp::InherentType;
23use std::error::Error;
24use std::sync::Arc;
25
26pub async fn get_inherent_data<CClient, CBlock, Block>(
27    consensus_client: Arc<CClient>,
28    consensus_block_hash: CBlock::Hash,
29    parent_hash: Block::Hash,
30    domain_id: DomainId,
31) -> Result<InherentData, sp_blockchain::Error>
32where
33    CBlock: BlockT,
34    Block: BlockT,
35    CClient: ProvideRuntimeApi<CBlock> + HeaderBackend<CBlock>,
36    CClient::Api:
37        DomainsApi<CBlock, Block::Header> + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>,
38{
39    let create_inherent_data_providers =
40        CreateInherentDataProvider::new(consensus_client, Some(consensus_block_hash), domain_id);
41    let inherent_data_providers = <CreateInherentDataProvider<_, _> as CreateInherentDataProviders<
42        Block,
43        (),
44    >>::create_inherent_data_providers(
45        &create_inherent_data_providers, parent_hash, ()
46    )
47        .await?;
48    let mut inherent_data = InherentData::new();
49    inherent_data_providers
50        .provide_inherent_data(&mut inherent_data)
51        .await
52        .map_err(|err| {
53            sp_blockchain::Error::Application(Box::from(format!(
54                "failed to provide inherent data: {err:?}"
55            )))
56        })?;
57
58    Ok(inherent_data)
59}
60
61pub(crate) fn is_runtime_upgraded<CClient, CBlock, Block>(
62    consensus_client: &Arc<CClient>,
63    consensus_block_hash: CBlock::Hash,
64    domain_id: DomainId,
65) -> Result<bool, sp_blockchain::Error>
66where
67    CClient: ProvideRuntimeApi<CBlock> + HeaderBackend<CBlock>,
68    CClient::Api: DomainsApi<CBlock, Block::Header>,
69    CBlock: BlockT,
70    Block: BlockT,
71{
72    let runtime_api = consensus_client.runtime_api();
73
74    let runtime_id = runtime_api
75        .runtime_id(consensus_block_hash, domain_id)?
76        .ok_or(sp_blockchain::Error::Application(Box::from(format!(
77            "No RuntimeId found for {domain_id:?}"
78        ))))?;
79
80    let runtime_upgrades = runtime_api.runtime_upgrades(consensus_block_hash)?;
81
82    Ok(runtime_upgrades.contains(&runtime_id))
83}
84
85/// Returns new upgraded runtime if upgraded did happen in the provided consensus block.
86pub fn extract_domain_runtime_upgrade_code<CClient, CBlock, Block>(
87    consensus_client: &Arc<CClient>,
88    consensus_block_hash: CBlock::Hash,
89    domain_id: DomainId,
90) -> Result<Option<Vec<u8>>, sp_blockchain::Error>
91where
92    CClient: ProvideRuntimeApi<CBlock> + HeaderBackend<CBlock>,
93    CClient::Api: DomainsApi<CBlock, Block::Header>,
94    CBlock: BlockT,
95    Block: BlockT,
96{
97    if is_runtime_upgraded::<_, _, Block>(consensus_client, consensus_block_hash, domain_id)? {
98        let new_domain_runtime = consensus_client
99            .runtime_api()
100            .domain_runtime_code(consensus_block_hash, domain_id)?
101            .ok_or_else(|| {
102                sp_blockchain::Error::Application(Box::from(format!(
103                    "No new runtime code for {domain_id:?}"
104                )))
105            })?;
106
107        Ok(Some(new_domain_runtime))
108    } else {
109        Ok(None)
110    }
111}
112
113#[derive(Debug)]
114pub struct CreateInherentDataProvider<CClient, CBlock: BlockT> {
115    consensus_client: Arc<CClient>,
116    maybe_consensus_block_hash: Option<CBlock::Hash>,
117    domain_id: DomainId,
118}
119
120impl<CClient, CBlock: BlockT + Clone> Clone for CreateInherentDataProvider<CClient, CBlock> {
121    fn clone(&self) -> Self {
122        Self {
123            consensus_client: self.consensus_client.clone(),
124            maybe_consensus_block_hash: self.maybe_consensus_block_hash,
125            domain_id: self.domain_id,
126        }
127    }
128}
129
130impl<CClient, CBlock: BlockT> CreateInherentDataProvider<CClient, CBlock> {
131    pub fn new(
132        consensus_client: Arc<CClient>,
133        maybe_consensus_block_hash: Option<CBlock::Hash>,
134        domain_id: DomainId,
135    ) -> Self {
136        Self {
137            consensus_client,
138            maybe_consensus_block_hash,
139            domain_id,
140        }
141    }
142}
143
144#[async_trait::async_trait]
145impl<CClient, CBlock, Block> CreateInherentDataProviders<Block, ()>
146    for CreateInherentDataProvider<CClient, CBlock>
147where
148    Block: BlockT,
149    CBlock: BlockT,
150    CClient: ProvideRuntimeApi<CBlock> + HeaderBackend<CBlock>,
151    CClient::Api:
152        DomainsApi<CBlock, Block::Header> + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>,
153{
154    type InherentDataProviders = (
155        sp_timestamp::InherentDataProvider,
156        sp_block_fees::InherentDataProvider,
157        sp_executive::InherentDataProvider,
158        sp_messenger::InherentDataProvider,
159        sp_domain_sudo::InherentDataProvider,
160        sp_evm_tracker::InherentDataProvider,
161    );
162
163    async fn create_inherent_data_providers(
164        &self,
165        _parent: Block::Hash,
166        _extra_args: (),
167    ) -> Result<Self::InherentDataProviders, Box<dyn Error + Send + Sync>> {
168        // always prefer the consensus block hash that was given but eth_rpc
169        // uses this inherent provider to while fetching pending state
170        // https://github.com/paritytech/frontier/blob/master/client/rpc/src/eth/pending.rs#L70
171        // This is a non mutable call used by web3 api and using the best consensus block hash
172        // here is completely ok.
173        let consensus_block_hash = self
174            .maybe_consensus_block_hash
175            .unwrap_or(self.consensus_client.info().best_hash);
176
177        let runtime_api = self.consensus_client.runtime_api();
178
179        let timestamp = runtime_api.domain_timestamp(consensus_block_hash)?;
180        let timestamp_provider =
181            sp_timestamp::InherentDataProvider::new(InherentType::new(timestamp));
182
183        let maybe_runtime_upgrade_code = extract_domain_runtime_upgrade_code::<_, _, Block>(
184            &self.consensus_client,
185            consensus_block_hash,
186            self.domain_id,
187        )?;
188        let runtime_upgrade_provider =
189            sp_executive::InherentDataProvider::new(maybe_runtime_upgrade_code);
190
191        let consensus_chain_byte_fee =
192            runtime_api.consensus_transaction_byte_fee(consensus_block_hash)?;
193        let storage_price_provider =
194            sp_block_fees::InherentDataProvider::new(consensus_chain_byte_fee);
195
196        let domain_chains_allowlist_update =
197            runtime_api.domain_chains_allowlist_update(consensus_block_hash, self.domain_id)?;
198        let messenger_inherent_provider =
199            sp_messenger::InherentDataProvider::new(sp_messenger::InherentType {
200                maybe_updates: domain_chains_allowlist_update,
201            });
202
203        let maybe_domain_sudo_call =
204            runtime_api.domain_sudo_call(consensus_block_hash, self.domain_id)?;
205        let domain_sudo_call_inherent_provider =
206            sp_domain_sudo::InherentDataProvider::new(maybe_domain_sudo_call);
207
208        let maybe_evm_domain_contract_creation_allowed_by_call = runtime_api
209            .evm_domain_contract_creation_allowed_by_call(consensus_block_hash, self.domain_id)?;
210        let evm_domain_contract_creation_allowed_by_call_inherent_provider =
211            sp_evm_tracker::InherentDataProvider::new(
212                maybe_evm_domain_contract_creation_allowed_by_call,
213            );
214
215        Ok((
216            timestamp_provider,
217            storage_price_provider,
218            runtime_upgrade_provider,
219            messenger_inherent_provider,
220            domain_sudo_call_inherent_provider,
221            evm_domain_contract_creation_allowed_by_call_inherent_provider,
222        ))
223    }
224}