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