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::{MessageKey, 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};
36use messages::{BlockMessagesWithStorageKey, ChannelId, CrossDomainMessage, MessageId};
37use parity_scale_codec::{Decode, Encode};
38use scale_info::TypeInfo;
39use sp_domains::{ChainId, DomainAllowlistUpdates, DomainId};
40use sp_subspace_mmr::ConsensusChainMmrLeafProof;
41#[cfg(feature = "std")]
42use std::collections::BTreeMap;
43#[cfg(feature = "std")]
44use std::collections::BTreeSet;
45
46/// Messenger inherent identifier.
47pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"messengr";
48
49/// Maximum number of XDMs per domain/channel with future nonces that are allowed to be validated.
50/// Any XDM comes with a nonce above Maximum future nonce will be rejected.
51// TODO: We need to benchmark how many XDMs can fit in to a
52//  - Single consensus block
53//  - Single domain block(includes all bundles filled with XDMs)
54//  Once we have that info, we can make a better judgement on how many XDMs
55//  we want to include per block while allowing other extrinsics to be included as well.
56//  Note: Currently XDM takes priority over other extrinsics unless they come with priority fee
57pub const MAX_FUTURE_ALLOWED_NONCES: u32 = 256;
58
59/// Trait to handle XDM rewards.
60pub trait OnXDMRewards<Balance> {
61    fn on_xdm_rewards(rewards: Balance);
62    fn on_chain_protocol_fees(chain_id: ChainId, fees: Balance);
63}
64
65/// Trait to note cross chain transfer
66pub trait NoteChainTransfer<Balance> {
67    fn note_transfer_in(amount: Balance, from_chain_id: ChainId) -> bool;
68    fn note_transfer_out(amount: Balance, to_chain_id: ChainId) -> bool;
69}
70
71impl<Balance> NoteChainTransfer<Balance> for () {
72    fn note_transfer_in(_amount: Balance, _from_chain_id: ChainId) -> bool {
73        true
74    }
75    fn note_transfer_out(_amount: Balance, _to_chain_id: ChainId) -> bool {
76        true
77    }
78}
79
80impl<Balance> OnXDMRewards<Balance> for () {
81    fn on_xdm_rewards(_: Balance) {}
82
83    fn on_chain_protocol_fees(_chain_id: ChainId, _fees: Balance) {}
84}
85
86/// Trait to check if the domain is registered.
87pub trait DomainRegistration {
88    fn is_domain_registered(domain_id: DomainId) -> bool;
89}
90
91impl DomainRegistration for () {
92    fn is_domain_registered(_domain_id: DomainId) -> bool {
93        false
94    }
95}
96
97/// Trait that return various storage keys for storages on Consensus chain and domains
98pub trait StorageKeys {
99    /// Returns the storage key for confirmed domain block on conensus chain
100    fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Option<Vec<u8>>;
101
102    /// Returns the outbox storage key for given chain.
103    fn outbox_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>>;
104
105    /// Returns the inbox responses storage key for given chain.
106    fn inbox_responses_storage_key(chain_id: ChainId, message_key: MessageKey) -> Option<Vec<u8>>;
107}
108
109impl StorageKeys for () {
110    fn confirmed_domain_block_storage_key(_domain_id: DomainId) -> Option<Vec<u8>> {
111        None
112    }
113
114    fn outbox_storage_key(_chain_id: ChainId, _message_key: MessageKey) -> Option<Vec<u8>> {
115        None
116    }
117
118    fn inbox_responses_storage_key(
119        _chain_id: ChainId,
120        _message_key: MessageKey,
121    ) -> Option<Vec<u8>> {
122        None
123    }
124}
125
126/// The type of the messenger inherent data.
127#[derive(Debug, Encode, Decode)]
128pub struct InherentType {
129    pub maybe_updates: Option<DomainAllowlistUpdates>,
130}
131
132/// Inherent specific errors
133#[derive(Debug, Encode)]
134#[cfg_attr(feature = "std", derive(Decode))]
135pub enum InherentError {
136    MissingAllowlistUpdates,
137    IncorrectAllowlistUpdates,
138}
139
140impl IsFatalError for InherentError {
141    fn is_fatal_error(&self) -> bool {
142        true
143    }
144}
145
146/// Provides the set code inherent data.
147#[cfg(feature = "std")]
148pub struct InherentDataProvider {
149    data: InherentType,
150}
151
152#[cfg(feature = "std")]
153impl InherentDataProvider {
154    /// Create new inherent data provider from the given `data`.
155    pub fn new(data: InherentType) -> Self {
156        Self { data }
157    }
158
159    /// Returns the `data` of this inherent data provider.
160    pub fn data(&self) -> &InherentType {
161        &self.data
162    }
163}
164
165#[cfg(feature = "std")]
166#[async_trait::async_trait]
167impl sp_inherents::InherentDataProvider for InherentDataProvider {
168    async fn provide_inherent_data(
169        &self,
170        inherent_data: &mut InherentData,
171    ) -> Result<(), sp_inherents::Error> {
172        inherent_data.put_data(INHERENT_IDENTIFIER, &self.data)
173    }
174
175    async fn try_handle_error(
176        &self,
177        identifier: &InherentIdentifier,
178        error: &[u8],
179    ) -> Option<Result<(), sp_inherents::Error>> {
180        if *identifier != INHERENT_IDENTIFIER {
181            return None;
182        }
183
184        let error = InherentError::decode(&mut &*error).ok()?;
185
186        Some(Err(sp_inherents::Error::Application(Box::from(format!(
187            "{error:?}"
188        )))))
189    }
190}
191
192/// Represent a union of XDM types with their message ID
193#[derive(Debug, Encode, Decode, TypeInfo, Copy, Clone)]
194pub enum XdmId {
195    RelayMessage(MessageKey),
196    RelayResponseMessage(MessageKey),
197}
198
199impl XdmId {
200    pub fn get_chain_id_and_channel_id(&self) -> (ChainId, ChannelId) {
201        match self {
202            XdmId::RelayMessage(key) => (key.0, key.1),
203            XdmId::RelayResponseMessage(key) => (key.0, key.1),
204        }
205    }
206}
207
208#[derive(Debug, Encode, Decode, TypeInfo, Copy, Clone)]
209pub struct ChannelNonce {
210    /// Last processed relay message nonce.
211    /// Could be None if there is no relay message yet.
212    pub relay_msg_nonce: Option<Nonce>,
213    /// Last processed relay response message nonce.
214    /// Could be None if there is no first response yet
215    pub relay_response_msg_nonce: Option<Nonce>,
216}
217
218sp_api::decl_runtime_apis! {
219    /// Api useful for relayers to fetch messages and submit transactions.
220    #[api_version(2)]
221    pub trait RelayerApi<BlockNumber, CNumber, CHash>
222    where
223        BlockNumber: Encode + Decode,
224        CNumber: Encode + Decode,
225        CHash: Encode + Decode,
226    {
227        /// Returns all the outbox and inbox responses to deliver.
228        /// Storage key is used to generate the storage proof for the message.
229        fn block_messages() -> BlockMessagesWithStorageKey;
230
231        /// Constructs an outbox message to the dst_chain as an unsigned extrinsic.
232        fn outbox_message_unsigned(
233            msg: CrossDomainMessage<CNumber, CHash, sp_core::H256>,
234        ) -> Option<Block::Extrinsic>;
235
236        /// Constructs an inbox response message to the dst_chain as an unsigned extrinsic.
237        fn inbox_response_message_unsigned(
238            msg: CrossDomainMessage<CNumber, CHash, sp_core::H256>,
239        ) -> Option<Block::Extrinsic>;
240
241        /// Returns true if the outbox message is ready to be relayed to dst_chain.
242        fn should_relay_outbox_message(dst_chain_id: ChainId, msg_id: MessageId) -> bool;
243
244        /// Returns true if the inbox message response is ready to be relayed to dst_chain.
245        fn should_relay_inbox_message_response(dst_chain_id: ChainId, msg_id: MessageId) -> bool;
246
247        /// Returns the list of channels updated in the given block.
248        fn updated_channels() -> BTreeSet<(ChainId, ChannelId)>;
249
250        /// Returns storage key for channels for given chain and channel id.
251        fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8>;
252
253        /// Returns all the open channels to other chains.
254        fn open_channels() -> BTreeSet<(ChainId, ChannelId)>;
255    }
256
257    /// Api to provide XDM extraction from Runtime Calls.
258    #[api_version(3)]
259    pub trait MessengerApi<CNumber, CHash>
260    where
261        CNumber: Encode + Decode,
262        CHash: Encode + Decode,
263    {
264        /// Returns `Some(true)` if valid XDM or `Some(false)` if not
265        /// Returns None if this is not an XDM
266        fn is_xdm_mmr_proof_valid(
267            ext: &Block::Extrinsic
268        ) -> Option<bool>;
269
270        // Extract the MMR proof from the XDM
271        fn extract_xdm_mmr_proof(ext: &Block::Extrinsic) -> Option<ConsensusChainMmrLeafProof<CNumber, CHash, sp_core::H256>>;
272
273        // Extract the MMR proofs a the given batch of XDM
274        // `allow(clippy::ptr_arg` is needed because Clippy complains to replace `&Vec<T>` with `&[T]`
275        // but the latter fails to compile.
276        #[allow(clippy::ptr_arg)]
277        fn batch_extract_xdm_mmr_proof(ext: &Vec<Block::Extrinsic>) -> BTreeMap<u32, ConsensusChainMmrLeafProof<CNumber, CHash, sp_core::H256>>;
278
279        /// Returns the confirmed domain block storage for given domain.
280        fn confirmed_domain_block_storage_key(domain_id: DomainId) -> Vec<u8>;
281
282        /// Returns storage key for outbox for a given message_id.
283        fn outbox_storage_key(message_key: MessageKey) -> Vec<u8>;
284
285        /// Returns storage key for inbox response for a given message_id.
286        fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8>;
287
288        /// Returns any domain's chains allowlist updates on consensus chain.
289        fn domain_chains_allowlist_update(domain_id: DomainId) -> Option<DomainAllowlistUpdates>;
290
291        /// Returns XDM message ID
292        fn xdm_id(ext: &Block::Extrinsic) -> Option<XdmId>;
293
294        /// Get Channel nonce for given chain and channel id.
295        fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce>;
296    }
297}