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