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