sp_messenger/
messages.rs

1#[cfg(not(feature = "std"))]
2extern crate alloc;
3
4use crate::endpoint::{Endpoint, EndpointRequestWithCollectedFee, EndpointResponse};
5#[cfg(not(feature = "std"))]
6use alloc::vec::Vec;
7use parity_scale_codec::{Decode, Encode};
8use scale_info::TypeInfo;
9pub use sp_domains::{ChainId, ChannelId};
10use sp_runtime::app_crypto::sp_core::U256;
11use sp_runtime::DispatchError;
12use sp_subspace_mmr::ConsensusChainMmrLeafProof;
13use sp_trie::StorageProof;
14
15/// Nonce used as an identifier and ordering of messages within a channel.
16/// Nonce is always increasing.
17pub type Nonce = U256;
18
19/// Unique Id of a message between two chains.
20pub type MessageId = (ChannelId, Nonce);
21
22/// Unique message key for Outbox and Inbox responses
23pub type MessageKey = (ChainId, ChannelId, Nonce);
24
25/// State of a channel.
26#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
27pub enum ChannelState {
28    /// Channel between chains is initiated but do not yet send or receive messages in this state.
29    #[default]
30    Initiated,
31    /// Channel is open and can send and receive messages.
32    Open,
33    /// Channel is closed and do not send or receive messages.
34    Closed,
35}
36
37/// State of channel and nonces when channel is closed.
38#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
39pub enum ChannelStateWithNonce {
40    /// Channel between chains is initiated but do not yet send or receive messages in this state.
41    #[default]
42    Initiated,
43    /// Channel is open and can send and receive messages.
44    Open,
45    /// Channel is closed along with nonces at current point.
46    Closed {
47        next_outbox_nonce: Nonce,
48        next_inbox_nonce: Nonce,
49    },
50}
51
52/// Channel describes a bridge to exchange messages between two chains.
53#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
54pub struct Channel<Balance, AccountId> {
55    /// Channel identifier.
56    pub channel_id: ChannelId,
57    /// State of the channel.
58    pub state: ChannelState,
59    /// Next inbox nonce.
60    pub next_inbox_nonce: Nonce,
61    /// Next outbox nonce.
62    pub next_outbox_nonce: Nonce,
63    /// Latest outbox message nonce for which response was received from dst_chain.
64    pub latest_response_received_message_nonce: Option<Nonce>,
65    /// Maximum outgoing non-delivered messages.
66    pub max_outgoing_messages: u32,
67    /// Owner of the channel
68    /// Owner maybe None if the channel was initiated on the other chain.
69    pub maybe_owner: Option<AccountId>,
70    /// The amount of funds put on hold by the owner account for this channel
71    pub channel_reserve_fee: Balance,
72}
73
74/// Channel V1 open parameters
75#[derive(Debug, Encode, Decode, Eq, PartialEq, TypeInfo, Copy, Clone)]
76pub struct ChannelOpenParamsV1 {
77    pub max_outgoing_messages: u32,
78}
79
80/// Defines protocol requests performed on chains.
81#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
82pub enum ProtocolMessageRequest<ChannelOpenParams> {
83    /// Request to open a channel with foreign chain.
84    ChannelOpen(ChannelOpenParams),
85    /// Request to close an open channel with foreign chain.
86    ChannelClose,
87}
88
89/// Defines protocol requests performed on chains.
90pub type ProtocolMessageResponse = Result<(), DispatchError>;
91
92/// Protocol message that encompasses  request or its response.
93#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
94pub enum RequestResponse<Request, Response> {
95    Request(Request),
96    Response(Response),
97}
98
99/// Payload v1 of the message
100#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
101pub enum PayloadV1<Balance> {
102    /// Protocol message.
103    Protocol(RequestResponse<ProtocolMessageRequest<ChannelOpenParamsV1>, ProtocolMessageResponse>),
104    /// Endpoint message.
105    Endpoint(RequestResponse<EndpointRequestWithCollectedFee<Balance>, EndpointResponse>),
106}
107
108/// Versioned message payload
109#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
110pub enum VersionedPayload<Balance> {
111    #[codec(index = 1)]
112    V1(PayloadV1<Balance>),
113}
114
115/// Message weight tag used to indicate the consumed weight when handling the message
116#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Default)]
117pub enum MessageWeightTag {
118    ProtocolChannelOpen,
119    ProtocolChannelClose,
120    EndpointRequest(Endpoint),
121    EndpointResponse(Endpoint),
122    #[default]
123    None,
124}
125
126impl MessageWeightTag {
127    // Construct the weight tag for outbox message based on the outbox payload
128    pub fn outbox<Balance>(outbox_payload: &VersionedPayload<Balance>) -> Self {
129        match outbox_payload {
130            VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
131                ProtocolMessageRequest::ChannelOpen(_),
132            ))) => MessageWeightTag::ProtocolChannelOpen,
133            VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
134                ProtocolMessageRequest::ChannelClose,
135            ))) => MessageWeightTag::ProtocolChannelClose,
136            VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(
137                EndpointRequestWithCollectedFee { req, .. },
138            ))) => MessageWeightTag::EndpointRequest(req.dst_endpoint.clone()),
139            _ => MessageWeightTag::None,
140        }
141    }
142
143    // Construct the weight tag for inbox response based on the weight tag of the request
144    // message and the response payload
145    pub fn inbox_response<Balance>(
146        req_type: MessageWeightTag,
147        resp_payload: &VersionedPayload<Balance>,
148    ) -> Self {
149        match (req_type, resp_payload) {
150            (
151                MessageWeightTag::ProtocolChannelOpen,
152                VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Response(Ok(_)))),
153            ) => MessageWeightTag::ProtocolChannelOpen,
154            (
155                MessageWeightTag::EndpointRequest(endpoint),
156                VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Response(_))),
157            ) => MessageWeightTag::EndpointResponse(endpoint),
158            _ => MessageWeightTag::None,
159        }
160    }
161}
162
163/// Message contains information to be sent to or received from another chain.
164#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
165pub struct Message<Balance> {
166    /// Chain which initiated this message.
167    pub src_chain_id: ChainId,
168    /// Chain this message is intended for.
169    pub dst_chain_id: ChainId,
170    /// ChannelId the message was sent through.
171    pub channel_id: ChannelId,
172    /// Message nonce within the channel.
173    pub nonce: Nonce,
174    /// Payload of the message
175    pub payload: VersionedPayload<Balance>,
176    /// Last delivered message response nonce on src_chain.
177    pub last_delivered_message_response_nonce: Option<Nonce>,
178}
179
180#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
181pub enum Proof<CBlockNumber, CBlockHash, MmrHash> {
182    Consensus {
183        /// Consensus chain MMR leaf proof.
184        consensus_chain_mmr_proof: ConsensusChainMmrLeafProof<CBlockNumber, CBlockHash, MmrHash>,
185        /// Storage proof that message is processed on src_chain.
186        message_proof: StorageProof,
187    },
188    Domain {
189        /// Consensus chain MMR leaf proof.
190        consensus_chain_mmr_proof: ConsensusChainMmrLeafProof<CBlockNumber, CBlockHash, MmrHash>,
191        /// Storage proof that src domain chain's block is out of the challenge period on Consensus chain.
192        domain_proof: StorageProof,
193        /// Storage proof that message is processed on src_chain.
194        message_proof: StorageProof,
195    },
196}
197
198impl<CBlockNumber, CBlockHash, MmrHash> Proof<CBlockNumber, CBlockHash, MmrHash> {
199    pub fn message_proof(&self) -> StorageProof {
200        match self {
201            Proof::Consensus { message_proof, .. } => message_proof.clone(),
202            Proof::Domain { message_proof, .. } => message_proof.clone(),
203        }
204    }
205
206    pub fn consensus_mmr_proof(
207        &self,
208    ) -> ConsensusChainMmrLeafProof<CBlockNumber, CBlockHash, MmrHash>
209    where
210        CBlockNumber: Clone,
211        CBlockHash: Clone,
212        MmrHash: Clone,
213    {
214        match self {
215            Proof::Consensus {
216                consensus_chain_mmr_proof,
217                ..
218            } => consensus_chain_mmr_proof.clone(),
219            Proof::Domain {
220                consensus_chain_mmr_proof,
221                ..
222            } => consensus_chain_mmr_proof.clone(),
223        }
224    }
225
226    pub fn domain_proof(&self) -> Option<StorageProof> {
227        match self {
228            Proof::Consensus { .. } => None,
229            Proof::Domain { domain_proof, .. } => Some(domain_proof.clone()),
230        }
231    }
232}
233
234/// Cross Domain message contains Message and its proof on src_chain.
235#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
236pub struct CrossDomainMessage<CBlockNumber, CBlockHash, MmrHash> {
237    /// Chain which initiated this message.
238    pub src_chain_id: ChainId,
239    /// Chain this message is intended for.
240    pub dst_chain_id: ChainId,
241    /// ChannelId the message was sent through.
242    pub channel_id: ChannelId,
243    /// Message nonce within the channel.
244    pub nonce: Nonce,
245    /// Proof of message processed on src_chain.
246    pub proof: Proof<CBlockNumber, CBlockHash, MmrHash>,
247    /// The message weight tag
248    pub weight_tag: MessageWeightTag,
249}
250
251/// Message with storage key to generate storage proof using the backend.
252#[derive(Debug, Encode, Decode, TypeInfo, Clone, Eq, PartialEq)]
253pub struct BlockMessageWithStorageKey {
254    /// Chain which initiated this message.
255    pub src_chain_id: ChainId,
256    /// Chain this message is intended for.
257    pub dst_chain_id: ChainId,
258    /// ChannelId the message was sent through.
259    pub channel_id: ChannelId,
260    /// Message nonce within the channel.
261    pub nonce: Nonce,
262    /// Storage key to generate proof for using proof backend.
263    pub storage_key: Vec<u8>,
264    /// The message weight tag
265    pub weight_tag: MessageWeightTag,
266}
267
268impl BlockMessageWithStorageKey {
269    pub fn id(&self) -> (ChannelId, Nonce) {
270        (self.channel_id, self.nonce)
271    }
272}
273
274/// Set of messages with storage keys to be relayed in a given block..
275#[derive(Default, Debug, Encode, Decode, TypeInfo, Clone, Eq, PartialEq)]
276pub struct BlockMessagesWithStorageKey {
277    pub outbox: Vec<BlockMessageWithStorageKey>,
278    pub inbox_responses: Vec<BlockMessageWithStorageKey>,
279}
280
281impl BlockMessagesWithStorageKey {
282    pub fn is_empty(&self) -> bool {
283        self.outbox.is_empty() && self.inbox_responses.is_empty()
284    }
285}
286
287/// Set of messages with storage keys to be relayed in a given block.
288#[derive(Default, Debug, Encode, Decode, TypeInfo, Clone, Eq, PartialEq)]
289pub struct MessagesWithStorageKey {
290    pub outbox: Vec<MessageNonceWithStorageKey>,
291    pub inbox_responses: Vec<MessageNonceWithStorageKey>,
292}
293
294/// Message with storage key to generate storage proof using the backend.
295#[derive(Debug, Encode, Decode, TypeInfo, Clone, Eq, PartialEq)]
296pub struct MessageNonceWithStorageKey {
297    /// Message nonce within the channel.
298    pub nonce: Nonce,
299    /// Storage key to generate proof for using proof backend.
300    pub storage_key: Vec<u8>,
301    /// The message weight tag
302    pub weight_tag: MessageWeightTag,
303}
304
305/// A query to fetch block messages for Outbox and Inbox Responses
306#[derive(Debug, Encode, Decode, TypeInfo, Clone, Eq, PartialEq)]
307pub struct BlockMessagesQuery {
308    pub chain_id: ChainId,
309    pub channel_id: ChannelId,
310    pub outbox_from: Nonce,
311    pub inbox_responses_from: Nonce,
312}
313
314impl<BlockNumber, BlockHash, MmrHash> CrossDomainMessage<BlockNumber, BlockHash, MmrHash> {
315    pub fn from_relayer_msg_with_proof(
316        src_chain_id: ChainId,
317        dst_chain_id: ChainId,
318        channel_id: ChannelId,
319        msg: MessageNonceWithStorageKey,
320        proof: Proof<BlockNumber, BlockHash, MmrHash>,
321    ) -> Self {
322        CrossDomainMessage {
323            src_chain_id,
324            dst_chain_id,
325            channel_id,
326            nonce: msg.nonce,
327            proof,
328            weight_tag: msg.weight_tag,
329        }
330    }
331}