sp_messenger_host_functions/
host_functions.rs

1use crate::StorageKeyRequest;
2use domain_block_preprocessor::stateless_runtime::StatelessRuntime;
3use sc_executor::RuntimeVersionOf;
4use sp_api::ProvideRuntimeApi;
5use sp_blockchain::HeaderBackend;
6use sp_core::traits::CodeExecutor;
7use sp_core::H256;
8use sp_domains::{DomainId, DomainsApi};
9use sp_messenger::messages::ChainId;
10pub use sp_messenger::MessengerApi;
11use sp_runtime::traits::{Block as BlockT, Header, NumberFor};
12use std::marker::PhantomData;
13use std::sync::Arc;
14
15/// Trait to query messenger specific details.
16pub trait MessengerHostFunctions: Send + Sync {
17    /// Returns the storage key for the given request.
18    fn get_storage_key(&self, req: StorageKeyRequest) -> Option<Vec<u8>>;
19}
20
21sp_externalities::decl_extension! {
22    pub struct MessengerExtension(Arc<dyn MessengerHostFunctions>);
23}
24
25impl MessengerExtension {
26    /// Create a new instance of [`MessengerExtension`].
27    pub fn new(inner: Arc<dyn MessengerHostFunctions>) -> Self {
28        Self(inner)
29    }
30}
31
32/// Implementation of Messenger host function.
33pub struct MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor> {
34    consensus_client: Arc<Client>,
35    domain_executor: Arc<Executor>,
36    _phantom: PhantomData<(Block, DomainBlock)>,
37}
38
39impl<Block, Client, DomainBlock, Executor>
40    MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
41{
42    pub fn new(consensus_client: Arc<Client>, domain_executor: Arc<Executor>) -> Self {
43        MessengerHostFunctionsImpl {
44            consensus_client,
45            domain_executor,
46            _phantom: Default::default(),
47        }
48    }
49}
50
51impl<Block, Client, DomainBlock, Executor>
52    MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
53where
54    Block: BlockT,
55    DomainBlock: BlockT,
56    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
57    Client::Api: DomainsApi<Block, DomainBlock::Header>,
58    Executor: CodeExecutor + RuntimeVersionOf,
59{
60    fn get_domain_runtime(
61        &self,
62        consensus_block_hash: Block::Hash,
63        domain_id: DomainId,
64    ) -> Option<StatelessRuntime<Block, DomainBlock, Executor>> {
65        let runtime_api = self.consensus_client.runtime_api();
66        // Use the parent hash to get the actual used domain runtime code
67        // TODO: update once we can get the actual used domain runtime code by `consensus_block_hash`
68        let consensus_block_header = self
69            .consensus_client
70            .header(consensus_block_hash)
71            .ok()
72            .flatten()?;
73        let domain_runtime = runtime_api
74            .domain_runtime_code(*consensus_block_header.parent_hash(), domain_id)
75            .ok()
76            .flatten()?;
77
78        // we need the initial state here so that SelfChainId is initialised on domain
79        let domain_state = runtime_api
80            .domain_instance_data(*consensus_block_header.parent_hash(), domain_id)
81            .ok()
82            .flatten()
83            .map(|(data, _)| data.raw_genesis.into_storage())?;
84        let mut domain_stateless_runtime = StatelessRuntime::<Block, DomainBlock, _>::new(
85            self.domain_executor.clone(),
86            domain_runtime.into(),
87        );
88
89        domain_stateless_runtime.set_storage(domain_state);
90        Some(domain_stateless_runtime)
91    }
92}
93
94impl<Block, Client, DomainBlock, Executor> MessengerHostFunctions
95    for MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
96where
97    Block: BlockT,
98    Block::Hash: From<H256>,
99    DomainBlock: BlockT,
100    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
101    Client::Api:
102        MessengerApi<Block, NumberFor<Block>, Block::Hash> + DomainsApi<Block, DomainBlock::Header>,
103    Executor: CodeExecutor + RuntimeVersionOf,
104{
105    fn get_storage_key(&self, req: StorageKeyRequest) -> Option<Vec<u8>> {
106        let best_hash = self.consensus_client.info().best_hash;
107        let runtime_api = self.consensus_client.runtime_api();
108        match req {
109            StorageKeyRequest::ConfirmedDomainBlockStorageKey(domain_id) => runtime_api
110                .confirmed_domain_block_storage_key(best_hash, domain_id)
111                .map(Some),
112            StorageKeyRequest::OutboxStorageKey {
113                message_key,
114                chain_id: ChainId::Consensus,
115            } => runtime_api
116                .outbox_storage_key(best_hash, message_key)
117                .map(Some),
118            StorageKeyRequest::OutboxStorageKey {
119                message_key,
120                chain_id: ChainId::Domain(domain_id),
121            } => {
122                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
123                domain_stateless_runtime
124                    .outbox_storage_key(message_key)
125                    .map(Some)
126            }
127            StorageKeyRequest::InboxResponseStorageKey {
128                message_key,
129                chain_id: ChainId::Consensus,
130            } => runtime_api
131                .inbox_response_storage_key(best_hash, message_key)
132                .map(Some),
133            StorageKeyRequest::InboxResponseStorageKey {
134                message_key,
135                chain_id: ChainId::Domain(domain_id),
136            } => {
137                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
138                domain_stateless_runtime
139                    .inbox_response_storage_key(message_key)
140                    .map(Some)
141            }
142        }
143        .expect(
144            "Runtime Api should not fail in host function, there is no recovery from this; qed.",
145        )
146    }
147}