domain_block_preprocessor/
stateless_runtime.rs

1use domain_runtime_primitives::opaque::AccountId;
2use domain_runtime_primitives::{
3    opaque, Balance, CheckExtrinsicsValidityError, DecodeExtrinsicError, EthereumAccountId,
4};
5use parity_scale_codec::{Codec, Encode};
6use sc_client_api::execution_extensions::ExtensionsFactory;
7use sc_executor::RuntimeVersionOf;
8use sp_api::{ApiError, Core};
9use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode};
10use sp_core::Hasher;
11use sp_domain_sudo::DomainSudoApi;
12use sp_domains::core_api::DomainCoreApi;
13use sp_domains::{ChainId, ChannelId, DomainAllowlistUpdates, PermissionedActionAllowedBy};
14use sp_evm_tracker::EvmTrackerApi;
15use sp_messenger::messages::MessageKey;
16use sp_messenger::{MessengerApi, RelayerApi};
17use sp_runtime::traits::{Block as BlockT, NumberFor};
18use sp_runtime::Storage;
19use sp_state_machine::BasicExternalities;
20use sp_subspace_mmr::ConsensusChainMmrLeafProof;
21use sp_version::RuntimeVersion;
22use sp_weights::Weight;
23use std::borrow::Cow;
24use std::collections::BTreeMap;
25use std::marker::PhantomData;
26use std::sync::Arc;
27use subspace_core_primitives::{BlockHash, BlockNumber, U256};
28use subspace_runtime_primitives::{ExtrinsicFor, Moment};
29
30/// Stateless runtime api based on the runtime code and partial state if provided.
31///
32/// NOTE:
33/// - This is only supposed to be used when no domain client available, i.e., when the
34///   caller does not own the entire domain state.
35/// - This perfectly fits the runtime APIs that are purely stateless, but it's also usable
36///   for the stateful APIs. If some states are used inside a runtime api, these states must
37///   be provided and set before dispatching otherwise [`StatelessRuntime`] may give invalid output.
38pub struct StatelessRuntime<CBlock, Block, Executor> {
39    executor: Arc<Executor>,
40    runtime_code: Cow<'static, [u8]>,
41    storage: Storage,
42    extension_factory: Box<dyn ExtensionsFactory<Block>>,
43    _marker: PhantomData<(CBlock, Block)>,
44}
45
46impl<CBlock, Block, Executor> Core<Block> for StatelessRuntime<CBlock, Block, Executor>
47where
48    CBlock: BlockT,
49    Block: BlockT,
50    Executor: CodeExecutor + RuntimeVersionOf,
51{
52    fn __runtime_api_internal_call_api_at(
53        &self,
54        _at: Block::Hash,
55        params: Vec<u8>,
56        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
57    ) -> Result<Vec<u8>, ApiError> {
58        self.dispatch_call(fn_name, params)
59    }
60}
61
62impl<CBlock, Block, Executor> DomainCoreApi<Block> for StatelessRuntime<CBlock, Block, Executor>
63where
64    CBlock: BlockT,
65    Block: BlockT,
66    Executor: CodeExecutor + RuntimeVersionOf,
67{
68    fn __runtime_api_internal_call_api_at(
69        &self,
70        _at: Block::Hash,
71        params: Vec<u8>,
72        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
73    ) -> Result<Vec<u8>, ApiError> {
74        self.dispatch_call(fn_name, params)
75    }
76}
77
78impl<CBlock, Block, Executor> MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>
79    for StatelessRuntime<CBlock, Block, Executor>
80where
81    CBlock: BlockT,
82    Block: BlockT,
83    NumberFor<Block>: Codec,
84    Executor: CodeExecutor + RuntimeVersionOf,
85{
86    fn __runtime_api_internal_call_api_at(
87        &self,
88        _at: Block::Hash,
89        params: Vec<u8>,
90        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
91    ) -> Result<Vec<u8>, ApiError> {
92        self.dispatch_call(fn_name, params)
93    }
94}
95
96impl<CBlock, Block, Executor> RelayerApi<Block, NumberFor<Block>, BlockNumber, BlockHash>
97    for StatelessRuntime<CBlock, Block, Executor>
98where
99    CBlock: BlockT,
100    Block: BlockT,
101    NumberFor<Block>: Codec,
102    Executor: CodeExecutor + RuntimeVersionOf,
103{
104    fn __runtime_api_internal_call_api_at(
105        &self,
106        _at: Block::Hash,
107        params: Vec<u8>,
108        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
109    ) -> Result<Vec<u8>, ApiError> {
110        self.dispatch_call(fn_name, params)
111    }
112}
113
114impl<CBlock, Block, Executor> DomainSudoApi<Block> for StatelessRuntime<CBlock, Block, Executor>
115where
116    CBlock: BlockT,
117    Block: BlockT,
118    NumberFor<Block>: Codec,
119    Executor: CodeExecutor + RuntimeVersionOf,
120{
121    fn __runtime_api_internal_call_api_at(
122        &self,
123        _at: Block::Hash,
124        params: Vec<u8>,
125        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
126    ) -> Result<Vec<u8>, ApiError> {
127        self.dispatch_call(fn_name, params)
128    }
129}
130
131impl<CBlock, Block, Executor> EvmTrackerApi<Block> for StatelessRuntime<CBlock, Block, Executor>
132where
133    CBlock: BlockT,
134    Block: BlockT,
135    NumberFor<Block>: Codec,
136    Executor: CodeExecutor + RuntimeVersionOf,
137{
138    fn __runtime_api_internal_call_api_at(
139        &self,
140        _at: Block::Hash,
141        params: Vec<u8>,
142        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
143    ) -> Result<Vec<u8>, ApiError> {
144        self.dispatch_call(fn_name, params)
145    }
146}
147
148impl<CBlock, Block, Executor> FetchRuntimeCode for StatelessRuntime<CBlock, Block, Executor> {
149    fn fetch_runtime_code(&self) -> Option<Cow<'static, [u8]>> {
150        Some(self.runtime_code.clone())
151    }
152}
153
154pub type ExtractSignerResult<Block> = Vec<(Option<AccountId>, ExtrinsicFor<Block>)>;
155
156impl<CBlock, Block, Executor> StatelessRuntime<CBlock, Block, Executor>
157where
158    CBlock: BlockT,
159    Block: BlockT,
160    Executor: CodeExecutor + RuntimeVersionOf,
161{
162    /// Create a new instance of [`StatelessRuntime`] with empty storage.
163    pub fn new(executor: Arc<Executor>, runtime_code: Cow<'static, [u8]>) -> Self {
164        Self {
165            executor,
166            runtime_code,
167            storage: Storage::default(),
168            extension_factory: Box::new(()),
169            _marker: Default::default(),
170        }
171    }
172
173    /// Set the storage.
174    ///
175    /// Inject the state necessary for calling stateful runtime APIs.
176    pub fn set_storage(&mut self, storage: Storage) {
177        self.storage = storage;
178    }
179
180    /// Set the extensions.
181    ///
182    /// Inject the necessary extensions for Domain.
183    pub fn set_extension_factory(&mut self, extension_factory: Box<dyn ExtensionsFactory<Block>>) {
184        self.extension_factory = extension_factory;
185    }
186
187    fn runtime_code(&self) -> RuntimeCode<'_> {
188        let code_hash = sp_core::Blake2Hasher::hash(&self.runtime_code);
189        RuntimeCode {
190            code_fetcher: self,
191            heap_pages: None,
192            hash: code_hash.encode(),
193        }
194    }
195
196    fn dispatch_call(
197        &self,
198        fn_name: &dyn Fn(RuntimeVersion) -> &'static str,
199        input: Vec<u8>,
200    ) -> Result<Vec<u8>, ApiError> {
201        let mut ext = BasicExternalities::new(self.storage.clone());
202        let ext_extensions = ext.extensions();
203        ext_extensions.merge(
204            self.extension_factory
205                .extensions_for(Default::default(), Default::default()),
206        );
207        let runtime_code = self.runtime_code();
208        let runtime_version = self
209            .executor
210            .runtime_version(&mut ext, &runtime_code)
211            .map_err(|err| {
212                ApiError::Application(Box::from(format!(
213                    "failed to read domain runtime version: {err}"
214                )))
215            })?;
216        let fn_name = fn_name(runtime_version);
217        self.executor
218            .call(
219                &mut ext,
220                &runtime_code,
221                fn_name,
222                &input,
223                CallContext::Offchain,
224            )
225            .0
226            .map_err(|err| {
227                ApiError::Application(format!("Failed to invoke call to {fn_name}: {err}").into())
228            })
229    }
230
231    pub fn outbox_storage_key(&self, message_key: MessageKey) -> Result<Vec<u8>, ApiError> {
232        let storage_key =
233            <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::outbox_storage_key(
234                self,
235                Default::default(),
236                message_key,
237            )?;
238        Ok(storage_key)
239    }
240
241    pub fn inbox_response_storage_key(&self, message_key: MessageKey) -> Result<Vec<u8>, ApiError> {
242        let storage_key = <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::inbox_response_storage_key(
243            self,
244            Default::default(),
245            message_key,
246        )?;
247        Ok(storage_key)
248    }
249
250    pub fn channel_storage_key(
251        &self,
252        chain_id: ChainId,
253        channel_id: ChannelId,
254    ) -> Result<Vec<u8>, ApiError> {
255        let storage_key = <Self as RelayerApi<Block, NumberFor<Block>, BlockNumber, BlockHash>>::channel_storage_key(
256            self,
257            Default::default(),
258            chain_id,
259            channel_id
260        )?;
261        Ok(storage_key)
262    }
263
264    pub fn extract_signer(
265        &self,
266        extrinsics: Vec<Block::Extrinsic>,
267    ) -> Result<ExtractSignerResult<Block>, ApiError> {
268        <Self as DomainCoreApi<Block>>::extract_signer(self, Default::default(), extrinsics)
269    }
270
271    pub fn construct_set_code_extrinsic(&self, runtime_code: Vec<u8>) -> Result<Vec<u8>, ApiError> {
272        <Self as DomainCoreApi<Block>>::construct_set_code_extrinsic(
273            self,
274            Default::default(),
275            runtime_code,
276        )
277    }
278
279    pub fn construct_timestamp_extrinsic(
280        &self,
281        moment: Moment,
282    ) -> Result<Block::Extrinsic, ApiError> {
283        <Self as DomainCoreApi<Block>>::construct_timestamp_extrinsic(
284            self,
285            Default::default(),
286            moment,
287        )
288    }
289
290    pub fn construct_consensus_chain_byte_fee_extrinsic(
291        &self,
292        consensus_chain_byte_fee: Balance,
293    ) -> Result<Block::Extrinsic, ApiError> {
294        <Self as DomainCoreApi<Block>>::construct_consensus_chain_byte_fee_extrinsic(
295            self,
296            Default::default(),
297            consensus_chain_byte_fee,
298        )
299    }
300
301    pub fn construct_domain_update_chain_allowlist_extrinsic(
302        &self,
303        updates: DomainAllowlistUpdates,
304    ) -> Result<Block::Extrinsic, ApiError> {
305        <Self as DomainCoreApi<Block>>::construct_domain_update_chain_allowlist_extrinsic(
306            self,
307            Default::default(),
308            updates,
309        )
310    }
311
312    pub fn is_inherent_extrinsic(&self, extrinsic: &Block::Extrinsic) -> Result<bool, ApiError> {
313        <Self as DomainCoreApi<Block>>::is_inherent_extrinsic(self, Default::default(), extrinsic)
314    }
315
316    pub fn find_first_inherent_extrinsic(
317        &self,
318        extrinsics: &Vec<Block::Extrinsic>,
319    ) -> Result<Option<u32>, ApiError> {
320        <Self as DomainCoreApi<Block>>::find_first_inherent_extrinsic(
321            self,
322            Default::default(),
323            extrinsics,
324        )
325    }
326
327    pub fn is_xdm_mmr_proof_valid(
328        &self,
329        extrinsic: &Block::Extrinsic,
330    ) -> Result<Option<bool>, ApiError> {
331        <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::is_xdm_mmr_proof_valid(
332            self,
333            Default::default(),
334            extrinsic,
335        )
336    }
337
338    pub fn extract_xdm_mmr_proof(
339        &self,
340        extrinsic: &Block::Extrinsic,
341    ) -> Result<Option<Vec<u8>>, ApiError> {
342        <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::extract_xdm_mmr_proof(
343            self,
344            Default::default(),
345            extrinsic,
346        )
347        .map(|maybe_proof| maybe_proof.map(|p| p.encode()))
348    }
349
350    #[allow(clippy::type_complexity)]
351    pub fn batch_extract_native_xdm_mmr_proof(
352        &self,
353        extrinsics: &Vec<Block::Extrinsic>,
354    ) -> Result<
355        BTreeMap<u32, ConsensusChainMmrLeafProof<NumberFor<CBlock>, CBlock::Hash, sp_core::H256>>,
356        ApiError,
357    > {
358        <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::batch_extract_xdm_mmr_proof(
359            self,
360            Default::default(),
361            extrinsics,
362        )
363    }
364
365    #[allow(clippy::type_complexity)]
366    pub fn extract_native_xdm_mmr_proof(
367        &self,
368        extrinsic: &Block::Extrinsic,
369    ) -> Result<
370        Option<ConsensusChainMmrLeafProof<NumberFor<CBlock>, CBlock::Hash, sp_core::H256>>,
371        ApiError,
372    > {
373        <Self as MessengerApi<Block, NumberFor<CBlock>, CBlock::Hash>>::extract_xdm_mmr_proof(
374            self,
375            Default::default(),
376            extrinsic,
377        )
378    }
379
380    pub fn decode_extrinsic(
381        &self,
382        opaque_extrinsic: sp_runtime::OpaqueExtrinsic,
383    ) -> Result<Result<Block::Extrinsic, DecodeExtrinsicError>, ApiError> {
384        <Self as DomainCoreApi<Block>>::decode_extrinsic(self, Default::default(), opaque_extrinsic)
385    }
386
387    pub fn decode_extrinsics_prefix(
388        &self,
389        opaque_extrinsics: Vec<sp_runtime::OpaqueExtrinsic>,
390    ) -> Result<Vec<Block::Extrinsic>, ApiError> {
391        <Self as DomainCoreApi<Block>>::decode_extrinsics_prefix(
392            self,
393            Default::default(),
394            opaque_extrinsics,
395        )
396    }
397
398    pub fn is_within_tx_range(
399        &self,
400        extrinsic: &Block::Extrinsic,
401        bundle_vrf_hash: &U256,
402        tx_range: &U256,
403    ) -> Result<bool, ApiError> {
404        <Self as DomainCoreApi<Block>>::is_within_tx_range(
405            self,
406            Default::default(),
407            extrinsic,
408            bundle_vrf_hash,
409            tx_range,
410        )
411    }
412
413    pub fn extract_signer_if_all_within_tx_range(
414        &self,
415        extrinsics: &Vec<Block::Extrinsic>,
416        bundle_vrf_hash: &U256,
417        tx_range: &U256,
418    ) -> Result<Result<Vec<Option<opaque::AccountId>>, u32>, ApiError> {
419        <Self as DomainCoreApi<Block>>::extract_signer_if_all_within_tx_range(
420            self,
421            Default::default(),
422            extrinsics,
423            bundle_vrf_hash,
424            tx_range,
425        )
426    }
427
428    /// This is a stateful runtime api call and requires setting storage keys.
429    pub fn check_extrinsics_and_do_pre_dispatch(
430        &self,
431        uxts: Vec<Block::Extrinsic>,
432        block_number: NumberFor<Block>,
433        block_hash: Block::Hash,
434    ) -> Result<Result<(), CheckExtrinsicsValidityError>, ApiError> {
435        <Self as DomainCoreApi<Block>>::check_extrinsics_and_do_pre_dispatch(
436            self,
437            Default::default(),
438            uxts,
439            block_number,
440            block_hash,
441        )
442    }
443
444    pub fn transfers_storage_key(&self) -> Result<Vec<u8>, ApiError> {
445        <Self as DomainCoreApi<Block>>::transfers_storage_key(self, Default::default())
446    }
447
448    pub fn block_fees_storage_key(&self) -> Result<Vec<u8>, ApiError> {
449        <Self as DomainCoreApi<Block>>::block_fees_storage_key(self, Default::default())
450    }
451
452    pub fn extrinsic_weight(&self, extrinsic: &Block::Extrinsic) -> Result<Weight, ApiError> {
453        <Self as DomainCoreApi<Block>>::extrinsic_weight(self, Default::default(), extrinsic)
454    }
455
456    pub fn extrinsics_weight(
457        &self,
458        extrinsics: &Vec<Block::Extrinsic>,
459    ) -> Result<Weight, ApiError> {
460        <Self as DomainCoreApi<Block>>::extrinsics_weight(self, Default::default(), extrinsics)
461    }
462
463    pub fn is_valid_sudo_call(&self, extrinsic: Vec<u8>) -> Result<bool, ApiError> {
464        <Self as DomainSudoApi<Block>>::is_valid_sudo_call(self, Default::default(), extrinsic)
465    }
466
467    pub fn construct_domain_sudo_extrinsic(
468        &self,
469        inner_call: Vec<u8>,
470    ) -> Result<Block::Extrinsic, ApiError> {
471        <Self as DomainSudoApi<Block>>::construct_domain_sudo_extrinsic(
472            self,
473            Default::default(),
474            inner_call,
475        )
476    }
477
478    pub fn construct_evm_contract_creation_allowed_by_extrinsic(
479        &self,
480        inner_call: PermissionedActionAllowedBy<EthereumAccountId>,
481    ) -> Result<Block::Extrinsic, ApiError> {
482        <Self as EvmTrackerApi<Block>>::construct_evm_contract_creation_allowed_by_extrinsic(
483            self,
484            Default::default(),
485            inner_call,
486        )
487    }
488}