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
30pub 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 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 pub fn set_storage(&mut self, storage: Storage) {
177 self.storage = storage;
178 }
179
180 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 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}