pallet_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//! Pallet messenger used to communicate between domains and other blockchains.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![forbid(unsafe_code)]
20#![warn(rust_2018_idioms)]
21#![feature(variant_count, if_let_guard)]
22
23#[cfg(feature = "runtime-benchmarks")]
24mod benchmarking;
25pub mod extensions;
26mod fees;
27mod messages;
28#[cfg(test)]
29mod mock;
30#[cfg(test)]
31mod tests;
32pub mod weights;
33
34#[cfg(not(feature = "std"))]
35extern crate alloc;
36
37use frame_support::__private::RuntimeDebug;
38use frame_support::pallet_prelude::{EnsureOrigin, MaxEncodedLen, StorageVersion};
39use frame_support::traits::fungible::{Inspect, InspectHold};
40use frame_system::pallet_prelude::BlockNumberFor;
41pub use pallet::*;
42use parity_scale_codec::{Decode, Encode};
43use scale_info::TypeInfo;
44use sp_core::U256;
45use sp_domains::{DomainAllowlistUpdates, DomainId};
46use sp_messenger::MAX_FUTURE_ALLOWED_NONCES;
47use sp_messenger::messages::{
48    ChainId, Channel, ChannelId, ChannelState, CrossDomainMessage, Message, Nonce,
49};
50use sp_runtime::DispatchError;
51use sp_runtime::traits::Hash;
52use subspace_runtime_primitives::CreateUnsigned;
53pub use weights::WeightInfo;
54
55/// Transaction validity for a given validated XDM extrinsic.
56/// If the extrinsic is not included in the bundle, extrinsic is removed from the TxPool.
57const XDM_TRANSACTION_LONGEVITY: u64 = 10;
58
59/// XDM verification errors.
60pub(crate) mod verification_errors {
61    // When updating these error codes, check for clashes between:
62    // <https://github.com/autonomys/subspace/blob/main/domains/primitives/runtime/src/lib.rs#L85-L88>
63    // <https://github.com/autonomys/subspace/blob/main/crates/sp-domains-fraud-proof/src/lib.rs#L49-L64>
64    pub(crate) const INVALID_NONCE: u8 = 201;
65    // Custom error code when a messenger nonce overflows.
66    pub(crate) const XDM_NONCE_OVERFLOW: u8 = 202;
67    // This error code was previously 200, but that clashed with ERR_BALANCE_OVERFLOW.
68    pub(crate) const INVALID_CHANNEL: u8 = 203;
69    pub(crate) const IN_FUTURE_NONCE: u8 = 204;
70    // Failed to update next nonce during the pre_dispatch
71    pub(crate) const NEXT_NONCE_UPDATE: u8 = 205;
72}
73
74#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
75pub enum OutboxMessageResult {
76    /// Message response handler returned Ok.
77    Ok,
78    /// Message response handler failed with Err.
79    Err(DispatchError),
80}
81
82/// Custom origin for validated unsigned extrinsics.
83#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
84pub enum RawOrigin {
85    ValidatedUnsigned,
86}
87
88/// Ensure the messenger origin.
89pub struct EnsureMessengerOrigin;
90impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O> for EnsureMessengerOrigin {
91    type Success = ();
92
93    fn try_origin(o: O) -> Result<Self::Success, O> {
94        o.into().map(|o| match o {
95            RawOrigin::ValidatedUnsigned => (),
96        })
97    }
98
99    #[cfg(feature = "runtime-benchmarks")]
100    fn try_successful_origin() -> Result<O, ()> {
101        Ok(O::from(RawOrigin::ValidatedUnsigned))
102    }
103}
104
105pub(crate) type StateRootOf<T> = <<T as frame_system::Config>::Hashing as Hash>::Output;
106pub(crate) type BalanceOf<T> =
107    <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
108pub(crate) type FungibleHoldId<T> =
109    <<T as Config>::Currency as InspectHold<<T as frame_system::Config>::AccountId>>::Reason;
110
111/// Parameter to update chain allow list.
112#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
113pub enum ChainAllowlistUpdate {
114    Add(ChainId),
115    Remove(ChainId),
116}
117
118impl ChainAllowlistUpdate {
119    fn chain_id(&self) -> ChainId {
120        match self {
121            ChainAllowlistUpdate::Add(chain_id) => *chain_id,
122            ChainAllowlistUpdate::Remove(chain_id) => *chain_id,
123        }
124    }
125}
126
127#[derive(Debug, Encode, Decode, TypeInfo)]
128pub struct ValidatedRelayMessage<T: Config> {
129    pub message: Message<BalanceOf<T>>,
130    pub should_init_channel: bool,
131    pub next_nonce: Nonce,
132}
133
134/// Channel can be closed either by Channel owner or Sudo
135#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
136pub(crate) enum CloseChannelBy<AccountId> {
137    Owner(AccountId),
138    Sudo,
139}
140
141/// Hold identifier trait for messenger specific balance holds
142pub trait HoldIdentifier<T: Config> {
143    fn messenger_channel() -> FungibleHoldId<T>;
144}
145
146/// The current storage version.
147const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
148
149#[frame_support::pallet]
150mod pallet {
151    pub use crate::extensions::weights::WeightInfo as ExtensionWeightInfo;
152    use crate::weights::WeightInfo;
153    use crate::{
154        BalanceOf, ChainAllowlistUpdate, Channel, ChannelId, ChannelState, CloseChannelBy,
155        HoldIdentifier, Nonce, OutboxMessageResult, RawOrigin, STORAGE_VERSION, StateRootOf, U256,
156        ValidatedRelayMessage,
157    };
158    #[cfg(not(feature = "std"))]
159    use alloc::boxed::Box;
160    #[cfg(not(feature = "std"))]
161    use alloc::collections::BTreeSet;
162    #[cfg(not(feature = "std"))]
163    use alloc::vec::Vec;
164    use core::cmp::Ordering;
165    use frame_support::ensure;
166    use frame_support::pallet_prelude::*;
167    use frame_support::storage::with_storage_layer;
168    use frame_support::traits::fungible::{Balanced, Inspect, InspectHold, Mutate, MutateHold};
169    use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
170    use frame_support::weights::WeightToFee;
171    use frame_system::pallet_prelude::*;
172    use sp_core::storage::StorageKey;
173    use sp_domains::proof_provider_and_verifier::{StorageProofVerifier, VerificationError};
174    use sp_domains::{DomainAllowlistUpdates, DomainId, DomainOwner};
175    use sp_messenger::endpoint::{
176        Endpoint, EndpointHandler, EndpointRequest, EndpointRequestWithCollectedFee, Sender,
177    };
178    use sp_messenger::messages::{
179        ChainId, ChannelOpenParamsV1, ChannelStateWithNonce, CrossDomainMessage, Message,
180        MessageId, MessageKey, MessageWeightTag, PayloadV1, ProtocolMessageRequest,
181        RequestResponse, VersionedPayload,
182    };
183    use sp_messenger::{
184        ChannelNonce, DomainRegistration, INHERENT_IDENTIFIER, InherentError, InherentType,
185        NoteChainTransfer, OnXDMRewards, StorageKeys,
186    };
187    use sp_runtime::traits::Zero;
188    use sp_runtime::{ArithmeticError, Perbill, Saturating};
189    use sp_subspace_mmr::MmrProofVerifier;
190    #[cfg(feature = "std")]
191    use std::collections::BTreeSet;
192
193    #[pallet::config]
194    pub trait Config: frame_system::Config {
195        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
196        /// Gets the chain_id that is treated as src_chain_id for outgoing messages.
197        type SelfChainId: Get<ChainId>;
198        /// function to fetch endpoint response handler by Endpoint.
199        fn get_endpoint_handler(endpoint: &Endpoint)
200        -> Option<Box<dyn EndpointHandler<MessageId>>>;
201        /// Currency type pallet uses for fees and deposits.
202        type Currency: Mutate<Self::AccountId>
203            + InspectHold<Self::AccountId>
204            + MutateHold<Self::AccountId>
205            + Balanced<Self::AccountId>;
206        /// Weight information for extrinsics in this pallet.
207        type WeightInfo: WeightInfo;
208        /// Weight to fee conversion.
209        type WeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
210        /// Adjusted Weight to fee conversion.
211        /// This includes the TransactionPayment Multiper at the time of fee deduction.
212        type AdjustedWeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
213        /// Fee Multiper for XDM
214        /// Final fee calculated will fee_multiplier * adjusted_weight_to_fee.
215        #[pallet::constant]
216        type FeeMultiplier: Get<u32>;
217        /// Handle XDM rewards.
218        type OnXDMRewards: OnXDMRewards<BalanceOf<Self>>;
219        /// Hash type of MMR
220        type MmrHash: Parameter + Member + Default + Clone;
221        /// MMR proof verifier
222        type MmrProofVerifier: MmrProofVerifier<Self::MmrHash, BlockNumberFor<Self>, StateRootOf<Self>>;
223        /// Storage key provider.
224        type StorageKeys: StorageKeys;
225        /// Domain owner provider.
226        type DomainOwner: DomainOwner<Self::AccountId>;
227        /// A variation of the Identifier used for holding the funds used for Messenger
228        type HoldIdentifier: HoldIdentifier<Self>;
229        /// Channel reserve fee to open a channel.
230        #[pallet::constant]
231        type ChannelReserveFee: Get<BalanceOf<Self>>;
232        /// Portion of Channel reserve taken by the protocol
233        /// if the channel is in init state and is requested to be closed.
234        #[pallet::constant]
235        type ChannelInitReservePortion: Get<Perbill>;
236        /// Type to check if a given domain is registered on Consensus chain.
237        type DomainRegistration: DomainRegistration;
238        /// Maximum outgoing messages from a given channel
239        #[pallet::constant]
240        type MaxOutgoingMessages: Get<u32>;
241        /// Origin for messenger call.
242        type MessengerOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ()>;
243        /// Helper to note cross chain XDM fee transfer
244        type NoteChainTransfer: NoteChainTransfer<BalanceOf<Self>>;
245        /// Weight info for extensions
246        type ExtensionWeightInfo: ExtensionWeightInfo;
247    }
248
249    /// Pallet messenger used to communicate between chains and other blockchains.
250    #[pallet::pallet]
251    #[pallet::without_storage_info]
252    #[pallet::storage_version(STORAGE_VERSION)]
253    pub struct Pallet<T>(_);
254
255    /// Stores the next channel id for a foreign chain.
256    #[pallet::storage]
257    #[pallet::getter(fn next_channel_id)]
258    pub(super) type NextChannelId<T: Config> =
259        StorageMap<_, Identity, ChainId, ChannelId, ValueQuery>;
260
261    /// Stores channel config between two chains.
262    /// Key points to the foreign chain wrt own chain's storage name space
263    #[pallet::storage]
264    #[pallet::getter(fn channels)]
265    pub(super) type Channels<T: Config> = StorageDoubleMap<
266        _,
267        Identity,
268        ChainId,
269        Identity,
270        ChannelId,
271        Channel<BalanceOf<T>, T::AccountId>,
272        OptionQuery,
273    >;
274
275    /// A temporary storage for storing decoded inbox message between `pre_dispatch_relay_message`
276    /// and `relay_message`.
277    #[pallet::storage]
278    #[pallet::getter(fn inbox)]
279    pub(super) type Inbox<T: Config> = StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
280
281    /// A temporary storage of fees for executing an inbox message.
282    /// The storage is cleared when the acknowledgement of inbox response is received
283    /// from the src_chain.
284    #[pallet::storage]
285    #[pallet::getter(fn inbox_fees)]
286    pub(super) type InboxFee<T: Config> =
287        StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
288
289    /// A temporary storage of fees for executing an outbox message and its response from dst_chain.
290    /// The storage is cleared when src_chain receives the response from dst_chain.
291    #[pallet::storage]
292    #[pallet::getter(fn outbox_fees)]
293    pub(super) type OutboxFee<T: Config> =
294        StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
295
296    /// Stores the message responses of the incoming processed responses.
297    /// Used by the dst_chains to verify the message response.
298    #[pallet::storage]
299    #[pallet::getter(fn inbox_responses)]
300    pub(super) type InboxResponses<T: Config> =
301        StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
302
303    /// Stores the outgoing messages that are awaiting message responses from the dst_chain.
304    /// Messages are processed in the outbox nonce order of chain's channel.
305    #[pallet::storage]
306    #[pallet::getter(fn outbox)]
307    pub(super) type Outbox<T: Config> =
308        StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
309
310    /// Stores the outgoing messages count that are awaiting message responses from the dst_chain.
311    #[pallet::storage]
312    #[pallet::getter(fn outbox_message_count)]
313    pub(super) type OutboxMessageCount<T: Config> =
314        StorageMap<_, Identity, (ChainId, ChannelId), u32, ValueQuery>;
315
316    /// A temporary storage for storing decoded outbox response message between `pre_dispatch_relay_message_response`
317    /// and `relay_message_response`.
318    #[pallet::storage]
319    #[pallet::getter(fn outbox_responses)]
320    pub(super) type OutboxResponses<T: Config> =
321        StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
322
323    /// Storage to store the weight tags for all the outbox messages.
324    #[pallet::storage]
325    #[pallet::getter(fn outbox_message_weight_tags)]
326    pub(super) type OutboxMessageWeightTags<T: Config> =
327        StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
328
329    /// Storage to store the weight tags for all the inbox responses messages.
330    #[pallet::storage]
331    #[pallet::getter(fn inbox_response_message_weight_tags)]
332    pub(super) type InboxResponseMessageWeightTags<T: Config> =
333        StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
334
335    /// An allowlist of chains that can open channel with this chain.
336    #[pallet::storage]
337    #[pallet::getter(fn chain_allowlist)]
338    pub(super) type ChainAllowlist<T: Config> = StorageValue<_, BTreeSet<ChainId>, ValueQuery>;
339
340    /// A storage to store any allowlist updates to domain. The updates will be cleared in the next block
341    /// once the previous block has a domain bundle, but a empty value should be left because in the invalid
342    /// extrinsic root fraud proof the prover need to generate a proof-of-empty-value for the domain.
343    #[pallet::storage]
344    #[pallet::getter(fn domain_chain_allowlist_updates)]
345    pub(super) type DomainChainAllowlistUpdate<T: Config> =
346        StorageMap<_, Identity, DomainId, DomainAllowlistUpdates, OptionQuery>;
347
348    /// Temporary storage to store the updated channels between this chain and other chain.
349    /// Storage is cleared on block initialization.
350    #[pallet::storage]
351    pub(super) type UpdatedChannels<T: Config> =
352        StorageValue<_, BTreeSet<(ChainId, ChannelId)>, ValueQuery>;
353
354    /// Storage to track the inbox fees that is hold on the chain before distributing.
355    ///
356    /// NOTE: The inbox fees is accounted to the chain's total issuance but not hold on any account
357    /// because an account with balance below ED will be reaped, in this way, we can manage small
358    /// inbox fee that less than ED easier. It also means whenever `InboxFeesOnHold` is increase/decrease
359    /// we need to increase/decrease the total issuance manually.
360    #[pallet::storage]
361    pub(super) type InboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
362
363    /// Storage to track the outbox fees that is hold on the chain before distributing.
364    ///
365    /// NOTE: The outbox fees is accounted to the chain's total issuance but not hold on any account
366    /// because an account with balance below ED will be reaped, in this way, we can manage small
367    /// outbox fee that less than ED easier. It also means whenever `OutboxFeesOnHold` is increase/decrease
368    /// we need to increase/decrease the total issuance manually.
369    #[pallet::storage]
370    pub(super) type OutboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
371
372    #[pallet::origin]
373    pub type Origin = RawOrigin;
374
375    /// `pallet-messenger` events
376    #[pallet::event]
377    #[pallet::generate_deposit(pub (super) fn deposit_event)]
378    pub enum Event<T: Config> {
379        /// Emits when a channel between two chains is initiated.
380        ChannelInitiated {
381            /// Foreign chain id this channel connects to.
382            chain_id: ChainId,
383            /// Channel ID of the said channel.
384            channel_id: ChannelId,
385        },
386
387        /// Emits when a channel between two chains is closed.
388        ChannelClosed {
389            /// Foreign chain id this channel connects to.
390            chain_id: ChainId,
391            /// Channel ID of the said channel.
392            channel_id: ChannelId,
393        },
394
395        /// Emits when a channel between two chain is open.
396        ChannelOpen {
397            /// Foreign chain id this channel connects to.
398            chain_id: ChainId,
399            /// Channel ID of the said channel.
400            channel_id: ChannelId,
401        },
402
403        /// Emits when a new message is added to the outbox.
404        OutboxMessage {
405            chain_id: ChainId,
406            channel_id: ChannelId,
407            nonce: Nonce,
408        },
409
410        /// Emits when a message response is available for Outbox message.
411        OutboxMessageResponse {
412            /// Destination chain ID.
413            chain_id: ChainId,
414            /// Channel Is
415            channel_id: ChannelId,
416            nonce: Nonce,
417        },
418
419        /// Emits outbox message result.
420        OutboxMessageResult {
421            chain_id: ChainId,
422            channel_id: ChannelId,
423            nonce: Nonce,
424            result: OutboxMessageResult,
425        },
426
427        /// Emits when a new inbox message is validated and added to Inbox.
428        InboxMessage {
429            chain_id: ChainId,
430            channel_id: ChannelId,
431            nonce: Nonce,
432        },
433
434        /// Emits when a message response is available for Inbox message.
435        InboxMessageResponse {
436            /// Destination chain ID.
437            chain_id: ChainId,
438            /// Channel Is
439            channel_id: ChannelId,
440            nonce: Nonce,
441        },
442    }
443
444    #[pallet::hooks]
445    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
446        fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
447            UpdatedChannels::<T>::take();
448            T::DbWeight::get().reads_writes(0, 1)
449        }
450    }
451
452    #[pallet::validate_unsigned]
453    impl<T: Config> ValidateUnsigned for Pallet<T> {
454        type Call = Call<T>;
455
456        fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
457            match call {
458                // always accept inherent extrinsic
459                Call::update_domain_allowlist { .. } => Ok(()),
460                _ => Err(InvalidTransaction::Call.into()),
461            }
462        }
463
464        /// Validate unsigned call to this module.
465        fn validate_unsigned(
466            _source: TransactionSource,
467            _call: &Self::Call,
468        ) -> TransactionValidity {
469            InvalidTransaction::Call.into()
470        }
471    }
472
473    /// `pallet-messenger` errors
474    #[pallet::error]
475    pub enum Error<T> {
476        /// Emits when the chain is neither consensus not chain.
477        InvalidChain,
478
479        /// Emits when there is no channel for a given Channel ID.
480        MissingChannel,
481
482        /// Emits when the said channel is not in an open state.
483        InvalidChannelState,
484
485        /// Emits when there are no open channels for a chain
486        NoOpenChannel,
487
488        /// Emits when there are not message handler with given endpoint ID.
489        NoMessageHandler,
490
491        /// Emits when the outbox is full for a channel.
492        OutboxFull,
493
494        /// Emits when the message payload is invalid.
495        InvalidMessagePayload,
496
497        /// Emits when the message destination is not valid.
498        InvalidMessageDestination,
499
500        /// Emits when the message verification failed.
501        MessageVerification(VerificationError),
502
503        /// Emits when there is no message available for the given nonce.
504        MissingMessage,
505
506        /// Emits when there is mismatch between the message's weight tag and the message's
507        /// actual processing path
508        WeightTagNotMatch,
509
510        /// Emits when the there is balance overflow.
511        BalanceOverflow,
512
513        /// Emits when the there is balance underflow.
514        BalanceUnderflow,
515
516        /// Invalid allowed chain.
517        InvalidAllowedChain,
518
519        /// Operation not allowed.
520        OperationNotAllowed,
521
522        /// Account is not a Domain owner.
523        NotDomainOwner,
524
525        /// Chain not allowed to open channel
526        ChainNotAllowed,
527
528        /// Not enough balance to do the operation
529        InsufficientBalance,
530
531        /// Failed to hold balance
532        BalanceHold,
533
534        /// Not a channel owner
535        ChannelOwner,
536
537        /// Failed to unlock the balance
538        BalanceUnlock,
539
540        /// Invalid channel reserve fee
541        InvalidChannelReserveFee,
542
543        /// Invalid max outgoing messages
544        InvalidMaxOutgoingMessages,
545
546        /// Message count overflow
547        MessageCountOverflow,
548
549        /// Message count underflow
550        MessageCountUnderflow,
551
552        /// Failed to note transfer in
553        FailedToNoteTransferIn,
554
555        /// Failed to note transfer out
556        FailedToNoteTransferOut,
557    }
558
559    #[pallet::call]
560    impl<T: Config> Pallet<T> {
561        /// A new Channel is initiated with a foreign chain.
562        /// Next Channel ID is used to assign the new channel.
563        /// Channel is set to initiated and do not accept or receive any messages.
564        #[pallet::call_index(0)]
565        #[pallet::weight(T::WeightInfo::initiate_channel())]
566        pub fn initiate_channel(origin: OriginFor<T>, dst_chain_id: ChainId) -> DispatchResult {
567            let owner = ensure_signed(origin)?;
568
569            // reserve channel open fees
570            let hold_id = T::HoldIdentifier::messenger_channel();
571            let amount = T::ChannelReserveFee::get();
572
573            // ensure there is enough free balance to lock
574            ensure!(
575                T::Currency::reducible_balance(&owner, Preservation::Preserve, Fortitude::Polite)
576                    >= amount,
577                Error::<T>::InsufficientBalance
578            );
579            T::Currency::hold(&hold_id, &owner, amount).map_err(|_| Error::<T>::BalanceHold)?;
580
581            // initiate the channel config
582            let channel_open_params = ChannelOpenParamsV1 {
583                max_outgoing_messages: T::MaxOutgoingMessages::get(),
584            };
585            let channel_id = Self::do_init_channel(
586                dst_chain_id,
587                channel_open_params,
588                Some(owner.clone()),
589                true,
590                amount,
591            )?;
592
593            let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
594                ProtocolMessageRequest::ChannelOpen(ChannelOpenParamsV1 {
595                    max_outgoing_messages: channel_open_params.max_outgoing_messages,
596                }),
597            )));
598
599            // send message to dst_chain
600            Self::new_outbox_message(T::SelfChainId::get(), dst_chain_id, channel_id, payload)?;
601
602            Ok(())
603        }
604
605        /// An open channel is closed with a foreign chain.
606        /// Channel is set to Closed and do not accept or receive any messages.
607        #[pallet::call_index(1)]
608        #[pallet::weight(T::WeightInfo::close_channel())]
609        pub fn close_channel(
610            origin: OriginFor<T>,
611            chain_id: ChainId,
612            channel_id: ChannelId,
613        ) -> DispatchResult {
614            // either owner can close the channel
615            // or sudo can close the channel
616            let close_channel_by = match ensure_signed_or_root(origin)? {
617                Some(owner) => CloseChannelBy::Owner(owner),
618                None => CloseChannelBy::Sudo,
619            };
620            Self::do_close_channel(chain_id, channel_id, close_channel_by)?;
621
622            let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
623                ProtocolMessageRequest::ChannelClose,
624            )));
625
626            Self::new_outbox_message(T::SelfChainId::get(), chain_id, channel_id, payload)?;
627
628            Ok(())
629        }
630
631        /// Receives an Inbox message that needs to be validated and processed.
632        #[pallet::call_index(2)]
633        #[pallet::weight(T::WeightInfo::relay_message().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
634        pub fn relay_message(
635            origin: OriginFor<T>,
636            msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
637        ) -> DispatchResult {
638            T::MessengerOrigin::ensure_origin(origin)?;
639            let inbox_msg = Inbox::<T>::take().ok_or(Error::<T>::MissingMessage)?;
640            Self::process_inbox_messages(inbox_msg, msg.weight_tag)?;
641            Ok(())
642        }
643
644        /// Receives a response from the dst_chain for a message in Outbox.
645        #[pallet::call_index(3)]
646        #[pallet::weight(T::WeightInfo::relay_message_response().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
647        pub fn relay_message_response(
648            origin: OriginFor<T>,
649            msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
650        ) -> DispatchResult {
651            T::MessengerOrigin::ensure_origin(origin)?;
652            let outbox_resp_msg = OutboxResponses::<T>::take().ok_or(Error::<T>::MissingMessage)?;
653            Self::process_outbox_message_responses(outbox_resp_msg, msg.weight_tag)?;
654            Ok(())
655        }
656
657        /// A call to update consensus chain allow list.
658        #[pallet::call_index(4)]
659        #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
660        pub fn update_consensus_chain_allowlist(
661            origin: OriginFor<T>,
662            update: ChainAllowlistUpdate,
663        ) -> DispatchResult {
664            ensure_root(origin)?;
665            ensure!(
666                T::SelfChainId::get().is_consensus_chain(),
667                Error::<T>::OperationNotAllowed
668            );
669
670            ensure!(
671                update.chain_id() != T::SelfChainId::get(),
672                Error::<T>::InvalidAllowedChain
673            );
674
675            if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
676                ensure!(
677                    T::DomainRegistration::is_domain_registered(domain_id),
678                    Error::<T>::InvalidChain
679                );
680            }
681
682            ChainAllowlist::<T>::mutate(|list| match update {
683                ChainAllowlistUpdate::Add(chain_id) => list.insert(chain_id),
684                ChainAllowlistUpdate::Remove(chain_id) => list.remove(&chain_id),
685            });
686            Ok(())
687        }
688
689        /// A call to initiate chain allowlist update on domains
690        #[pallet::call_index(5)]
691        #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
692        pub fn initiate_domain_update_chain_allowlist(
693            origin: OriginFor<T>,
694            domain_id: DomainId,
695            update: ChainAllowlistUpdate,
696        ) -> DispatchResult {
697            let domain_owner = ensure_signed(origin)?;
698            ensure!(
699                T::DomainOwner::is_domain_owner(domain_id, domain_owner),
700                Error::<T>::NotDomainOwner
701            );
702
703            ensure!(
704                T::SelfChainId::get().is_consensus_chain(),
705                Error::<T>::OperationNotAllowed
706            );
707
708            if let Some(dst_domain_id) = update.chain_id().maybe_domain_chain() {
709                ensure!(dst_domain_id != domain_id, Error::<T>::InvalidAllowedChain);
710            }
711
712            if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
713                ensure!(
714                    T::DomainRegistration::is_domain_registered(domain_id),
715                    Error::<T>::InvalidChain
716                );
717            }
718
719            DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_domain_updates| {
720                let mut domain_updates = maybe_domain_updates.take().unwrap_or_default();
721                match update {
722                    ChainAllowlistUpdate::Add(chain_id) => {
723                        domain_updates.remove_chains.remove(&chain_id);
724                        domain_updates.allow_chains.insert(chain_id);
725                    }
726                    ChainAllowlistUpdate::Remove(chain_id) => {
727                        domain_updates.allow_chains.remove(&chain_id);
728                        domain_updates.remove_chains.insert(chain_id);
729                    }
730                }
731
732                *maybe_domain_updates = Some(domain_updates)
733            });
734            Ok(())
735        }
736
737        /// An inherent call to update allowlist for domain.
738        #[pallet::call_index(6)]
739        #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Mandatory))]
740        pub fn update_domain_allowlist(
741            origin: OriginFor<T>,
742            updates: DomainAllowlistUpdates,
743        ) -> DispatchResult {
744            ensure_none(origin)?;
745            ensure!(
746                !T::SelfChainId::get().is_consensus_chain(),
747                Error::<T>::OperationNotAllowed
748            );
749
750            let DomainAllowlistUpdates {
751                allow_chains,
752                remove_chains,
753            } = updates;
754
755            ChainAllowlist::<T>::mutate(|list| {
756                // remove chains from set
757                remove_chains.into_iter().for_each(|chain_id| {
758                    list.remove(&chain_id);
759                });
760
761                // add new chains
762                allow_chains.into_iter().for_each(|chain_id| {
763                    list.insert(chain_id);
764                });
765            });
766
767            Ok(())
768        }
769    }
770
771    #[pallet::inherent]
772    impl<T: Config> ProvideInherent for Pallet<T> {
773        type Call = Call<T>;
774        type Error = InherentError;
775        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
776
777        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
778            let inherent_data = data
779                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
780                .expect("Messenger inherent data not correctly encoded")
781                .expect("Messenger inherent data must be provided");
782
783            inherent_data
784                .maybe_updates
785                .map(|updates| Call::update_domain_allowlist { updates })
786        }
787
788        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
789            let inherent_data = data
790                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
791                .expect("Messenger inherent data not correctly encoded")
792                .expect("Messenger inherent data must be provided");
793
794            Ok(if inherent_data.maybe_updates.is_none() {
795                None
796            } else {
797                Some(InherentError::MissingAllowlistUpdates)
798            })
799        }
800
801        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
802            let inherent_data = data
803                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
804                .expect("Messenger inherent data not correctly encoded")
805                .expect("Messenger inherent data must be provided");
806
807            if let Some(provided_updates) = inherent_data.maybe_updates {
808                if let Call::update_domain_allowlist { updates } = call
809                    && updates != &provided_updates
810                {
811                    return Err(InherentError::IncorrectAllowlistUpdates);
812                }
813            } else {
814                return Err(InherentError::MissingAllowlistUpdates);
815            }
816
817            Ok(())
818        }
819
820        fn is_inherent(call: &Self::Call) -> bool {
821            matches!(call, Call::update_domain_allowlist { .. })
822        }
823    }
824
825    impl<T: Config> Sender<T::AccountId> for Pallet<T> {
826        type MessageId = MessageId;
827
828        fn send_message(
829            sender: &T::AccountId,
830            dst_chain_id: ChainId,
831            req: EndpointRequest,
832        ) -> Result<Self::MessageId, DispatchError> {
833            let allowed_chains = ChainAllowlist::<T>::get();
834            ensure!(
835                allowed_chains.contains(&dst_chain_id),
836                Error::<T>::ChainNotAllowed
837            );
838
839            let channel_id =
840                Self::get_open_channel_for_chain(dst_chain_id).ok_or(Error::<T>::NoOpenChannel)?;
841
842            let src_endpoint = req.src_endpoint.clone();
843
844            let message_id = {
845                // collect the fees from the sender
846                let collected_fee = Self::collect_fees_for_message_v1(sender, &src_endpoint)?;
847                let src_chain_fee = collected_fee.src_chain_fee;
848                let dst_chain_fee = collected_fee.dst_chain_fee;
849                let nonce = Self::new_outbox_message(
850                    T::SelfChainId::get(),
851                    dst_chain_id,
852                    channel_id,
853                    VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(
854                        EndpointRequestWithCollectedFee { req, collected_fee },
855                    ))),
856                )?;
857
858                // store src_chain, this chain, fee to OutboxFee
859                let message_id = (channel_id, nonce);
860                Self::store_outbox_fee(dst_chain_id, message_id, src_chain_fee, dst_chain_fee)?;
861                message_id
862            };
863
864            Ok(message_id)
865        }
866
867        /// Only used in benchmark to prepare for a upcoming `send_message` call to
868        /// ensure it will succeed.
869        #[cfg(feature = "runtime-benchmarks")]
870        fn unchecked_open_channel(dst_chain_id: ChainId) -> Result<(), DispatchError> {
871            let init_params = ChannelOpenParamsV1 {
872                max_outgoing_messages: 100,
873            };
874            ChainAllowlist::<T>::mutate(|list| list.insert(dst_chain_id));
875            let channel_id =
876                Self::do_init_channel(dst_chain_id, init_params, None, true, Zero::zero())?;
877            Self::do_open_channel(dst_chain_id, channel_id)?;
878            Ok(())
879        }
880    }
881
882    impl<T: Config> Pallet<T> {
883        // Get the weight according the given weight tag
884        fn message_weight(weight_tag: &MessageWeightTag) -> Weight {
885            match weight_tag {
886                MessageWeightTag::ProtocolChannelOpen => T::WeightInfo::do_open_channel(),
887                MessageWeightTag::ProtocolChannelClose => T::WeightInfo::do_close_channel(),
888                MessageWeightTag::EndpointRequest(endpoint) => {
889                    T::get_endpoint_handler(endpoint)
890                        .map(|endpoint_handler| endpoint_handler.message_weight())
891                        // If there is no endpoint handler the request won't be handled thus return zero weight
892                        .unwrap_or(Weight::zero())
893                }
894                MessageWeightTag::EndpointResponse(endpoint) => {
895                    T::get_endpoint_handler(endpoint)
896                        .map(|endpoint_handler| endpoint_handler.message_response_weight())
897                        // If there is no endpoint handler the request won't be handled thus return zero weight
898                        .unwrap_or(Weight::zero())
899                }
900                MessageWeightTag::None => Weight::zero(),
901            }
902        }
903
904        /// Returns the last open channel for a given chain.
905        pub fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option<ChannelId> {
906            let mut next_channel_id = NextChannelId::<T>::get(dst_chain_id);
907
908            // loop through channels in descending order until open channel is found.
909            // we always prefer latest opened channel.
910            while let Some(channel_id) = next_channel_id.checked_sub(ChannelId::one()) {
911                let message_count = OutboxMessageCount::<T>::get((dst_chain_id, channel_id));
912                if let Some(channel) = Channels::<T>::get(dst_chain_id, channel_id)
913                    && channel.state == ChannelState::Open
914                    && message_count < channel.max_outgoing_messages
915                {
916                    return Some(channel_id);
917                }
918
919                next_channel_id = channel_id
920            }
921
922            None
923        }
924
925        /// Opens an initiated channel.
926        pub(crate) fn do_open_channel(chain_id: ChainId, channel_id: ChannelId) -> DispatchResult {
927            Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
928                let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
929
930                ensure!(
931                    channel.state == ChannelState::Initiated,
932                    Error::<T>::InvalidChannelState
933                );
934
935                channel.state = ChannelState::Open;
936                Ok(())
937            })?;
938
939            Self::deposit_event(Event::ChannelOpen {
940                chain_id,
941                channel_id,
942            });
943
944            Ok(())
945        }
946
947        pub(crate) fn do_close_channel(
948            chain_id: ChainId,
949            channel_id: ChannelId,
950            close_channel_by: CloseChannelBy<T::AccountId>,
951        ) -> DispatchResult {
952            Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
953                let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
954
955                ensure!(
956                    channel.state != ChannelState::Closed,
957                    Error::<T>::InvalidChannelState
958                );
959
960                if let CloseChannelBy::Owner(owner) = close_channel_by {
961                    ensure!(channel.maybe_owner == Some(owner), Error::<T>::ChannelOwner);
962                }
963
964                if let Some(owner) = &channel.maybe_owner {
965                    let hold_id = T::HoldIdentifier::messenger_channel();
966                    let locked_amount = channel.channel_reserve_fee;
967                    let (amount_to_release, maybe_amount_to_burn) = {
968                        if channel.state == ChannelState::Open {
969                            (locked_amount, None)
970                        } else {
971                            let protocol_fee = T::ChannelInitReservePortion::get() * locked_amount;
972                            let release_amount = locked_amount.saturating_sub(protocol_fee);
973                            (release_amount, Some(protocol_fee))
974                        }
975                    };
976
977                    with_storage_layer(|| {
978                        if let Some(protocol_fee) = maybe_amount_to_burn {
979                            T::Currency::burn_held(
980                                &hold_id,
981                                owner,
982                                protocol_fee,
983                                Precision::Exact,
984                                Fortitude::Force,
985                            )?;
986                            T::OnXDMRewards::on_chain_protocol_fees(chain_id, protocol_fee);
987                        }
988
989                        T::Currency::release(&hold_id, owner, amount_to_release, Precision::Exact)
990                            .map_err(|_| Error::<T>::BalanceUnlock)?;
991
992                        Ok::<(), DispatchError>(())
993                    })?;
994                }
995
996                channel.state = ChannelState::Closed;
997                Ok(())
998            })?;
999
1000            Self::deposit_event(Event::ChannelClosed {
1001                chain_id,
1002                channel_id,
1003            });
1004
1005            Ok(())
1006        }
1007
1008        pub(crate) fn do_init_channel(
1009            dst_chain_id: ChainId,
1010            init_params: ChannelOpenParamsV1,
1011            maybe_owner: Option<T::AccountId>,
1012            check_allowlist: bool,
1013            channel_reserve_fee: BalanceOf<T>,
1014        ) -> Result<ChannelId, DispatchError> {
1015            ensure!(
1016                T::SelfChainId::get() != dst_chain_id,
1017                Error::<T>::InvalidChain,
1018            );
1019
1020            // ensure max outgoing messages is at least 1
1021            ensure!(
1022                init_params.max_outgoing_messages >= 1u32,
1023                Error::<T>::InvalidMaxOutgoingMessages
1024            );
1025
1026            // If the channel owner is in this chain then the channel reserve fee
1027            // must not be empty
1028            ensure!(
1029                maybe_owner.is_none() || !channel_reserve_fee.is_zero(),
1030                Error::<T>::InvalidChannelReserveFee,
1031            );
1032
1033            if check_allowlist {
1034                let chain_allowlist = ChainAllowlist::<T>::get();
1035                ensure!(
1036                    chain_allowlist.contains(&dst_chain_id),
1037                    Error::<T>::ChainNotAllowed
1038                );
1039            }
1040
1041            let channel_id = NextChannelId::<T>::get(dst_chain_id);
1042            let next_channel_id = channel_id
1043                .checked_add(U256::one())
1044                .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1045
1046            Channels::<T>::insert(
1047                dst_chain_id,
1048                channel_id,
1049                Channel {
1050                    channel_id,
1051                    state: ChannelState::Initiated,
1052                    next_inbox_nonce: Default::default(),
1053                    next_outbox_nonce: Default::default(),
1054                    latest_response_received_message_nonce: Default::default(),
1055                    max_outgoing_messages: init_params.max_outgoing_messages,
1056                    maybe_owner,
1057                    channel_reserve_fee,
1058                },
1059            );
1060
1061            NextChannelId::<T>::insert(dst_chain_id, next_channel_id);
1062            Self::deposit_event(Event::ChannelInitiated {
1063                chain_id: dst_chain_id,
1064                channel_id,
1065            });
1066            Ok(channel_id)
1067        }
1068
1069        pub fn validate_relay_message(
1070            xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1071            consensus_state_root: StateRootOf<T>,
1072        ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1073            let (next_nonce, maybe_channel) =
1074                match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1075                    None => {
1076                        // if there is no channel config, this must the Channel open request.
1077                        // so nonce is 0
1078                        log::debug!(
1079                            "Initiating new channel: {:?} to chain: {:?}",
1080                            xdm.channel_id,
1081                            xdm.src_chain_id
1082                        );
1083                        (Nonce::zero(), None)
1084                    }
1085                    Some(channel) => {
1086                        log::debug!(
1087                            "Message to channel: {:?} from chain: {:?}",
1088                            xdm.channel_id,
1089                            xdm.src_chain_id
1090                        );
1091                        (channel.next_inbox_nonce, Some(channel))
1092                    }
1093                };
1094
1095            // derive the key as stored on the src_chain.
1096            let key = StorageKey(
1097                T::StorageKeys::outbox_storage_key(
1098                    xdm.src_chain_id,
1099                    (T::SelfChainId::get(), xdm.channel_id, xdm.nonce),
1100                )
1101                .ok_or(UnknownTransaction::CannotLookup)?,
1102            );
1103
1104            // verify and decode message
1105            let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1106
1107            let is_valid_call = match &msg.payload {
1108                VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(req))) => {
1109                    match req {
1110                        // channel open should ensure there is no Channel present already
1111                        ProtocolMessageRequest::ChannelOpen(_) => maybe_channel.is_none(),
1112                        // we allow channel close only if it is init or open state
1113                        ProtocolMessageRequest::ChannelClose => {
1114                            if let Some(ref channel) = maybe_channel {
1115                                !(channel.state == ChannelState::Closed)
1116                            } else {
1117                                false
1118                            }
1119                        }
1120                    }
1121                }
1122                // endpoint request messages are only allowed when
1123                // channel is open, or
1124                // channel is closed. Channel can be closed by dst_chain simultaneously
1125                // while src_chain already sent a message. We allow the message but return an
1126                // error in the response so that src_chain can revert any necessary actions
1127                VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(_))) => {
1128                    if let Some(ref channel) = maybe_channel {
1129                        !(channel.state == ChannelState::Initiated)
1130                    } else {
1131                        false
1132                    }
1133                }
1134                // any other message variants are not allowed
1135                _ => false,
1136            };
1137
1138            if !is_valid_call {
1139                log::error!("Unexpected XDM message: {:?}", msg,);
1140                return Err(InvalidTransaction::Call.into());
1141            }
1142
1143            // Reject stale message
1144            if msg.nonce.cmp(&next_nonce) == Ordering::Less {
1145                return Err(InvalidTransaction::Stale.into());
1146            }
1147
1148            let validated_relay_msg = ValidatedRelayMessage {
1149                message: msg,
1150                should_init_channel: maybe_channel.is_none(),
1151                next_nonce,
1152            };
1153
1154            Ok(validated_relay_msg)
1155        }
1156
1157        pub(crate) fn pre_dispatch_relay_message(
1158            msg: Message<BalanceOf<T>>,
1159            should_init_channel: bool,
1160        ) -> Result<(), TransactionValidityError> {
1161            if should_init_channel {
1162                if let VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
1163                    ProtocolMessageRequest::ChannelOpen(params),
1164                ))) = msg.payload
1165                {
1166                    // channel is being opened without an owner since this is a relay message
1167                    // from other chain
1168                    // we do not check the allowlist to finish the end to end flow
1169                    Self::do_init_channel(msg.src_chain_id, params, None, false, Zero::zero())
1170                        .map_err(|err| {
1171                            log::error!(
1172                                "Error initiating channel: {:?} with chain: {:?}: {:?}",
1173                                msg.channel_id,
1174                                msg.src_chain_id,
1175                                err
1176                            );
1177                            InvalidTransaction::Call
1178                        })?;
1179                } else {
1180                    log::error!("Unexpected call instead of channel open request: {:?}", msg,);
1181                    return Err(InvalidTransaction::Call.into());
1182                }
1183            }
1184
1185            let (dst_chain_id, channel_id, nonce) = (msg.src_chain_id, msg.channel_id, msg.nonce);
1186            // future nonce check is already validated by the extension
1187            // it is safe to increment the next nonce here before processing.
1188            Channels::<T>::mutate(
1189                dst_chain_id,
1190                channel_id,
1191                |maybe_channel| -> sp_runtime::DispatchResult {
1192                    let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
1193                    channel.next_inbox_nonce = nonce
1194                        .checked_add(Nonce::one())
1195                        .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1196                    Ok(())
1197                },
1198            )
1199            .map_err(|err| {
1200                log::error!(
1201                    "Failed to increment the next relay message nonce for Chain[{:?}] with Channel[{:?}]: {:?}",
1202                    dst_chain_id,
1203                    channel_id,
1204                    err,
1205                );
1206                InvalidTransaction::Custom(crate::verification_errors::NEXT_NONCE_UPDATE)
1207            })?;
1208
1209            Self::deposit_event(Event::InboxMessage {
1210                chain_id: msg.src_chain_id,
1211                channel_id: msg.channel_id,
1212                nonce: msg.nonce,
1213            });
1214            Inbox::<T>::put(msg);
1215            Ok(())
1216        }
1217
1218        pub fn validate_relay_message_response(
1219            xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1220            consensus_state_root: StateRootOf<T>,
1221        ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1222            // channel should be open and message should be present in outbox
1223            let next_nonce =
1224                match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1225                    // unknown channel. return
1226                    None => {
1227                        log::error!("Unexpected inbox message response: {:?}", xdm,);
1228                        return Err(InvalidTransaction::Call.into());
1229                    }
1230                    Some(channel) => match channel.latest_response_received_message_nonce {
1231                        None => Nonce::zero(),
1232                        Some(last_nonce) => last_nonce.checked_add(Nonce::one()).ok_or(
1233                            InvalidTransaction::Custom(
1234                                crate::verification_errors::XDM_NONCE_OVERFLOW,
1235                            ),
1236                        )?,
1237                    },
1238                };
1239
1240            // derive the key as stored on the src_chain.
1241            let key = StorageKey(
1242                T::StorageKeys::inbox_responses_storage_key(
1243                    xdm.src_chain_id,
1244                    (T::SelfChainId::get(), xdm.channel_id, xdm.nonce),
1245                )
1246                .ok_or(UnknownTransaction::CannotLookup)?,
1247            );
1248
1249            // verify, decode, and store the message
1250            let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1251
1252            // Reject stale message
1253            if msg.nonce.cmp(&next_nonce) == Ordering::Less {
1254                return Err(InvalidTransaction::Stale.into());
1255            }
1256
1257            let validated_relay_msg = ValidatedRelayMessage {
1258                message: msg,
1259                next_nonce,
1260                // not applicable in relay message response, default should be fine here
1261                should_init_channel: false,
1262            };
1263
1264            Ok(validated_relay_msg)
1265        }
1266
1267        pub(crate) fn pre_dispatch_relay_message_response(
1268            msg: Message<BalanceOf<T>>,
1269        ) -> Result<(), TransactionValidityError> {
1270            // future nonce check is already validated by the extension
1271            // it is safe to increment the next nonce here before processing.
1272            let (dst_chain_id, channel_id, nonce) = (msg.src_chain_id, msg.channel_id, msg.nonce);
1273            Channels::<T>::mutate(
1274                dst_chain_id,
1275                channel_id,
1276                |maybe_channel| -> sp_runtime::DispatchResult {
1277                    let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
1278                    channel.latest_response_received_message_nonce = Some(nonce);
1279                    Ok(())
1280                },
1281            )
1282            .map_err(|err| {
1283                log::error!(
1284                    "Failed to increment the next relay message response nonce for Chain[{:?}] with Channel[{:?}]: {:?}",
1285                    dst_chain_id,
1286                    channel_id,
1287                    err,
1288                );
1289                InvalidTransaction::Custom(crate::verification_errors::NEXT_NONCE_UPDATE)
1290            })?;
1291
1292            Self::deposit_event(Event::OutboxMessageResponse {
1293                chain_id: msg.src_chain_id,
1294                channel_id: msg.channel_id,
1295                nonce: msg.nonce,
1296            });
1297
1298            OutboxResponses::<T>::put(msg);
1299            Ok(())
1300        }
1301
1302        pub(crate) fn do_verify_xdm(
1303            next_nonce: Nonce,
1304            storage_key: StorageKey,
1305            consensus_state_root: StateRootOf<T>,
1306            xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1307        ) -> Result<Message<BalanceOf<T>>, TransactionValidityError> {
1308            // channel should be either already be created or match the next channelId for chain.
1309            let next_channel_id = NextChannelId::<T>::get(xdm.src_chain_id);
1310            ensure!(
1311                xdm.channel_id <= next_channel_id,
1312                InvalidTransaction::Custom(crate::verification_errors::INVALID_CHANNEL)
1313            );
1314
1315            // verify nonce
1316            // nonce should be either be next or in future.
1317            ensure!(
1318                xdm.nonce >= next_nonce,
1319                InvalidTransaction::Custom(crate::verification_errors::INVALID_NONCE)
1320            );
1321
1322            // if the message is from domain, verify domain confirmation proof
1323            let state_root = if let Some(domain_proof) = xdm.proof.domain_proof().clone()
1324                && let Some(domain_id) = xdm.src_chain_id.maybe_domain_chain()
1325            {
1326                let confirmed_domain_block_storage_key =
1327                    T::StorageKeys::confirmed_domain_block_storage_key(domain_id)
1328                        .ok_or(UnknownTransaction::CannotLookup)?;
1329
1330                *StorageProofVerifier::<T::Hashing>::get_decoded_value::<
1331                    sp_domains::execution_receipt::ExecutionReceipt<
1332                        BlockNumberFor<T>,
1333                        T::Hash,
1334                        BlockNumberFor<T>,
1335                        T::Hash,
1336                        BalanceOf<T>,
1337                    >,
1338                >(
1339                    &consensus_state_root,
1340                    domain_proof,
1341                    StorageKey(confirmed_domain_block_storage_key),
1342                )
1343                .map_err(|err| {
1344                    log::error!(
1345                        "Failed to verify storage proof for confirmed Domain block: {:?}",
1346                        err
1347                    );
1348                    TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1349                })?
1350                .final_state_root()
1351            } else {
1352                consensus_state_root
1353            };
1354
1355            // verify and decode the message
1356            let msg =
1357                StorageProofVerifier::<T::Hashing>::get_decoded_value::<Message<BalanceOf<T>>>(
1358                    &state_root,
1359                    xdm.proof.message_proof(),
1360                    storage_key,
1361                )
1362                .map_err(|err| {
1363                    log::error!("Failed to verify storage proof for message: {:?}", err);
1364                    TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1365                })?;
1366
1367            Ok(msg)
1368        }
1369
1370        pub fn outbox_storage_key(message_key: MessageKey) -> Vec<u8> {
1371            Outbox::<T>::hashed_key_for(message_key)
1372        }
1373
1374        pub fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8> {
1375            InboxResponses::<T>::hashed_key_for(message_key)
1376        }
1377
1378        pub fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8> {
1379            Channels::<T>::hashed_key_for(chain_id, channel_id)
1380        }
1381
1382        pub fn domain_chains_allowlist_update(
1383            domain_id: DomainId,
1384        ) -> Option<DomainAllowlistUpdates> {
1385            DomainChainAllowlistUpdate::<T>::get(domain_id).filter(|updates| !updates.is_empty())
1386        }
1387
1388        pub fn domain_allow_list_update_storage_key(domain_id: DomainId) -> Vec<u8> {
1389            DomainChainAllowlistUpdate::<T>::hashed_key_for(domain_id)
1390        }
1391
1392        pub fn updated_channels() -> BTreeSet<(ChainId, ChannelId)> {
1393            UpdatedChannels::<T>::get()
1394        }
1395
1396        pub fn open_channels() -> BTreeSet<(ChainId, ChannelId)> {
1397            Channels::<T>::iter_keys().collect()
1398        }
1399
1400        pub fn channels_and_states() -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)> {
1401            let keys: Vec<(ChainId, ChannelId)> = Channels::<T>::iter_keys().collect();
1402            keys.into_iter()
1403                .filter_map(|(chain_id, channel_id)| {
1404                    Channels::<T>::get(chain_id, channel_id).map(|channel| {
1405                        let state = channel.state;
1406                        let state_with_nonce = match state {
1407                            ChannelState::Initiated => ChannelStateWithNonce::Initiated,
1408                            ChannelState::Open => ChannelStateWithNonce::Open,
1409                            ChannelState::Closed => ChannelStateWithNonce::Closed {
1410                                next_outbox_nonce: channel.next_outbox_nonce,
1411                                next_inbox_nonce: channel.next_inbox_nonce,
1412                            },
1413                        };
1414
1415                        (chain_id, channel_id, state_with_nonce)
1416                    })
1417                })
1418                .collect()
1419        }
1420
1421        pub fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce> {
1422            Channels::<T>::get(chain_id, channel_id).map(|channel| {
1423                let last_inbox_nonce = channel.next_inbox_nonce.checked_sub(U256::one());
1424                ChannelNonce {
1425                    relay_msg_nonce: last_inbox_nonce,
1426                    relay_response_msg_nonce: channel.latest_response_received_message_nonce,
1427                }
1428            })
1429        }
1430
1431        pub fn store_inbox_fee(
1432            src_chain_id: ChainId,
1433            message_id: MessageId,
1434            inbox_fees: BalanceOf<T>,
1435        ) -> DispatchResult {
1436            InboxFeesOnHold::<T>::mutate(|inbox_fees_on_hold| {
1437                *inbox_fees_on_hold = inbox_fees_on_hold
1438                    .checked_add(&inbox_fees)
1439                    .ok_or(Error::<T>::BalanceOverflow)?;
1440
1441                // If the `imbalance` is dropped without consuming it will reduce the total issuance by
1442                // the same amount as we issued here, thus we need to manually `mem::forget` it.
1443                let imbalance = T::Currency::issue(inbox_fees);
1444                core::mem::forget(imbalance);
1445
1446                Ok::<(), Error<T>>(())
1447            })?;
1448
1449            InboxFee::<T>::insert((src_chain_id, message_id), inbox_fees);
1450
1451            // Note `dst_chain_fee` as transfer in
1452            if !T::NoteChainTransfer::note_transfer_in(inbox_fees, src_chain_id) {
1453                return Err(Error::<T>::FailedToNoteTransferIn.into());
1454            }
1455
1456            Ok(())
1457        }
1458
1459        pub fn store_outbox_fee(
1460            dst_chain_id: ChainId,
1461            message_id: MessageId,
1462            outbox_fees: BalanceOf<T>,
1463            inbox_fees: BalanceOf<T>,
1464        ) -> DispatchResult {
1465            OutboxFeesOnHold::<T>::mutate(|outbox_fees_on_hold| {
1466                *outbox_fees_on_hold = outbox_fees_on_hold
1467                    .checked_add(&outbox_fees)
1468                    .ok_or(Error::<T>::BalanceOverflow)?;
1469
1470                // If the `imbalance` is dropped without consuming it will reduce the total issuance by
1471                // the same amount as we issued here, thus we need to manually `mem::forget` it.
1472                let imbalance = T::Currency::issue(outbox_fees);
1473                core::mem::forget(imbalance);
1474
1475                Ok::<(), Error<T>>(())
1476            })?;
1477
1478            OutboxFee::<T>::insert((dst_chain_id, message_id), outbox_fees);
1479
1480            // Note `dst_chain_fee` as transfer out
1481            if !T::NoteChainTransfer::note_transfer_out(inbox_fees, dst_chain_id) {
1482                return Err(Error::<T>::FailedToNoteTransferOut.into());
1483            }
1484
1485            Ok(())
1486        }
1487    }
1488}
1489
1490impl<T> Pallet<T>
1491where
1492    T: Config + CreateUnsigned<Call<T>>,
1493{
1494    pub fn outbox_message_unsigned(
1495        msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1496    ) -> Option<T::Extrinsic> {
1497        let call = Call::relay_message { msg };
1498        Some(T::create_unsigned(call.into()))
1499    }
1500
1501    pub fn inbox_response_message_unsigned(
1502        msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1503    ) -> Option<T::Extrinsic> {
1504        let call = Call::relay_message_response { msg };
1505        Some(T::create_unsigned(call.into()))
1506    }
1507
1508    /// Returns the first outbox message nonce that should be relayed to the dst_chain.
1509    pub fn first_outbox_message_nonce_to_relay(
1510        dst_chain_id: ChainId,
1511        channel_id: ChannelId,
1512        from_nonce: Nonce,
1513    ) -> Option<Nonce> {
1514        Self::first_relay_message(
1515            dst_chain_id,
1516            channel_id,
1517            from_nonce,
1518            Outbox::<T>::contains_key,
1519        )
1520    }
1521
1522    /// Returns the first inbox response message nonce that should be relayed to the dst_chain.
1523    pub fn first_inbox_message_response_nonce_to_relay(
1524        dst_chain_id: ChainId,
1525        channel_id: ChannelId,
1526        from_nonce: Nonce,
1527    ) -> Option<Nonce> {
1528        Self::first_relay_message(
1529            dst_chain_id,
1530            channel_id,
1531            from_nonce,
1532            InboxResponses::<T>::contains_key,
1533        )
1534    }
1535
1536    fn first_relay_message<Check>(
1537        dst_chain_id: ChainId,
1538        channel_id: ChannelId,
1539        from_nonce: Nonce,
1540        check: Check,
1541    ) -> Option<Nonce>
1542    where
1543        Check: Fn((ChainId, ChannelId, Nonce)) -> bool,
1544    {
1545        let mut nonce = from_nonce;
1546        let to_nonce = from_nonce.saturating_add(MAX_FUTURE_ALLOWED_NONCES.into());
1547        while nonce <= to_nonce {
1548            if check((dst_chain_id, channel_id, nonce)) {
1549                return Some(nonce);
1550            }
1551
1552            nonce = nonce.saturating_add(Nonce::one())
1553        }
1554
1555        None
1556    }
1557}
1558
1559impl<T: Config> sp_domains::DomainBundleSubmitted for Pallet<T> {
1560    fn domain_bundle_submitted(domain_id: DomainId) {
1561        // NOTE: clear the updates leave an empty value but does not delete the value for the
1562        // domain completely because in the invalid extrinsic root fraud proof the prover need
1563        // to generate a proof-of-empty-value for the domain.
1564        DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_updates| {
1565            if let Some(updates) = maybe_updates {
1566                updates.clear();
1567            }
1568        });
1569    }
1570}
1571
1572impl<T: Config> sp_domains::OnDomainInstantiated for Pallet<T> {
1573    fn on_domain_instantiated(domain_id: DomainId) {
1574        DomainChainAllowlistUpdate::<T>::insert(domain_id, DomainAllowlistUpdates::default());
1575    }
1576}