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
74        // if we are snap syncing, it is possible that block's parent may not exist.
75        // in such cases, we would rather just pick the best hash to fetch the domain runtime
76        // This is fine to fetch the domain code since we only use it to derive the storage key.
77        // This will not be an issue unless the Storage type was changed for Outbox and Inbox Response.
78        // which might not happen but good to be aware if such situations arise.
79        let block_hash = match self
80            .consensus_client
81            .header(*consensus_block_header.parent_hash())
82            .ok()
83            .flatten()
84        {
85            // missing block body here means, we just snap synced,
86            // use the consensus block hash instead of its parent hash
87            None => consensus_block_hash,
88            Some(header) => header.hash(),
89        };
90
91        let domain_runtime = runtime_api
92            .domain_runtime_code(block_hash, domain_id)
93            .ok()
94            .flatten()?;
95
96        // we need the initial state here so that SelfChainId is initialised on domain
97        let domain_state = runtime_api
98            .domain_instance_data(block_hash, domain_id)
99            .ok()
100            .flatten()
101            .map(|(data, _)| data.raw_genesis.into_storage())?;
102        let mut domain_stateless_runtime = StatelessRuntime::<Block, DomainBlock, _>::new(
103            self.domain_executor.clone(),
104            domain_runtime.into(),
105        );
106
107        domain_stateless_runtime.set_storage(domain_state);
108        Some(domain_stateless_runtime)
109    }
110}
111
112impl<Block, Client, DomainBlock, Executor> MessengerHostFunctions
113    for MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
114where
115    Block: BlockT,
116    Block::Hash: From<H256>,
117    DomainBlock: BlockT,
118    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
119    Client::Api:
120        MessengerApi<Block, NumberFor<Block>, Block::Hash> + DomainsApi<Block, DomainBlock::Header>,
121    Executor: CodeExecutor + RuntimeVersionOf,
122{
123    fn get_storage_key(&self, req: StorageKeyRequest) -> Option<Vec<u8>> {
124        let best_hash = self.consensus_client.info().best_hash;
125        let runtime_api = self.consensus_client.runtime_api();
126        match req {
127            StorageKeyRequest::ConfirmedDomainBlockStorageKey(domain_id) => runtime_api
128                .confirmed_domain_block_storage_key(best_hash, domain_id)
129                .map(Some),
130            StorageKeyRequest::OutboxStorageKey {
131                message_key,
132                chain_id: ChainId::Consensus,
133            } => runtime_api
134                .outbox_storage_key(best_hash, message_key)
135                .map(Some),
136            StorageKeyRequest::OutboxStorageKey {
137                message_key,
138                chain_id: ChainId::Domain(domain_id),
139            } => {
140                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
141                domain_stateless_runtime
142                    .outbox_storage_key(message_key)
143                    .map(Some)
144            }
145            StorageKeyRequest::InboxResponseStorageKey {
146                message_key,
147                chain_id: ChainId::Consensus,
148            } => runtime_api
149                .inbox_response_storage_key(best_hash, message_key)
150                .map(Some),
151            StorageKeyRequest::InboxResponseStorageKey {
152                message_key,
153                chain_id: ChainId::Domain(domain_id),
154            } => {
155                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
156                domain_stateless_runtime
157                    .inbox_response_storage_key(message_key)
158                    .map(Some)
159            }
160        }
161        .expect(
162            "Runtime Api should not fail in host function, there is no recovery from this; qed.",
163        )
164    }
165}