1#![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;
54
55const XDM_TRANSACTION_LONGEVITY: u64 = 10;
58
59pub(crate) mod verification_errors {
60 pub(crate) const INVALID_NONCE: u8 = 201;
64 pub(crate) const NONCE_OVERFLOW: u8 = 202;
65 pub(crate) const INVALID_CHANNEL: u8 = 203;
67 pub(crate) const IN_FUTURE_NONCE: u8 = 204;
68 pub(crate) const NEXT_NONCE_UPDATE: u8 = 205;
70}
71
72#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
73pub enum OutboxMessageResult {
74 Ok,
76 Err(DispatchError),
78}
79
80#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
82pub enum RawOrigin {
83 ValidatedUnsigned,
84}
85
86pub struct EnsureMessengerOrigin;
88impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O> for EnsureMessengerOrigin {
89 type Success = ();
90
91 fn try_origin(o: O) -> Result<Self::Success, O> {
92 o.into().map(|o| match o {
93 RawOrigin::ValidatedUnsigned => (),
94 })
95 }
96
97 #[cfg(feature = "runtime-benchmarks")]
98 fn try_successful_origin() -> Result<O, ()> {
99 Ok(O::from(RawOrigin::ValidatedUnsigned))
100 }
101}
102
103pub(crate) type StateRootOf<T> = <<T as frame_system::Config>::Hashing as Hash>::Output;
104pub(crate) type BalanceOf<T> =
105 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
106pub(crate) type FungibleHoldId<T> =
107 <<T as Config>::Currency as InspectHold<<T as frame_system::Config>::AccountId>>::Reason;
108
109#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
111pub enum ChainAllowlistUpdate {
112 Add(ChainId),
113 Remove(ChainId),
114}
115
116impl ChainAllowlistUpdate {
117 fn chain_id(&self) -> ChainId {
118 match self {
119 ChainAllowlistUpdate::Add(chain_id) => *chain_id,
120 ChainAllowlistUpdate::Remove(chain_id) => *chain_id,
121 }
122 }
123}
124
125#[derive(Debug, Encode, Decode, TypeInfo)]
126pub struct ValidatedRelayMessage<T: Config> {
127 pub message: Message<BalanceOf<T>>,
128 pub should_init_channel: bool,
129 pub next_nonce: Nonce,
130}
131
132#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Copy)]
134pub(crate) enum CloseChannelBy<AccountId> {
135 Owner(AccountId),
136 Sudo,
137}
138
139pub trait HoldIdentifier<T: Config> {
141 fn messenger_channel() -> FungibleHoldId<T>;
142}
143
144const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
146
147#[frame_support::pallet]
148mod pallet {
149 pub use crate::extensions::weights::WeightInfo as ExtensionWeightInfo;
150 use crate::weights::WeightInfo;
151 use crate::{
152 BalanceOf, ChainAllowlistUpdate, Channel, ChannelId, ChannelState, CloseChannelBy,
153 HoldIdentifier, Nonce, OutboxMessageResult, RawOrigin, STORAGE_VERSION, StateRootOf, U256,
154 ValidatedRelayMessage,
155 };
156 #[cfg(not(feature = "std"))]
157 use alloc::boxed::Box;
158 #[cfg(not(feature = "std"))]
159 use alloc::collections::BTreeSet;
160 #[cfg(not(feature = "std"))]
161 use alloc::vec::Vec;
162 use core::cmp::Ordering;
163 use frame_support::ensure;
164 use frame_support::pallet_prelude::*;
165 use frame_support::storage::with_storage_layer;
166 use frame_support::traits::fungible::{Balanced, Inspect, InspectHold, Mutate, MutateHold};
167 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
168 use frame_support::weights::WeightToFee;
169 use frame_system::pallet_prelude::*;
170 use sp_core::storage::StorageKey;
171 use sp_domains::proof_provider_and_verifier::{StorageProofVerifier, VerificationError};
172 use sp_domains::{DomainAllowlistUpdates, DomainId, DomainOwner};
173 use sp_messenger::endpoint::{
174 Endpoint, EndpointHandler, EndpointRequest, EndpointRequestWithCollectedFee, Sender,
175 };
176 use sp_messenger::messages::{
177 ChainId, ChannelOpenParamsV1, ChannelStateWithNonce, CrossDomainMessage, Message,
178 MessageId, MessageKey, MessageWeightTag, PayloadV1, ProtocolMessageRequest,
179 RequestResponse, VersionedPayload,
180 };
181 use sp_messenger::{
182 ChannelNonce, DomainRegistration, INHERENT_IDENTIFIER, InherentError, InherentType,
183 NoteChainTransfer, OnXDMRewards, StorageKeys,
184 };
185 use sp_runtime::traits::Zero;
186 use sp_runtime::{ArithmeticError, Perbill, Saturating};
187 use sp_subspace_mmr::MmrProofVerifier;
188 #[cfg(feature = "std")]
189 use std::collections::BTreeSet;
190
191 #[pallet::config]
192 pub trait Config: frame_system::Config {
193 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
194 type SelfChainId: Get<ChainId>;
196 fn get_endpoint_handler(endpoint: &Endpoint)
198 -> Option<Box<dyn EndpointHandler<MessageId>>>;
199 type Currency: Mutate<Self::AccountId>
201 + InspectHold<Self::AccountId>
202 + MutateHold<Self::AccountId>
203 + Balanced<Self::AccountId>;
204 type WeightInfo: WeightInfo;
206 type WeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
208 type AdjustedWeightToFee: WeightToFee<Balance = BalanceOf<Self>>;
211 #[pallet::constant]
214 type FeeMultiplier: Get<u32>;
215 type OnXDMRewards: OnXDMRewards<BalanceOf<Self>>;
217 type MmrHash: Parameter + Member + Default + Clone;
219 type MmrProofVerifier: MmrProofVerifier<Self::MmrHash, BlockNumberFor<Self>, StateRootOf<Self>>;
221 type StorageKeys: StorageKeys;
223 type DomainOwner: DomainOwner<Self::AccountId>;
225 type HoldIdentifier: HoldIdentifier<Self>;
227 #[pallet::constant]
229 type ChannelReserveFee: Get<BalanceOf<Self>>;
230 #[pallet::constant]
233 type ChannelInitReservePortion: Get<Perbill>;
234 type DomainRegistration: DomainRegistration;
236 #[pallet::constant]
238 type MaxOutgoingMessages: Get<u32>;
239 type MessengerOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ()>;
241 type NoteChainTransfer: NoteChainTransfer<BalanceOf<Self>>;
243 type ExtensionWeightInfo: ExtensionWeightInfo;
245 }
246
247 #[pallet::pallet]
249 #[pallet::without_storage_info]
250 #[pallet::storage_version(STORAGE_VERSION)]
251 pub struct Pallet<T>(_);
252
253 #[pallet::storage]
255 #[pallet::getter(fn next_channel_id)]
256 pub(super) type NextChannelId<T: Config> =
257 StorageMap<_, Identity, ChainId, ChannelId, ValueQuery>;
258
259 #[pallet::storage]
262 #[pallet::getter(fn channels)]
263 pub(super) type Channels<T: Config> = StorageDoubleMap<
264 _,
265 Identity,
266 ChainId,
267 Identity,
268 ChannelId,
269 Channel<BalanceOf<T>, T::AccountId>,
270 OptionQuery,
271 >;
272
273 #[pallet::storage]
276 #[pallet::getter(fn inbox)]
277 pub(super) type Inbox<T: Config> = StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
278
279 #[pallet::storage]
283 #[pallet::getter(fn inbox_fees)]
284 pub(super) type InboxFee<T: Config> =
285 StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
286
287 #[pallet::storage]
290 #[pallet::getter(fn outbox_fees)]
291 pub(super) type OutboxFee<T: Config> =
292 StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
293
294 #[pallet::storage]
297 #[pallet::getter(fn inbox_responses)]
298 pub(super) type InboxResponses<T: Config> =
299 StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
300
301 #[pallet::storage]
304 #[pallet::getter(fn outbox)]
305 pub(super) type Outbox<T: Config> =
306 StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
307
308 #[pallet::storage]
310 #[pallet::getter(fn outbox_message_count)]
311 pub(super) type OutboxMessageCount<T: Config> =
312 StorageMap<_, Identity, (ChainId, ChannelId), u32, ValueQuery>;
313
314 #[pallet::storage]
317 #[pallet::getter(fn outbox_responses)]
318 pub(super) type OutboxResponses<T: Config> =
319 StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
320
321 #[pallet::storage]
323 #[pallet::getter(fn outbox_message_weight_tags)]
324 pub(super) type OutboxMessageWeightTags<T: Config> =
325 StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
326
327 #[pallet::storage]
329 #[pallet::getter(fn inbox_response_message_weight_tags)]
330 pub(super) type InboxResponseMessageWeightTags<T: Config> =
331 StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
332
333 #[pallet::storage]
335 #[pallet::getter(fn chain_allowlist)]
336 pub(super) type ChainAllowlist<T: Config> = StorageValue<_, BTreeSet<ChainId>, ValueQuery>;
337
338 #[pallet::storage]
342 #[pallet::getter(fn domain_chain_allowlist_updates)]
343 pub(super) type DomainChainAllowlistUpdate<T: Config> =
344 StorageMap<_, Identity, DomainId, DomainAllowlistUpdates, OptionQuery>;
345
346 #[pallet::storage]
349 pub(super) type UpdatedChannels<T: Config> =
350 StorageValue<_, BTreeSet<(ChainId, ChannelId)>, ValueQuery>;
351
352 #[pallet::storage]
359 pub(super) type InboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
360
361 #[pallet::storage]
368 pub(super) type OutboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
369
370 #[pallet::storage]
376 pub(super) type InboxFeesOnHoldStartAt<T: Config> =
377 StorageMap<_, Identity, ChannelId, Nonce, OptionQuery>;
378 #[pallet::storage]
379 pub(super) type OutboxFeesOnHoldStartAt<T: Config> =
380 StorageMap<_, Identity, ChannelId, Nonce, OptionQuery>;
381
382 #[pallet::origin]
383 pub type Origin = RawOrigin;
384
385 #[pallet::event]
387 #[pallet::generate_deposit(pub (super) fn deposit_event)]
388 pub enum Event<T: Config> {
389 ChannelInitiated {
391 chain_id: ChainId,
393 channel_id: ChannelId,
395 },
396
397 ChannelClosed {
399 chain_id: ChainId,
401 channel_id: ChannelId,
403 },
404
405 ChannelOpen {
407 chain_id: ChainId,
409 channel_id: ChannelId,
411 },
412
413 OutboxMessage {
415 chain_id: ChainId,
416 channel_id: ChannelId,
417 nonce: Nonce,
418 },
419
420 OutboxMessageResponse {
422 chain_id: ChainId,
424 channel_id: ChannelId,
426 nonce: Nonce,
427 },
428
429 OutboxMessageResult {
431 chain_id: ChainId,
432 channel_id: ChannelId,
433 nonce: Nonce,
434 result: OutboxMessageResult,
435 },
436
437 InboxMessage {
439 chain_id: ChainId,
440 channel_id: ChannelId,
441 nonce: Nonce,
442 },
443
444 InboxMessageResponse {
446 chain_id: ChainId,
448 channel_id: ChannelId,
450 nonce: Nonce,
451 },
452 }
453
454 #[pallet::hooks]
455 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
456 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
457 UpdatedChannels::<T>::take();
458 T::DbWeight::get().reads_writes(0, 1)
459 }
460 }
461
462 #[pallet::validate_unsigned]
463 impl<T: Config> ValidateUnsigned for Pallet<T> {
464 type Call = Call<T>;
465
466 fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
467 match call {
468 Call::update_domain_allowlist { .. } => Ok(()),
470 _ => Err(InvalidTransaction::Call.into()),
471 }
472 }
473
474 fn validate_unsigned(
476 _source: TransactionSource,
477 _call: &Self::Call,
478 ) -> TransactionValidity {
479 InvalidTransaction::Call.into()
480 }
481 }
482
483 #[pallet::error]
485 pub enum Error<T> {
486 InvalidChain,
488
489 MissingChannel,
491
492 InvalidChannelState,
494
495 NoOpenChannel,
497
498 NoMessageHandler,
500
501 OutboxFull,
503
504 InvalidMessagePayload,
506
507 InvalidMessageDestination,
509
510 MessageVerification(VerificationError),
512
513 MissingMessage,
515
516 WeightTagNotMatch,
519
520 BalanceOverflow,
522
523 BalanceUnderflow,
525
526 InvalidAllowedChain,
528
529 OperationNotAllowed,
531
532 NotDomainOwner,
534
535 ChainNotAllowed,
537
538 InsufficientBalance,
540
541 BalanceHold,
543
544 ChannelOwner,
546
547 BalanceUnlock,
549
550 InvalidChannelReserveFee,
552
553 InvalidMaxOutgoingMessages,
555
556 MessageCountOverflow,
558
559 MessageCountUnderflow,
561
562 FailedToNoteTransferIn,
564
565 FailedToNoteTransferOut,
567 }
568
569 #[pallet::call]
570 impl<T: Config> Pallet<T> {
571 #[pallet::call_index(0)]
575 #[pallet::weight(T::WeightInfo::initiate_channel())]
576 pub fn initiate_channel(origin: OriginFor<T>, dst_chain_id: ChainId) -> DispatchResult {
577 let owner = ensure_signed(origin)?;
578
579 let hold_id = T::HoldIdentifier::messenger_channel();
581 let amount = T::ChannelReserveFee::get();
582
583 ensure!(
585 T::Currency::reducible_balance(&owner, Preservation::Preserve, Fortitude::Polite)
586 >= amount,
587 Error::<T>::InsufficientBalance
588 );
589 T::Currency::hold(&hold_id, &owner, amount).map_err(|_| Error::<T>::BalanceHold)?;
590
591 let channel_open_params = ChannelOpenParamsV1 {
593 max_outgoing_messages: T::MaxOutgoingMessages::get(),
594 };
595 let channel_id = Self::do_init_channel(
596 dst_chain_id,
597 channel_open_params,
598 Some(owner.clone()),
599 true,
600 amount,
601 )?;
602
603 let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
604 ProtocolMessageRequest::ChannelOpen(ChannelOpenParamsV1 {
605 max_outgoing_messages: channel_open_params.max_outgoing_messages,
606 }),
607 )));
608
609 Self::new_outbox_message(T::SelfChainId::get(), dst_chain_id, channel_id, payload)?;
611
612 Ok(())
613 }
614
615 #[pallet::call_index(1)]
618 #[pallet::weight(T::WeightInfo::close_channel())]
619 pub fn close_channel(
620 origin: OriginFor<T>,
621 chain_id: ChainId,
622 channel_id: ChannelId,
623 ) -> DispatchResult {
624 let close_channel_by = match ensure_signed_or_root(origin)? {
627 Some(owner) => CloseChannelBy::Owner(owner),
628 None => CloseChannelBy::Sudo,
629 };
630 Self::do_close_channel(chain_id, channel_id, close_channel_by)?;
631
632 let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
633 ProtocolMessageRequest::ChannelClose,
634 )));
635
636 Self::new_outbox_message(T::SelfChainId::get(), chain_id, channel_id, payload)?;
637
638 Ok(())
639 }
640
641 #[pallet::call_index(2)]
643 #[pallet::weight(T::WeightInfo::relay_message().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
644 pub fn relay_message(
645 origin: OriginFor<T>,
646 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
647 ) -> DispatchResult {
648 T::MessengerOrigin::ensure_origin(origin)?;
649 let inbox_msg = Inbox::<T>::take().ok_or(Error::<T>::MissingMessage)?;
650 Self::process_inbox_messages(inbox_msg, msg.weight_tag)?;
651 Ok(())
652 }
653
654 #[pallet::call_index(3)]
656 #[pallet::weight(T::WeightInfo::relay_message_response().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
657 pub fn relay_message_response(
658 origin: OriginFor<T>,
659 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
660 ) -> DispatchResult {
661 T::MessengerOrigin::ensure_origin(origin)?;
662 let outbox_resp_msg = OutboxResponses::<T>::take().ok_or(Error::<T>::MissingMessage)?;
663 Self::process_outbox_message_responses(outbox_resp_msg, msg.weight_tag)?;
664 Ok(())
665 }
666
667 #[pallet::call_index(4)]
669 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
670 pub fn update_consensus_chain_allowlist(
671 origin: OriginFor<T>,
672 update: ChainAllowlistUpdate,
673 ) -> DispatchResult {
674 ensure_root(origin)?;
675 ensure!(
676 T::SelfChainId::get().is_consensus_chain(),
677 Error::<T>::OperationNotAllowed
678 );
679
680 ensure!(
681 update.chain_id() != T::SelfChainId::get(),
682 Error::<T>::InvalidAllowedChain
683 );
684
685 if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
686 ensure!(
687 T::DomainRegistration::is_domain_registered(domain_id),
688 Error::<T>::InvalidChain
689 );
690 }
691
692 ChainAllowlist::<T>::mutate(|list| match update {
693 ChainAllowlistUpdate::Add(chain_id) => list.insert(chain_id),
694 ChainAllowlistUpdate::Remove(chain_id) => list.remove(&chain_id),
695 });
696 Ok(())
697 }
698
699 #[pallet::call_index(5)]
701 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
702 pub fn initiate_domain_update_chain_allowlist(
703 origin: OriginFor<T>,
704 domain_id: DomainId,
705 update: ChainAllowlistUpdate,
706 ) -> DispatchResult {
707 let domain_owner = ensure_signed(origin)?;
708 ensure!(
709 T::DomainOwner::is_domain_owner(domain_id, domain_owner),
710 Error::<T>::NotDomainOwner
711 );
712
713 ensure!(
714 T::SelfChainId::get().is_consensus_chain(),
715 Error::<T>::OperationNotAllowed
716 );
717
718 if let Some(dst_domain_id) = update.chain_id().maybe_domain_chain() {
719 ensure!(dst_domain_id != domain_id, Error::<T>::InvalidAllowedChain);
720 }
721
722 if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
723 ensure!(
724 T::DomainRegistration::is_domain_registered(domain_id),
725 Error::<T>::InvalidChain
726 );
727 }
728
729 DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_domain_updates| {
730 let mut domain_updates = maybe_domain_updates.take().unwrap_or_default();
731 match update {
732 ChainAllowlistUpdate::Add(chain_id) => {
733 domain_updates.remove_chains.remove(&chain_id);
734 domain_updates.allow_chains.insert(chain_id);
735 }
736 ChainAllowlistUpdate::Remove(chain_id) => {
737 domain_updates.allow_chains.remove(&chain_id);
738 domain_updates.remove_chains.insert(chain_id);
739 }
740 }
741
742 *maybe_domain_updates = Some(domain_updates)
743 });
744 Ok(())
745 }
746
747 #[pallet::call_index(6)]
749 #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Mandatory))]
750 pub fn update_domain_allowlist(
751 origin: OriginFor<T>,
752 updates: DomainAllowlistUpdates,
753 ) -> DispatchResult {
754 ensure_none(origin)?;
755 ensure!(
756 !T::SelfChainId::get().is_consensus_chain(),
757 Error::<T>::OperationNotAllowed
758 );
759
760 let DomainAllowlistUpdates {
761 allow_chains,
762 remove_chains,
763 } = updates;
764
765 ChainAllowlist::<T>::mutate(|list| {
766 remove_chains.into_iter().for_each(|chain_id| {
768 list.remove(&chain_id);
769 });
770
771 allow_chains.into_iter().for_each(|chain_id| {
773 list.insert(chain_id);
774 });
775 });
776
777 Ok(())
778 }
779 }
780
781 #[pallet::inherent]
782 impl<T: Config> ProvideInherent for Pallet<T> {
783 type Call = Call<T>;
784 type Error = InherentError;
785 const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
786
787 fn create_inherent(data: &InherentData) -> Option<Self::Call> {
788 let inherent_data = data
789 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
790 .expect("Messenger inherent data not correctly encoded")
791 .expect("Messenger inherent data must be provided");
792
793 inherent_data
794 .maybe_updates
795 .map(|updates| Call::update_domain_allowlist { updates })
796 }
797
798 fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
799 let inherent_data = data
800 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
801 .expect("Messenger inherent data not correctly encoded")
802 .expect("Messenger inherent data must be provided");
803
804 Ok(if inherent_data.maybe_updates.is_none() {
805 None
806 } else {
807 Some(InherentError::MissingAllowlistUpdates)
808 })
809 }
810
811 fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
812 let inherent_data = data
813 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
814 .expect("Messenger inherent data not correctly encoded")
815 .expect("Messenger inherent data must be provided");
816
817 if let Some(provided_updates) = inherent_data.maybe_updates {
818 if let Call::update_domain_allowlist { updates } = call
819 && updates != &provided_updates
820 {
821 return Err(InherentError::IncorrectAllowlistUpdates);
822 }
823 } else {
824 return Err(InherentError::MissingAllowlistUpdates);
825 }
826
827 Ok(())
828 }
829
830 fn is_inherent(call: &Self::Call) -> bool {
831 matches!(call, Call::update_domain_allowlist { .. })
832 }
833 }
834
835 impl<T: Config> Sender<T::AccountId> for Pallet<T> {
836 type MessageId = MessageId;
837
838 fn send_message(
839 sender: &T::AccountId,
840 dst_chain_id: ChainId,
841 req: EndpointRequest,
842 ) -> Result<Self::MessageId, DispatchError> {
843 let allowed_chains = ChainAllowlist::<T>::get();
844 ensure!(
845 allowed_chains.contains(&dst_chain_id),
846 Error::<T>::ChainNotAllowed
847 );
848
849 let channel_id =
850 Self::get_open_channel_for_chain(dst_chain_id).ok_or(Error::<T>::NoOpenChannel)?;
851
852 let src_endpoint = req.src_endpoint.clone();
853
854 let message_id = {
855 let collected_fee = Self::collect_fees_for_message_v1(sender, &src_endpoint)?;
857 let src_chain_fee = collected_fee.src_chain_fee;
858 let dst_chain_fee = collected_fee.dst_chain_fee;
859 let nonce = Self::new_outbox_message(
860 T::SelfChainId::get(),
861 dst_chain_id,
862 channel_id,
863 VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(
864 EndpointRequestWithCollectedFee { req, collected_fee },
865 ))),
866 )?;
867
868 let message_id = (channel_id, nonce);
870 Self::store_outbox_fee(dst_chain_id, message_id, src_chain_fee, dst_chain_fee)?;
871 message_id
872 };
873
874 Ok(message_id)
875 }
876
877 #[cfg(feature = "runtime-benchmarks")]
880 fn unchecked_open_channel(dst_chain_id: ChainId) -> Result<(), DispatchError> {
881 let init_params = ChannelOpenParamsV1 {
882 max_outgoing_messages: 100,
883 };
884 ChainAllowlist::<T>::mutate(|list| list.insert(dst_chain_id));
885 let channel_id =
886 Self::do_init_channel(dst_chain_id, init_params, None, true, Zero::zero())?;
887 Self::do_open_channel(dst_chain_id, channel_id)?;
888 Ok(())
889 }
890 }
891
892 impl<T: Config> Pallet<T> {
893 fn message_weight(weight_tag: &MessageWeightTag) -> Weight {
895 match weight_tag {
896 MessageWeightTag::ProtocolChannelOpen => T::WeightInfo::do_open_channel(),
897 MessageWeightTag::ProtocolChannelClose => T::WeightInfo::do_close_channel(),
898 MessageWeightTag::EndpointRequest(endpoint) => {
899 T::get_endpoint_handler(endpoint)
900 .map(|endpoint_handler| endpoint_handler.message_weight())
901 .unwrap_or(Weight::zero())
903 }
904 MessageWeightTag::EndpointResponse(endpoint) => {
905 T::get_endpoint_handler(endpoint)
906 .map(|endpoint_handler| endpoint_handler.message_response_weight())
907 .unwrap_or(Weight::zero())
909 }
910 MessageWeightTag::None => Weight::zero(),
911 }
912 }
913
914 pub fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option<ChannelId> {
916 let mut next_channel_id = NextChannelId::<T>::get(dst_chain_id);
917
918 while let Some(channel_id) = next_channel_id.checked_sub(ChannelId::one()) {
921 let message_count = OutboxMessageCount::<T>::get((dst_chain_id, channel_id));
922 if let Some(channel) = Channels::<T>::get(dst_chain_id, channel_id)
923 && channel.state == ChannelState::Open
924 && message_count < channel.max_outgoing_messages
925 {
926 return Some(channel_id);
927 }
928
929 next_channel_id = channel_id
930 }
931
932 None
933 }
934
935 pub(crate) fn do_open_channel(chain_id: ChainId, channel_id: ChannelId) -> DispatchResult {
937 Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
938 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
939
940 ensure!(
941 channel.state == ChannelState::Initiated,
942 Error::<T>::InvalidChannelState
943 );
944
945 channel.state = ChannelState::Open;
946 Ok(())
947 })?;
948
949 Self::deposit_event(Event::ChannelOpen {
950 chain_id,
951 channel_id,
952 });
953
954 Ok(())
955 }
956
957 pub(crate) fn do_close_channel(
958 chain_id: ChainId,
959 channel_id: ChannelId,
960 close_channel_by: CloseChannelBy<T::AccountId>,
961 ) -> DispatchResult {
962 Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
963 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
964
965 ensure!(
966 channel.state != ChannelState::Closed,
967 Error::<T>::InvalidChannelState
968 );
969
970 if let CloseChannelBy::Owner(owner) = close_channel_by {
971 ensure!(channel.maybe_owner == Some(owner), Error::<T>::ChannelOwner);
972 }
973
974 if let Some(owner) = &channel.maybe_owner {
975 let hold_id = T::HoldIdentifier::messenger_channel();
976 let locked_amount = channel.channel_reserve_fee;
977 let (amount_to_release, maybe_amount_to_burn) = {
978 if channel.state == ChannelState::Open {
979 (locked_amount, None)
980 } else {
981 let protocol_fee = T::ChannelInitReservePortion::get() * locked_amount;
982 let release_amount = locked_amount.saturating_sub(protocol_fee);
983 (release_amount, Some(protocol_fee))
984 }
985 };
986
987 with_storage_layer(|| {
988 if let Some(protocol_fee) = maybe_amount_to_burn {
989 T::Currency::burn_held(
990 &hold_id,
991 owner,
992 protocol_fee,
993 Precision::Exact,
994 Fortitude::Force,
995 )?;
996 T::OnXDMRewards::on_chain_protocol_fees(chain_id, protocol_fee);
997 }
998
999 T::Currency::release(&hold_id, owner, amount_to_release, Precision::Exact)
1000 .map_err(|_| Error::<T>::BalanceUnlock)?;
1001
1002 Ok::<(), DispatchError>(())
1003 })?;
1004 }
1005
1006 channel.state = ChannelState::Closed;
1007 Ok(())
1008 })?;
1009
1010 Self::deposit_event(Event::ChannelClosed {
1011 chain_id,
1012 channel_id,
1013 });
1014
1015 Ok(())
1016 }
1017
1018 pub(crate) fn do_init_channel(
1019 dst_chain_id: ChainId,
1020 init_params: ChannelOpenParamsV1,
1021 maybe_owner: Option<T::AccountId>,
1022 check_allowlist: bool,
1023 channel_reserve_fee: BalanceOf<T>,
1024 ) -> Result<ChannelId, DispatchError> {
1025 ensure!(
1026 T::SelfChainId::get() != dst_chain_id,
1027 Error::<T>::InvalidChain,
1028 );
1029
1030 ensure!(
1032 init_params.max_outgoing_messages >= 1u32,
1033 Error::<T>::InvalidMaxOutgoingMessages
1034 );
1035
1036 ensure!(
1039 maybe_owner.is_none() || !channel_reserve_fee.is_zero(),
1040 Error::<T>::InvalidChannelReserveFee,
1041 );
1042
1043 if check_allowlist {
1044 let chain_allowlist = ChainAllowlist::<T>::get();
1045 ensure!(
1046 chain_allowlist.contains(&dst_chain_id),
1047 Error::<T>::ChainNotAllowed
1048 );
1049 }
1050
1051 let channel_id = NextChannelId::<T>::get(dst_chain_id);
1052 let next_channel_id = channel_id
1053 .checked_add(U256::one())
1054 .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1055
1056 Channels::<T>::insert(
1057 dst_chain_id,
1058 channel_id,
1059 Channel {
1060 channel_id,
1061 state: ChannelState::Initiated,
1062 next_inbox_nonce: Default::default(),
1063 next_outbox_nonce: Default::default(),
1064 latest_response_received_message_nonce: Default::default(),
1065 max_outgoing_messages: init_params.max_outgoing_messages,
1066 maybe_owner,
1067 channel_reserve_fee,
1068 },
1069 );
1070
1071 NextChannelId::<T>::insert(dst_chain_id, next_channel_id);
1072 Self::deposit_event(Event::ChannelInitiated {
1073 chain_id: dst_chain_id,
1074 channel_id,
1075 });
1076 Ok(channel_id)
1077 }
1078
1079 pub fn validate_relay_message(
1080 xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1081 consensus_state_root: StateRootOf<T>,
1082 ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1083 let (next_nonce, maybe_channel) =
1084 match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1085 None => {
1086 log::debug!(
1089 "Initiating new channel: {:?} to chain: {:?}",
1090 xdm.channel_id,
1091 xdm.src_chain_id
1092 );
1093 (Nonce::zero(), None)
1094 }
1095 Some(channel) => {
1096 log::debug!(
1097 "Message to channel: {:?} from chain: {:?}",
1098 xdm.channel_id,
1099 xdm.src_chain_id
1100 );
1101 (channel.next_inbox_nonce, Some(channel))
1102 }
1103 };
1104
1105 let key = StorageKey(
1107 T::StorageKeys::outbox_storage_key(
1108 xdm.src_chain_id,
1109 (T::SelfChainId::get(), xdm.channel_id, xdm.nonce),
1110 )
1111 .ok_or(UnknownTransaction::CannotLookup)?,
1112 );
1113
1114 let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1116
1117 let is_valid_call = match &msg.payload {
1118 VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(req))) => {
1119 match req {
1120 ProtocolMessageRequest::ChannelOpen(_) => maybe_channel.is_none(),
1122 ProtocolMessageRequest::ChannelClose => {
1124 if let Some(ref channel) = maybe_channel {
1125 !(channel.state == ChannelState::Closed)
1126 } else {
1127 false
1128 }
1129 }
1130 }
1131 }
1132 VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(_))) => {
1138 if let Some(ref channel) = maybe_channel {
1139 !(channel.state == ChannelState::Initiated)
1140 } else {
1141 false
1142 }
1143 }
1144 _ => false,
1146 };
1147
1148 if !is_valid_call {
1149 log::error!("Unexpected XDM message: {:?}", msg,);
1150 return Err(InvalidTransaction::Call.into());
1151 }
1152
1153 if msg.nonce.cmp(&next_nonce) == Ordering::Less {
1155 return Err(InvalidTransaction::Stale.into());
1156 }
1157
1158 let validated_relay_msg = ValidatedRelayMessage {
1159 message: msg,
1160 should_init_channel: maybe_channel.is_none(),
1161 next_nonce,
1162 };
1163
1164 Ok(validated_relay_msg)
1165 }
1166
1167 pub(crate) fn pre_dispatch_relay_message(
1168 msg: Message<BalanceOf<T>>,
1169 should_init_channel: bool,
1170 ) -> Result<(), TransactionValidityError> {
1171 if should_init_channel {
1172 if let VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
1173 ProtocolMessageRequest::ChannelOpen(params),
1174 ))) = msg.payload
1175 {
1176 Self::do_init_channel(msg.src_chain_id, params, None, false, Zero::zero())
1180 .map_err(|err| {
1181 log::error!(
1182 "Error initiating channel: {:?} with chain: {:?}: {:?}",
1183 msg.channel_id,
1184 msg.src_chain_id,
1185 err
1186 );
1187 InvalidTransaction::Call
1188 })?;
1189 } else {
1190 log::error!("Unexpected call instead of channel open request: {:?}", msg,);
1191 return Err(InvalidTransaction::Call.into());
1192 }
1193 }
1194
1195 let (dst_chain_id, channel_id, nonce) = (msg.src_chain_id, msg.channel_id, msg.nonce);
1196 Channels::<T>::mutate(
1199 dst_chain_id,
1200 channel_id,
1201 |maybe_channel| -> sp_runtime::DispatchResult {
1202 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
1203 channel.next_inbox_nonce = nonce
1204 .checked_add(Nonce::one())
1205 .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1206 Ok(())
1207 },
1208 )
1209 .map_err(|err| {
1210 log::error!(
1211 "Failed to increment the next relay message nonce for Chain[{:?}] with Channel[{:?}]: {:?}",
1212 dst_chain_id,
1213 channel_id,
1214 err,
1215 );
1216 InvalidTransaction::Custom(crate::verification_errors::NEXT_NONCE_UPDATE)
1217 })?;
1218
1219 Self::deposit_event(Event::InboxMessage {
1220 chain_id: msg.src_chain_id,
1221 channel_id: msg.channel_id,
1222 nonce: msg.nonce,
1223 });
1224 Inbox::<T>::put(msg);
1225 Ok(())
1226 }
1227
1228 pub fn validate_relay_message_response(
1229 xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1230 consensus_state_root: StateRootOf<T>,
1231 ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1232 let next_nonce =
1234 match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1235 None => {
1237 log::error!("Unexpected inbox message response: {:?}", xdm,);
1238 return Err(InvalidTransaction::Call.into());
1239 }
1240 Some(channel) => match channel.latest_response_received_message_nonce {
1241 None => Nonce::zero(),
1242 Some(last_nonce) => last_nonce.checked_add(Nonce::one()).ok_or(
1243 InvalidTransaction::Custom(crate::verification_errors::NONCE_OVERFLOW),
1244 )?,
1245 },
1246 };
1247
1248 let key = StorageKey(
1250 T::StorageKeys::inbox_responses_storage_key(
1251 xdm.src_chain_id,
1252 (T::SelfChainId::get(), xdm.channel_id, xdm.nonce),
1253 )
1254 .ok_or(UnknownTransaction::CannotLookup)?,
1255 );
1256
1257 let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1259
1260 if msg.nonce.cmp(&next_nonce) == Ordering::Less {
1262 return Err(InvalidTransaction::Stale.into());
1263 }
1264
1265 let validated_relay_msg = ValidatedRelayMessage {
1266 message: msg,
1267 next_nonce,
1268 should_init_channel: false,
1270 };
1271
1272 Ok(validated_relay_msg)
1273 }
1274
1275 pub(crate) fn pre_dispatch_relay_message_response(
1276 msg: Message<BalanceOf<T>>,
1277 ) -> Result<(), TransactionValidityError> {
1278 let (dst_chain_id, channel_id, nonce) = (msg.src_chain_id, msg.channel_id, msg.nonce);
1281 Channels::<T>::mutate(
1282 dst_chain_id,
1283 channel_id,
1284 |maybe_channel| -> sp_runtime::DispatchResult {
1285 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
1286 channel.latest_response_received_message_nonce = Some(nonce);
1287 Ok(())
1288 },
1289 )
1290 .map_err(|err| {
1291 log::error!(
1292 "Failed to increment the next relay message response nonce for Chain[{:?}] with Channel[{:?}]: {:?}",
1293 dst_chain_id,
1294 channel_id,
1295 err,
1296 );
1297 InvalidTransaction::Custom(crate::verification_errors::NEXT_NONCE_UPDATE)
1298 })?;
1299
1300 Self::deposit_event(Event::OutboxMessageResponse {
1301 chain_id: msg.src_chain_id,
1302 channel_id: msg.channel_id,
1303 nonce: msg.nonce,
1304 });
1305
1306 OutboxResponses::<T>::put(msg);
1307 Ok(())
1308 }
1309
1310 pub(crate) fn do_verify_xdm(
1311 next_nonce: Nonce,
1312 storage_key: StorageKey,
1313 consensus_state_root: StateRootOf<T>,
1314 xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1315 ) -> Result<Message<BalanceOf<T>>, TransactionValidityError> {
1316 let next_channel_id = NextChannelId::<T>::get(xdm.src_chain_id);
1318 ensure!(
1319 xdm.channel_id <= next_channel_id,
1320 InvalidTransaction::Custom(crate::verification_errors::INVALID_CHANNEL)
1321 );
1322
1323 ensure!(
1326 xdm.nonce >= next_nonce,
1327 InvalidTransaction::Custom(crate::verification_errors::INVALID_NONCE)
1328 );
1329
1330 let state_root = if let Some(domain_proof) = xdm.proof.domain_proof().clone()
1332 && let Some(domain_id) = xdm.src_chain_id.maybe_domain_chain()
1333 {
1334 let confirmed_domain_block_storage_key =
1335 T::StorageKeys::confirmed_domain_block_storage_key(domain_id)
1336 .ok_or(UnknownTransaction::CannotLookup)?;
1337
1338 StorageProofVerifier::<T::Hashing>::get_decoded_value::<
1339 sp_domains::ExecutionReceipt<
1340 BlockNumberFor<T>,
1341 T::Hash,
1342 BlockNumberFor<T>,
1343 T::Hash,
1344 BalanceOf<T>,
1345 >,
1346 >(
1347 &consensus_state_root,
1348 domain_proof,
1349 StorageKey(confirmed_domain_block_storage_key),
1350 )
1351 .map_err(|err| {
1352 log::error!(
1353 "Failed to verify storage proof for confirmed Domain block: {:?}",
1354 err
1355 );
1356 TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1357 })?
1358 .final_state_root
1359 } else {
1360 consensus_state_root
1361 };
1362
1363 let msg =
1365 StorageProofVerifier::<T::Hashing>::get_decoded_value::<Message<BalanceOf<T>>>(
1366 &state_root,
1367 xdm.proof.message_proof(),
1368 storage_key,
1369 )
1370 .map_err(|err| {
1371 log::error!("Failed to verify storage proof for message: {:?}", err);
1372 TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1373 })?;
1374
1375 Ok(msg)
1376 }
1377
1378 pub fn outbox_storage_key(message_key: MessageKey) -> Vec<u8> {
1379 Outbox::<T>::hashed_key_for(message_key)
1380 }
1381
1382 pub fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8> {
1383 InboxResponses::<T>::hashed_key_for(message_key)
1384 }
1385
1386 pub fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8> {
1387 Channels::<T>::hashed_key_for(chain_id, channel_id)
1388 }
1389
1390 pub fn domain_chains_allowlist_update(
1391 domain_id: DomainId,
1392 ) -> Option<DomainAllowlistUpdates> {
1393 DomainChainAllowlistUpdate::<T>::get(domain_id).filter(|updates| !updates.is_empty())
1394 }
1395
1396 pub fn domain_allow_list_update_storage_key(domain_id: DomainId) -> Vec<u8> {
1397 DomainChainAllowlistUpdate::<T>::hashed_key_for(domain_id)
1398 }
1399
1400 pub fn updated_channels() -> BTreeSet<(ChainId, ChannelId)> {
1401 UpdatedChannels::<T>::get()
1402 }
1403
1404 pub fn open_channels() -> BTreeSet<(ChainId, ChannelId)> {
1405 Channels::<T>::iter_keys().collect()
1406 }
1407
1408 pub fn channels_and_states() -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)> {
1409 crate::migrations::get_channels_and_states::<T>()
1410 }
1411
1412 pub fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce> {
1413 crate::migrations::get_channel::<T>(chain_id, channel_id).map(|channel| {
1414 let last_inbox_nonce = channel.next_inbox_nonce.checked_sub(U256::one());
1415 ChannelNonce {
1416 relay_msg_nonce: last_inbox_nonce,
1417 relay_response_msg_nonce: channel.latest_response_received_message_nonce,
1418 }
1419 })
1420 }
1421
1422 pub fn store_inbox_fee(
1423 src_chain_id: ChainId,
1424 message_id: MessageId,
1425 inbox_fees: BalanceOf<T>,
1426 ) -> DispatchResult {
1427 if !InboxFeesOnHoldStartAt::<T>::contains_key(message_id.0) {
1428 InboxFeesOnHoldStartAt::<T>::insert(message_id.0, message_id.1);
1429 }
1430 InboxFeesOnHold::<T>::mutate(|inbox_fees_on_hold| {
1431 *inbox_fees_on_hold = inbox_fees_on_hold
1432 .checked_add(&inbox_fees)
1433 .ok_or(Error::<T>::BalanceOverflow)?;
1434
1435 let imbalance = T::Currency::issue(inbox_fees);
1438 core::mem::forget(imbalance);
1439
1440 Ok::<(), Error<T>>(())
1441 })?;
1442
1443 InboxFee::<T>::insert((src_chain_id, message_id), inbox_fees);
1444
1445 if !T::NoteChainTransfer::note_transfer_in(inbox_fees, src_chain_id) {
1447 return Err(Error::<T>::FailedToNoteTransferIn.into());
1448 }
1449
1450 Ok(())
1451 }
1452
1453 pub fn store_outbox_fee(
1454 dst_chain_id: ChainId,
1455 message_id: MessageId,
1456 outbox_fees: BalanceOf<T>,
1457 inbox_fees: BalanceOf<T>,
1458 ) -> DispatchResult {
1459 if !OutboxFeesOnHoldStartAt::<T>::contains_key(message_id.0) {
1460 OutboxFeesOnHoldStartAt::<T>::insert(message_id.0, message_id.1);
1461 }
1462 OutboxFeesOnHold::<T>::mutate(|outbox_fees_on_hold| {
1463 *outbox_fees_on_hold = outbox_fees_on_hold
1464 .checked_add(&outbox_fees)
1465 .ok_or(Error::<T>::BalanceOverflow)?;
1466
1467 let imbalance = T::Currency::issue(outbox_fees);
1470 core::mem::forget(imbalance);
1471
1472 Ok::<(), Error<T>>(())
1473 })?;
1474
1475 OutboxFee::<T>::insert((dst_chain_id, message_id), outbox_fees);
1476
1477 if !T::NoteChainTransfer::note_transfer_out(inbox_fees, dst_chain_id) {
1479 return Err(Error::<T>::FailedToNoteTransferOut.into());
1480 }
1481
1482 Ok(())
1483 }
1484 }
1485}
1486
1487impl<T> Pallet<T>
1488where
1489 T: Config + CreateUnsigned<Call<T>>,
1490{
1491 pub fn outbox_message_unsigned(
1492 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1493 ) -> Option<T::Extrinsic> {
1494 let call = Call::relay_message { msg };
1495 Some(T::create_unsigned(call.into()))
1496 }
1497
1498 pub fn inbox_response_message_unsigned(
1499 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1500 ) -> Option<T::Extrinsic> {
1501 let call = Call::relay_message_response { msg };
1502 Some(T::create_unsigned(call.into()))
1503 }
1504
1505 pub fn first_outbox_message_nonce_to_relay(
1507 dst_chain_id: ChainId,
1508 channel_id: ChannelId,
1509 from_nonce: Nonce,
1510 ) -> Option<Nonce> {
1511 Self::first_relay_message(
1512 dst_chain_id,
1513 channel_id,
1514 from_nonce,
1515 Outbox::<T>::contains_key,
1516 )
1517 }
1518
1519 pub fn first_inbox_message_response_nonce_to_relay(
1521 dst_chain_id: ChainId,
1522 channel_id: ChannelId,
1523 from_nonce: Nonce,
1524 ) -> Option<Nonce> {
1525 Self::first_relay_message(
1526 dst_chain_id,
1527 channel_id,
1528 from_nonce,
1529 InboxResponses::<T>::contains_key,
1530 )
1531 }
1532
1533 fn first_relay_message<Check>(
1534 dst_chain_id: ChainId,
1535 channel_id: ChannelId,
1536 from_nonce: Nonce,
1537 check: Check,
1538 ) -> Option<Nonce>
1539 where
1540 Check: Fn((ChainId, ChannelId, Nonce)) -> bool,
1541 {
1542 let mut nonce = from_nonce;
1543 let to_nonce = from_nonce.saturating_add(MAX_FUTURE_ALLOWED_NONCES.into());
1544 while nonce <= to_nonce {
1545 if check((dst_chain_id, channel_id, nonce)) {
1546 return Some(nonce);
1547 }
1548
1549 nonce = nonce.saturating_add(Nonce::one())
1550 }
1551
1552 None
1553 }
1554}
1555
1556impl<T: Config> sp_domains::DomainBundleSubmitted for Pallet<T> {
1557 fn domain_bundle_submitted(domain_id: DomainId) {
1558 DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_updates| {
1562 if let Some(updates) = maybe_updates {
1563 updates.clear();
1564 }
1565 });
1566 }
1567}
1568
1569impl<T: Config> sp_domains::OnDomainInstantiated for Pallet<T> {
1570 fn on_domain_instantiated(domain_id: DomainId) {
1571 DomainChainAllowlistUpdate::<T>::insert(domain_id, DomainAllowlistUpdates::default());
1572 }
1573}