sp_messenger/
lib.rs

1// Copyright (C) 2021 Subspace Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// 	http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Primitives for Messenger.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19
20pub mod endpoint;
21pub mod messages;
22
23#[cfg(not(feature = "std"))]
24extern crate alloc;
25
26use crate::messages::{ChannelStateWithNonce, MessageKey, MessagesWithStorageKey, Nonce};
27#[cfg(not(feature = "std"))]
28use alloc::collections::BTreeMap;
29#[cfg(not(feature = "std"))]
30use alloc::collections::BTreeSet;
31#[cfg(not(feature = "std"))]
32use alloc::vec::Vec;
33#[cfg(feature = "std")]
34use frame_support::inherent::InherentData;
35use frame_support::inherent::{InherentIdentifier, IsFatalError};
36#[cfg(feature = "runtime-benchmarks")]
37use frame_support::storage::storage_prefix;
38#[cfg(feature = "runtime-benchmarks")]
39use frame_support::{Identity, StorageHasher};
40use messages::{
41    BlockMessagesQuery, BlockMessagesWithStorageKey, ChannelId, CrossDomainMessage, MessageId,
42};
43use parity_scale_codec::{Decode, Encode};
44use scale_info::TypeInfo;
45use sp_domains::{ChainId, DomainAllowlistUpdates, DomainId};
46use sp_subspace_mmr::ConsensusChainMmrLeafProof;
47#[cfg(feature = "std")]
48use std::collections::BTreeMap;
49#[cfg(feature = "std")]
50use std::collections::BTreeSet;
51
52/// Messenger inherent identifier.
53pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"messengr";
54
55/// Maximum number of XDMs per domain/channel with future nonces that are allowed to be validated.
56/// Any XDM comes with a nonce above Maximum future nonce will be rejected.
57// TODO: We need to benchmark how many XDMs can fit in to a
58//  - Single consensus block
59//  - Single domain block(includes all bundles filled with XDMs)
60//  Once we have that info, we can make a better judgement on how many XDMs
61//  we want to include per block while allowing other extrinsics to be included as well.
62//  Note: Currently XDM takes priority over other extrinsics unless they come with priority fee
63pub const MAX_FUTURE_ALLOWED_NONCES: u32 = 256;
64
65/// Trait to handle XDM rewards.
66pub trait OnXDMRewards<Balance> {
67    fn on_xdm_rewards(rewards: Balance);
68    fn on_chain_protocol_fees(chain_id: ChainId, fees: Balance);
69}
70
71/// Trait to note cross chain transfer
72pub trait NoteChainTransfer<Balance> {
73    fn note_transfer_in(amount: Balance, from_chain_id: ChainId) -> bool;
74    fn note_transfer_out(amount: Balance, to_chain_id: ChainId) -> bool;
75}
76
77impl<Balance> NoteChainTransfer<Balance> for () {
78    fn note_transfer_in(_amount: Balance, _from_chain_id: ChainId) -> bool {
79        true
80    }
81    fn note_transfer_out(_amount: Balance, _to_chain_id: ChainId) -> bool {
82        true
83    }
84}
85
86impl<Balance> OnXDMRewards<Balance> for () {
87    fn on_xdm_rewards(_: Balance) {}
88
89    fn on_chain_protocol_fees(_chain_id: ChainId, _fees: Balance) {}
90}
91
92/// Trait to check if the domain is registered.
93pub trait DomainRegistration {
94    fn is_domain_registered(domain_id: DomainId) -> bool;
95}
96
97impl DomainRegistration for () {
98    fn is_domain_registered(_domain_id: DomainId) -> bool {
99        false
100    }
101}
102
103/// Trait that return various storage keys for storages on Consensus chain and domains
104pub trait StorageKeys {
105    /// Returns the storage key for confirmed domain block on conensus chain
106    fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Option<Vec<u8>>;
107
108    /// Returns the outbox storage key for given chain.
109    fn outbox_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>>;
110
111    /// Returns the inbox responses storage key for given chain.
112    fn inbox_responses_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>>;
113}
114
115impl StorageKeys for () {
116    fn confirmed_domain_block_storage_key(_domain_id: DomainId) -> Option<Vec<u8>> {
117        None
118    }
119
120    fn outbox_storage_key(_chain_id: ChainId, _message_key: MessageKey) -> Option<Vec<u8>> {
121        None
122    }
123
124    fn inbox_responses_storage_key(
125        _chain_id: ChainId,
126        _message_key: MessageKey,
127    ) -> Option<Vec<u8>> {
128        None
129    }
130}
131
132#[cfg(feature = "runtime-benchmarks")]
133pub struct BenchmarkStorageKeys;
134
135#[cfg(feature = "runtime-benchmarks")]
136impl StorageKeys for BenchmarkStorageKeys {
137    fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Option<Vec<u8>> {
138        let storage_prefix = storage_prefix(
139            "Domains".as_bytes(),
140            "LatestConfirmedDomainExecutionReceipt".as_bytes(),
141        );
142        let key_hashed = domain_id.using_encoded(Identity::hash);
143
144        let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
145
146        final_key.extend_from_slice(&storage_prefix);
147        final_key.extend_from_slice(key_hashed.as_ref());
148
149        Some(final_key)
150    }
151
152    fn outbox_storage_key(_chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>> {
153        let storage_prefix = storage_prefix("Messenger".as_bytes(), "Outbox".as_bytes());
154        let key_hashed = message_key.using_encoded(Identity::hash);
155
156        let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
157
158        final_key.extend_from_slice(&storage_prefix);
159        final_key.extend_from_slice(key_hashed.as_ref());
160
161        Some(final_key)
162    }
163
164    fn inbox_responses_storage_key(_chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>> {
165        let storage_prefix = storage_prefix("Messenger".as_bytes(), "InboxResponses".as_bytes());
166        let key_hashed = message_key.using_encoded(Identity::hash);
167
168        let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len());
169
170        final_key.extend_from_slice(&storage_prefix);
171        final_key.extend_from_slice(key_hashed.as_ref());
172
173        Some(final_key)
174    }
175}
176
177/// The type of the messenger inherent data.
178#[derive(Debug, Encode, Decode)]
179pub struct InherentType {
180    pub maybe_updates: Option<DomainAllowlistUpdates>,
181}
182
183/// Inherent specific errors
184#[derive(Debug, Encode)]
185#[cfg_attr(feature = "std", derive(Decode))]
186pub enum InherentError {
187    MissingAllowlistUpdates,
188    IncorrectAllowlistUpdates,
189}
190
191impl IsFatalError for InherentError {
192    fn is_fatal_error(&self) -> bool {
193        true
194    }
195}
196
197/// Provides the set code inherent data.
198#[cfg(feature = "std")]
199pub struct InherentDataProvider {
200    data: InherentType,
201}
202
203#[cfg(feature = "std")]
204impl InherentDataProvider {
205    /// Create new inherent data provider from the given `data`.
206    pub fn new(data: InherentType) -> Self {
207        Self { data }
208    }
209
210    /// Returns the `data` of this inherent data provider.
211    pub fn data(&self) -> &InherentType {
212        &self.data
213    }
214}
215
216#[cfg(feature = "std")]
217#[async_trait::async_trait]
218impl sp_inherents::InherentDataProvider for InherentDataProvider {
219    async fn provide_inherent_data(
220        &self,
221        inherent_data: &mut InherentData,
222    ) -> Result<(), sp_inherents::Error> {
223        inherent_data.put_data(INHERENT_IDENTIFIER, &self.data)
224    }
225
226    async fn try_handle_error(
227        &self,
228        identifier: &InherentIdentifier,
229        error: &[u8],
230    ) -> Option<Result<(), sp_inherents::Error>> {
231        if *identifier != INHERENT_IDENTIFIER {
232            return None;
233        }
234
235        let error = InherentError::decode(&mut &*error).ok()?;
236
237        Some(Err(sp_inherents::Error::Application(Box::from(format!(
238            "{error:?}"
239        )))))
240    }
241}
242
243/// Represent a union of XDM types with their message ID
244#[derive(Debug, Encode, Decode, TypeInfo, Copy, Clone)]
245pub enum XdmId {
246    RelayMessage(MessageKey),
247    RelayResponseMessage(MessageKey),
248}
249
250impl XdmId {
251    pub fn get_chain_id_and_channel_id(&self) -> (ChainId, ChannelId) {
252        match self {
253            XdmId::RelayMessage(key) => (key.0, key.1),
254            XdmId::RelayResponseMessage(key) => (key.0, key.1),
255        }
256    }
257}
258
259#[derive(Debug, Encode, Decode, TypeInfo, Copy, Clone)]
260pub struct ChannelNonce {
261    /// Last processed relay message nonce.
262    /// Could be None if there is no relay message yet.
263    pub relay_msg_nonce: Option<Nonce>,
264    /// Last processed relay response message nonce.
265    /// Could be None if there is no first response yet
266    pub relay_response_msg_nonce: Option<Nonce>,
267}
268
269sp_api::decl_runtime_apis! {
270    /// Api useful for relayers to fetch messages and submit transactions.
271    #[api_version(3)]
272    pub trait RelayerApi<BlockNumber, CNumber, CHash>
273    where
274        BlockNumber: Encode + Decode,
275        CNumber: Encode + Decode,
276        CHash: Encode + Decode,
277    {
278        /// Returns all the outbox and inbox responses to deliver.
279        /// Storage key is used to generate the storage proof for the message.
280        fn block_messages() -> BlockMessagesWithStorageKey;
281
282        /// Constructs an outbox message to the dst_chain as an unsigned extrinsic.
283        fn outbox_message_unsigned(
284            msg: CrossDomainMessage<CNumber, CHash, sp_core::H256>,
285        ) -> Option<Block::Extrinsic>;
286
287        /// Constructs an inbox response message to the dst_chain as an unsigned extrinsic.
288        fn inbox_response_message_unsigned(
289            msg: CrossDomainMessage<CNumber, CHash, sp_core::H256>,
290        ) -> Option<Block::Extrinsic>;
291
292        /// Returns true if the outbox message is ready to be relayed to dst_chain.
293        fn should_relay_outbox_message(dst_chain_id: ChainId, msg_id: MessageId) -> bool;
294
295        /// Returns true if the inbox message response is ready to be relayed to dst_chain.
296        fn should_relay_inbox_message_response(dst_chain_id: ChainId, msg_id: MessageId) -> bool;
297
298        /// Returns the list of channels updated in the given block.
299        fn updated_channels() -> BTreeSet<(ChainId, ChannelId)>;
300
301        /// Returns storage key for channels for given chain and channel id.
302        fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8>;
303
304        /// Returns all the open channels to other chains.
305        fn open_channels() -> BTreeSet<(ChainId, ChannelId)>;
306
307        /// Returns outbox and inbox responses from given nonce to maximum allowed nonce per block
308        /// Storage key is used to generate the storage proof for the message.
309        fn block_messages_with_query(query: BlockMessagesQuery) -> MessagesWithStorageKey;
310
311        /// Returns all the channels to other chains and their local Channel state.
312        fn channels_and_state() -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)>;
313
314        /// Returns the first outbox message nonce that should be relayed to the dst_chain.
315        fn first_outbox_message_nonce_to_relay(dst_chain_id: ChainId, channel_id: ChannelId, from_nonce: Nonce) -> Option<Nonce>;
316
317        /// Returns the first inbox response message nonce that should be relayed to the dst_chain.
318        fn first_inbox_message_response_nonce_to_relay(dst_chain_id: ChainId,channel_id: ChannelId, from_nonce: Nonce) -> Option<Nonce>;
319    }
320
321    /// Api to provide XDM extraction from Runtime Calls.
322    #[api_version(3)]
323    pub trait MessengerApi<CNumber, CHash>
324    where
325        CNumber: Encode + Decode,
326        CHash: Encode + Decode,
327    {
328        /// Returns `Some(true)` if valid XDM or `Some(false)` if not
329        /// Returns None if this is not an XDM
330        fn is_xdm_mmr_proof_valid(
331            ext: &Block::Extrinsic
332        ) -> Option<bool>;
333
334        // Extract the MMR proof from the XDM
335        fn extract_xdm_mmr_proof(ext: &Block::Extrinsic) -> Option<ConsensusChainMmrLeafProof<CNumber, CHash, sp_core::H256>>;
336
337        // Extract the MMR proofs a the given batch of XDM
338        // `allow(clippy::ptr_arg` is needed because Clippy complains to replace `&Vec<T>` with `&[T]`
339        // but the latter fails to compile.
340        #[allow(clippy::ptr_arg)]
341        fn batch_extract_xdm_mmr_proof(ext: &Vec<Block::Extrinsic>) -> BTreeMap<u32, ConsensusChainMmrLeafProof<CNumber, CHash, sp_core::H256>>;
342
343        /// Returns the confirmed domain block storage for given domain.
344        fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Vec<u8>;
345
346        /// Returns storage key for outbox for a given message_id.
347        fn outbox_storage_key(message_key: MessageKey) -> Vec<u8>;
348
349        /// Returns storage key for inbox response for a given message_id.
350        fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8>;
351
352        /// Returns any domain's chains allowlist updates on consensus chain.
353        fn domain_chains_allowlist_update(domain_id: DomainId) -> Option<DomainAllowlistUpdates>;
354
355        /// Returns XDM message ID
356        fn xdm_id(ext: &Block::Extrinsic) -> Option<XdmId>;
357
358        /// Get Channel nonce for given chain and channel id.
359        fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce>;
360    }
361}