1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::StorageKeyRequest;
use domain_block_preprocessor::stateless_runtime::StatelessRuntime;
use sc_executor::RuntimeVersionOf;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_core::traits::CodeExecutor;
use sp_core::H256;
use sp_domains::{DomainId, DomainsApi};
use sp_messenger::messages::ChainId;
pub use sp_messenger::MessengerApi;
use sp_runtime::traits::{Block as BlockT, Header, NumberFor};
use std::marker::PhantomData;
use std::sync::Arc;

/// Trait to query messenger specific details.
pub trait MessengerHostFunctions: Send + Sync {
    /// Returns the storage key for the given request.
    fn get_storage_key(&self, req: StorageKeyRequest) -> Option<Vec<u8>>;
}

sp_externalities::decl_extension! {
    pub struct MessengerExtension(Arc<dyn MessengerHostFunctions>);
}

impl MessengerExtension {
    /// Create a new instance of [`MessengerExtension`].
    pub fn new(inner: Arc<dyn MessengerHostFunctions>) -> Self {
        Self(inner)
    }
}

/// Implementation of Messenger host function.
pub struct MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor> {
    consensus_client: Arc<Client>,
    domain_executor: Arc<Executor>,
    _phantom: PhantomData<(Block, DomainBlock)>,
}

impl<Block, Client, DomainBlock, Executor>
    MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
{
    pub fn new(consensus_client: Arc<Client>, domain_executor: Arc<Executor>) -> Self {
        MessengerHostFunctionsImpl {
            consensus_client,
            domain_executor,
            _phantom: Default::default(),
        }
    }
}

impl<Block, Client, DomainBlock, Executor>
    MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
where
    Block: BlockT,
    DomainBlock: BlockT,
    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
    Client::Api: DomainsApi<Block, DomainBlock::Header>,
    Executor: CodeExecutor + RuntimeVersionOf,
{
    fn get_domain_runtime(
        &self,
        consensus_block_hash: Block::Hash,
        domain_id: DomainId,
    ) -> Option<StatelessRuntime<Block, DomainBlock, Executor>> {
        let runtime_api = self.consensus_client.runtime_api();
        // Use the parent hash to get the actual used domain runtime code
        // TODO: update once we can get the actual used domain runtime code by `consensus_block_hash`
        let consensus_block_header = self
            .consensus_client
            .header(consensus_block_hash)
            .ok()
            .flatten()?;
        let domain_runtime = runtime_api
            .domain_runtime_code(*consensus_block_header.parent_hash(), domain_id)
            .ok()
            .flatten()?;

        // we need the initial state here so that SelfChainId is initialised on domain
        let domain_state = runtime_api
            .domain_instance_data(*consensus_block_header.parent_hash(), domain_id)
            .ok()
            .flatten()
            .map(|(data, _)| data.raw_genesis.into_storage())?;
        let mut domain_stateless_runtime = StatelessRuntime::<Block, DomainBlock, _>::new(
            self.domain_executor.clone(),
            domain_runtime.into(),
        );

        domain_stateless_runtime.set_storage(domain_state);
        Some(domain_stateless_runtime)
    }
}

impl<Block, Client, DomainBlock, Executor> MessengerHostFunctions
    for MessengerHostFunctionsImpl<Block, Client, DomainBlock, Executor>
where
    Block: BlockT,
    Block::Hash: From<H256>,
    DomainBlock: BlockT,
    Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
    Client::Api:
        MessengerApi<Block, NumberFor<Block>, Block::Hash> + DomainsApi<Block, DomainBlock::Header>,
    Executor: CodeExecutor + RuntimeVersionOf,
{
    fn get_storage_key(&self, req: StorageKeyRequest) -> Option<Vec<u8>> {
        let best_hash = self.consensus_client.info().best_hash;
        let runtime_api = self.consensus_client.runtime_api();
        match req {
            StorageKeyRequest::ConfirmedDomainBlockStorageKey(domain_id) => runtime_api
                .confirmed_domain_block_storage_key(best_hash, domain_id)
                .map(Some),
            StorageKeyRequest::OutboxStorageKey {
                message_key,
                chain_id: ChainId::Consensus,
            } => runtime_api
                .outbox_storage_key(best_hash, message_key)
                .map(Some),
            StorageKeyRequest::OutboxStorageKey {
                message_key,
                chain_id: ChainId::Domain(domain_id),
            } => {
                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
                domain_stateless_runtime
                    .outbox_storage_key(message_key)
                    .map(Some)
            }
            StorageKeyRequest::InboxResponseStorageKey {
                message_key,
                chain_id: ChainId::Consensus,
            } => runtime_api
                .inbox_response_storage_key(best_hash, message_key)
                .map(Some),
            StorageKeyRequest::InboxResponseStorageKey {
                message_key,
                chain_id: ChainId::Domain(domain_id),
            } => {
                let domain_stateless_runtime = self.get_domain_runtime(best_hash, domain_id)?;
                domain_stateless_runtime
                    .inbox_response_storage_key(message_key)
                    .map(Some)
            }
        }
        .expect(
            "Runtime Api should not fail in host function, there is no recovery from this; qed.",
        )
    }
}