sc_domains/
lib.rs

1//! Domain specific Host functions and Extension factory
2
3pub mod domain_block_er;
4
5use sc_client_api::execution_extensions::ExtensionsFactory as ExtensionsFactoryT;
6use sc_executor::RuntimeVersionOf;
7use sp_api::ProvideRuntimeApi;
8use sp_blockchain::HeaderBackend;
9use sp_core::H256;
10use sp_core::traits::CodeExecutor;
11use sp_domains::DomainsApi;
12use sp_domains_fraud_proof::FraudProofApi;
13use sp_domains_fraud_proof::storage_proof::{
14    FraudProofStorageKeyProviderInstance, FraudProofStorageKeyRequest,
15};
16use sp_externalities::Extensions;
17use sp_messenger_host_functions::{MessengerApi, MessengerExtension, MessengerHostFunctionsImpl};
18use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, One};
19use sp_subspace_mmr::ConsensusChainMmrLeafProof;
20use sp_subspace_mmr::host_functions::{MmrApi, SubspaceMmrExtension, SubspaceMmrHostFunctionsImpl};
21use std::marker::PhantomData;
22use std::sync::Arc;
23
24/// Host functions required for Subspace domain
25#[cfg(not(feature = "runtime-benchmarks"))]
26pub type HostFunctions = (
27    sp_auto_id::auto_id_runtime_interface::HostFunctions,
28    sp_io::SubstrateHostFunctions,
29    sp_messenger_host_functions::HostFunctions,
30    sp_subspace_mmr::DomainHostFunctions,
31    cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
32);
33
34/// Host functions required for Subspace domain
35#[cfg(feature = "runtime-benchmarks")]
36pub type HostFunctions = (
37    sp_auto_id::auto_id_runtime_interface::HostFunctions,
38    sp_io::SubstrateHostFunctions,
39    sp_messenger_host_functions::HostFunctions,
40    sp_subspace_mmr::DomainHostFunctions,
41    frame_benchmarking::benchmarking::HostFunctions,
42    cumulus_primitives_proof_size_hostfunction::storage_proof_size::HostFunctions,
43);
44
45/// Runtime executor for Domains
46pub type RuntimeExecutor = sc_executor::WasmExecutor<HostFunctions>;
47
48/// Extensions factory for subspace domains.
49pub struct ExtensionsFactory<CClient, CBlock, Block, Executor> {
50    consensus_client: Arc<CClient>,
51    executor: Arc<Executor>,
52    confirmation_depth_k: u32,
53    _marker: PhantomData<(CBlock, Block)>,
54}
55
56impl<CClient, CBlock, Block, Executor> ExtensionsFactory<CClient, CBlock, Block, Executor> {
57    pub fn new(
58        consensus_client: Arc<CClient>,
59        executor: Arc<Executor>,
60        confirmation_depth_k: u32,
61    ) -> Self {
62        Self {
63            consensus_client,
64            executor,
65            confirmation_depth_k,
66            _marker: Default::default(),
67        }
68    }
69}
70
71impl<CClient, CBlock, Block, Executor> ExtensionsFactoryT<Block>
72    for ExtensionsFactory<CClient, CBlock, Block, Executor>
73where
74    Block: BlockT,
75    CBlock: BlockT,
76    CBlock::Hash: From<H256> + Into<H256>,
77    CClient: HeaderBackend<CBlock> + ProvideRuntimeApi<CBlock> + 'static,
78    CClient::Api: MmrApi<CBlock, H256, NumberFor<CBlock>>
79        + MessengerApi<CBlock, NumberFor<CBlock>, CBlock::Hash>
80        + DomainsApi<CBlock, Block::Header>,
81    Executor: CodeExecutor + RuntimeVersionOf,
82{
83    fn extensions_for(
84        &self,
85        _block_hash: Block::Hash,
86        _block_number: NumberFor<Block>,
87    ) -> Extensions {
88        let mut exts = Extensions::new();
89        exts.register(SubspaceMmrExtension::new(Arc::new(
90            SubspaceMmrHostFunctionsImpl::<CBlock, _>::new(
91                self.consensus_client.clone(),
92                self.confirmation_depth_k,
93            ),
94        )));
95
96        exts.register(MessengerExtension::new(Arc::new(
97            MessengerHostFunctionsImpl::<CBlock, _, Block, _>::new(
98                self.consensus_client.clone(),
99                self.executor.clone(),
100            ),
101        )));
102
103        exts.register(sp_auto_id::host_functions::HostFunctionExtension::new(
104            Arc::new(sp_auto_id::host_functions::HostFunctionsImpl),
105        ));
106
107        exts
108    }
109}
110
111pub struct FPStorageKeyProvider<CBlock, DomainHeader, CClient> {
112    consensus_client: Arc<CClient>,
113    _phantom: PhantomData<(CBlock, DomainHeader)>,
114}
115
116impl<CBlock, DomainHeader, CClient> Clone for FPStorageKeyProvider<CBlock, DomainHeader, CClient> {
117    fn clone(&self) -> Self {
118        Self {
119            consensus_client: self.consensus_client.clone(),
120            _phantom: self._phantom,
121        }
122    }
123}
124
125impl<CBlock, DomainHeader, CClient> FPStorageKeyProvider<CBlock, DomainHeader, CClient> {
126    pub fn new(consensus_client: Arc<CClient>) -> Self {
127        Self {
128            consensus_client,
129            _phantom: Default::default(),
130        }
131    }
132}
133
134impl<CBlock, DomainHeader, CClient> FraudProofStorageKeyProviderInstance<NumberFor<CBlock>>
135    for FPStorageKeyProvider<CBlock, DomainHeader, CClient>
136where
137    CBlock: BlockT,
138    DomainHeader: HeaderT,
139    CClient: HeaderBackend<CBlock> + ProvideRuntimeApi<CBlock> + 'static,
140    CClient::Api: FraudProofApi<CBlock, DomainHeader>,
141{
142    fn storage_key(&self, req: FraudProofStorageKeyRequest<NumberFor<CBlock>>) -> Option<Vec<u8>> {
143        let best_hash = self.consensus_client.info().best_hash;
144        self.consensus_client
145            .runtime_api()
146            .fraud_proof_storage_key(best_hash, req)
147            .ok()
148    }
149}
150
151/// Generate MMR proof for the block `to_prove` in the current best fork.
152///
153/// The returned proof can be later used to verify stateless (without query offchain MMR leaf) and
154/// extract the state root at `to_prove`.
155pub fn generate_mmr_proof<CClient, CBlock>(
156    consensus_client: &Arc<CClient>,
157    to_prove: NumberFor<CBlock>,
158) -> sp_blockchain::Result<ConsensusChainMmrLeafProof<NumberFor<CBlock>, CBlock::Hash, H256>>
159where
160    CBlock: BlockT,
161    CClient: HeaderBackend<CBlock> + ProvideRuntimeApi<CBlock> + 'static,
162    CClient::Api: MmrApi<CBlock, H256, NumberFor<CBlock>>,
163{
164    let api = consensus_client.runtime_api();
165    let prove_at_hash = consensus_client.info().best_hash;
166    let prove_at_number = consensus_client.info().best_number;
167
168    if to_prove >= prove_at_number {
169        return Err(sp_blockchain::Error::Application(Box::from(format!(
170            "Can't generate MMR proof for block {to_prove:?} >= best block {prove_at_number:?}"
171        ))));
172    }
173
174    let (mut leaves, proof) = api
175        // NOTE: the mmr leaf data is added in the next block so to generate the MMR proof of
176        // block `to_prove` we need to use `to_prove + 1` here.
177        .generate_proof(
178            prove_at_hash,
179            vec![to_prove + One::one()],
180            Some(prove_at_number),
181        )?
182        .map_err(|err| {
183            sp_blockchain::Error::Application(Box::from(format!(
184                "Failed to generate MMR proof: {err}"
185            )))
186        })?;
187    debug_assert!(leaves.len() == 1, "should always be of length 1");
188    let leaf = leaves
189        .pop()
190        .ok_or(sp_blockchain::Error::Application(Box::from(
191            "Unexpected missing mmr leaf".to_string(),
192        )))?;
193
194    Ok(ConsensusChainMmrLeafProof {
195        consensus_block_number: prove_at_number,
196        consensus_block_hash: prove_at_hash,
197        opaque_mmr_leaf: leaf,
198        proof,
199    })
200}