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