1#![cfg_attr(not(feature = "std"), no_std)]
19#![forbid(unsafe_code)]
20#![warn(rust_2018_idioms)]
21#![feature(let_chains, variant_count, if_let_guard)]
22
23#[cfg(feature = "runtime-benchmarks")]
24mod benchmarking;
25pub mod extensions;
26mod fees;
27mod messages;
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::messages::{
48 ChainId, Channel, ChannelId, ChannelState, CrossDomainMessage, Message, Nonce,
49};
50use sp_messenger::MAX_FUTURE_ALLOWED_NONCES;
51use sp_runtime::traits::Hash;
52use sp_runtime::DispatchError;
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, StateRootOf, ValidatedRelayMessage,
154 STORAGE_VERSION, U256,
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, InherentError, InherentType, NoteChainTransfer,
183 OnXDMRewards, StorageKeys, INHERENT_IDENTIFIER,
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<
221 Self::MmrHash,
222 BlockNumberFor<Self>,
223 StateRootOf<Self>,
224 >;
225 type StorageKeys: StorageKeys;
227 type DomainOwner: DomainOwner<Self::AccountId>;
229 type HoldIdentifier: HoldIdentifier<Self>;
231 #[pallet::constant]
233 type ChannelReserveFee: Get<BalanceOf<Self>>;
234 #[pallet::constant]
237 type ChannelInitReservePortion: Get<Perbill>;
238 type DomainRegistration: DomainRegistration;
240 #[pallet::constant]
242 type MaxOutgoingMessages: Get<u32>;
243 type MessengerOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ()>;
245 type NoteChainTransfer: NoteChainTransfer<BalanceOf<Self>>;
247 type ExtensionWeightInfo: ExtensionWeightInfo;
249 }
250
251 #[pallet::pallet]
253 #[pallet::without_storage_info]
254 #[pallet::storage_version(STORAGE_VERSION)]
255 pub struct Pallet<T>(_);
256
257 #[pallet::storage]
259 #[pallet::getter(fn next_channel_id)]
260 pub(super) type NextChannelId<T: Config> =
261 StorageMap<_, Identity, ChainId, ChannelId, ValueQuery>;
262
263 #[pallet::storage]
266 #[pallet::getter(fn channels)]
267 pub(super) type Channels<T: Config> = StorageDoubleMap<
268 _,
269 Identity,
270 ChainId,
271 Identity,
272 ChannelId,
273 Channel<BalanceOf<T>, T::AccountId>,
274 OptionQuery,
275 >;
276
277 #[pallet::storage]
280 #[pallet::getter(fn inbox)]
281 pub(super) type Inbox<T: Config> = StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
282
283 #[pallet::storage]
287 #[pallet::getter(fn inbox_fees)]
288 pub(super) type InboxFee<T: Config> =
289 StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
290
291 #[pallet::storage]
294 #[pallet::getter(fn outbox_fees)]
295 pub(super) type OutboxFee<T: Config> =
296 StorageMap<_, Identity, (ChainId, MessageId), BalanceOf<T>, OptionQuery>;
297
298 #[pallet::storage]
301 #[pallet::getter(fn inbox_responses)]
302 pub(super) type InboxResponses<T: Config> =
303 StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
304
305 #[pallet::storage]
308 #[pallet::getter(fn outbox)]
309 pub(super) type Outbox<T: Config> =
310 StorageMap<_, Identity, (ChainId, ChannelId, Nonce), Message<BalanceOf<T>>, OptionQuery>;
311
312 #[pallet::storage]
314 #[pallet::getter(fn outbox_message_count)]
315 pub(super) type OutboxMessageCount<T: Config> =
316 StorageMap<_, Identity, (ChainId, ChannelId), u32, ValueQuery>;
317
318 #[pallet::storage]
321 #[pallet::getter(fn outbox_responses)]
322 pub(super) type OutboxResponses<T: Config> =
323 StorageValue<_, Message<BalanceOf<T>>, OptionQuery>;
324
325 #[pallet::storage]
327 #[pallet::getter(fn outbox_message_weight_tags)]
328 pub(super) type OutboxMessageWeightTags<T: Config> =
329 StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
330
331 #[pallet::storage]
333 #[pallet::getter(fn inbox_response_message_weight_tags)]
334 pub(super) type InboxResponseMessageWeightTags<T: Config> =
335 StorageMap<_, Identity, (ChainId, MessageId), MessageWeightTag>;
336
337 #[pallet::storage]
339 #[pallet::getter(fn chain_allowlist)]
340 pub(super) type ChainAllowlist<T: Config> = StorageValue<_, BTreeSet<ChainId>, ValueQuery>;
341
342 #[pallet::storage]
346 #[pallet::getter(fn domain_chain_allowlist_updates)]
347 pub(super) type DomainChainAllowlistUpdate<T: Config> =
348 StorageMap<_, Identity, DomainId, DomainAllowlistUpdates, OptionQuery>;
349
350 #[pallet::storage]
353 pub(super) type UpdatedChannels<T: Config> =
354 StorageValue<_, BTreeSet<(ChainId, ChannelId)>, ValueQuery>;
355
356 #[pallet::storage]
363 pub(super) type InboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
364
365 #[pallet::storage]
372 pub(super) type OutboxFeesOnHold<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
373
374 #[pallet::storage]
380 pub(super) type InboxFeesOnHoldStartAt<T: Config> =
381 StorageMap<_, Identity, ChannelId, Nonce, OptionQuery>;
382 #[pallet::storage]
383 pub(super) type OutboxFeesOnHoldStartAt<T: Config> =
384 StorageMap<_, Identity, ChannelId, Nonce, OptionQuery>;
385
386 #[pallet::origin]
387 pub type Origin = RawOrigin;
388
389 #[pallet::event]
391 #[pallet::generate_deposit(pub (super) fn deposit_event)]
392 pub enum Event<T: Config> {
393 ChannelInitiated {
395 chain_id: ChainId,
397 channel_id: ChannelId,
399 },
400
401 ChannelClosed {
403 chain_id: ChainId,
405 channel_id: ChannelId,
407 },
408
409 ChannelOpen {
411 chain_id: ChainId,
413 channel_id: ChannelId,
415 },
416
417 OutboxMessage {
419 chain_id: ChainId,
420 channel_id: ChannelId,
421 nonce: Nonce,
422 },
423
424 OutboxMessageResponse {
426 chain_id: ChainId,
428 channel_id: ChannelId,
430 nonce: Nonce,
431 },
432
433 OutboxMessageResult {
435 chain_id: ChainId,
436 channel_id: ChannelId,
437 nonce: Nonce,
438 result: OutboxMessageResult,
439 },
440
441 InboxMessage {
443 chain_id: ChainId,
444 channel_id: ChannelId,
445 nonce: Nonce,
446 },
447
448 InboxMessageResponse {
450 chain_id: ChainId,
452 channel_id: ChannelId,
454 nonce: Nonce,
455 },
456 }
457
458 #[pallet::hooks]
459 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
460 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
461 UpdatedChannels::<T>::take();
462 T::DbWeight::get().reads_writes(0, 1)
463 }
464 }
465
466 #[pallet::validate_unsigned]
467 impl<T: Config> ValidateUnsigned for Pallet<T> {
468 type Call = Call<T>;
469
470 fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
471 match call {
472 Call::update_domain_allowlist { .. } => Ok(()),
474 _ => Err(InvalidTransaction::Call.into()),
475 }
476 }
477
478 fn validate_unsigned(
480 _source: TransactionSource,
481 _call: &Self::Call,
482 ) -> TransactionValidity {
483 InvalidTransaction::Call.into()
484 }
485 }
486
487 #[pallet::error]
489 pub enum Error<T> {
490 InvalidChain,
492
493 MissingChannel,
495
496 InvalidChannelState,
498
499 NoOpenChannel,
501
502 NoMessageHandler,
504
505 OutboxFull,
507
508 InvalidMessagePayload,
510
511 InvalidMessageDestination,
513
514 MessageVerification(VerificationError),
516
517 MissingMessage,
519
520 WeightTagNotMatch,
523
524 BalanceOverflow,
526
527 BalanceUnderflow,
529
530 InvalidAllowedChain,
532
533 OperationNotAllowed,
535
536 NotDomainOwner,
538
539 ChainNotAllowed,
541
542 InsufficientBalance,
544
545 BalanceHold,
547
548 ChannelOwner,
550
551 BalanceUnlock,
553
554 InvalidChannelReserveFee,
556
557 InvalidMaxOutgoingMessages,
559
560 MessageCountOverflow,
562
563 MessageCountUnderflow,
565
566 FailedToNoteTransferIn,
568
569 FailedToNoteTransferOut,
571 }
572
573 #[pallet::call]
574 impl<T: Config> Pallet<T> {
575 #[pallet::call_index(0)]
579 #[pallet::weight(T::WeightInfo::initiate_channel())]
580 pub fn initiate_channel(origin: OriginFor<T>, dst_chain_id: ChainId) -> DispatchResult {
581 let owner = ensure_signed(origin)?;
582
583 let hold_id = T::HoldIdentifier::messenger_channel();
585 let amount = T::ChannelReserveFee::get();
586
587 ensure!(
589 T::Currency::reducible_balance(&owner, Preservation::Preserve, Fortitude::Polite)
590 >= amount,
591 Error::<T>::InsufficientBalance
592 );
593 T::Currency::hold(&hold_id, &owner, amount).map_err(|_| Error::<T>::BalanceHold)?;
594
595 let channel_open_params = ChannelOpenParamsV1 {
597 max_outgoing_messages: T::MaxOutgoingMessages::get(),
598 };
599 let channel_id = Self::do_init_channel(
600 dst_chain_id,
601 channel_open_params,
602 Some(owner.clone()),
603 true,
604 amount,
605 )?;
606
607 let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
608 ProtocolMessageRequest::ChannelOpen(ChannelOpenParamsV1 {
609 max_outgoing_messages: channel_open_params.max_outgoing_messages,
610 }),
611 )));
612
613 Self::new_outbox_message(T::SelfChainId::get(), dst_chain_id, channel_id, payload)?;
615
616 Ok(())
617 }
618
619 #[pallet::call_index(1)]
622 #[pallet::weight(T::WeightInfo::close_channel())]
623 pub fn close_channel(
624 origin: OriginFor<T>,
625 chain_id: ChainId,
626 channel_id: ChannelId,
627 ) -> DispatchResult {
628 let close_channel_by = match ensure_signed_or_root(origin)? {
631 Some(owner) => CloseChannelBy::Owner(owner),
632 None => CloseChannelBy::Sudo,
633 };
634 Self::do_close_channel(chain_id, channel_id, close_channel_by)?;
635
636 let payload = VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
637 ProtocolMessageRequest::ChannelClose,
638 )));
639
640 Self::new_outbox_message(T::SelfChainId::get(), chain_id, channel_id, payload)?;
641
642 Ok(())
643 }
644
645 #[pallet::call_index(2)]
647 #[pallet::weight(T::WeightInfo::relay_message().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
648 pub fn relay_message(
649 origin: OriginFor<T>,
650 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
651 ) -> DispatchResult {
652 T::MessengerOrigin::ensure_origin(origin)?;
653 let inbox_msg = Inbox::<T>::take().ok_or(Error::<T>::MissingMessage)?;
654 Self::process_inbox_messages(inbox_msg, msg.weight_tag)?;
655 Ok(())
656 }
657
658 #[pallet::call_index(3)]
660 #[pallet::weight(T::WeightInfo::relay_message_response().saturating_add(Pallet::< T >::message_weight(& msg.weight_tag)))]
661 pub fn relay_message_response(
662 origin: OriginFor<T>,
663 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
664 ) -> DispatchResult {
665 T::MessengerOrigin::ensure_origin(origin)?;
666 let outbox_resp_msg = OutboxResponses::<T>::take().ok_or(Error::<T>::MissingMessage)?;
667 Self::process_outbox_message_responses(outbox_resp_msg, msg.weight_tag)?;
668 Ok(())
669 }
670
671 #[pallet::call_index(4)]
673 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
674 pub fn update_consensus_chain_allowlist(
675 origin: OriginFor<T>,
676 update: ChainAllowlistUpdate,
677 ) -> DispatchResult {
678 ensure_root(origin)?;
679 ensure!(
680 T::SelfChainId::get().is_consensus_chain(),
681 Error::<T>::OperationNotAllowed
682 );
683
684 ensure!(
685 update.chain_id() != T::SelfChainId::get(),
686 Error::<T>::InvalidAllowedChain
687 );
688
689 if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
690 ensure!(
691 T::DomainRegistration::is_domain_registered(domain_id),
692 Error::<T>::InvalidChain
693 );
694 }
695
696 ChainAllowlist::<T>::mutate(|list| match update {
697 ChainAllowlistUpdate::Add(chain_id) => list.insert(chain_id),
698 ChainAllowlistUpdate::Remove(chain_id) => list.remove(&chain_id),
699 });
700 Ok(())
701 }
702
703 #[pallet::call_index(5)]
705 #[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(1, 1))]
706 pub fn initiate_domain_update_chain_allowlist(
707 origin: OriginFor<T>,
708 domain_id: DomainId,
709 update: ChainAllowlistUpdate,
710 ) -> DispatchResult {
711 let domain_owner = ensure_signed(origin)?;
712 ensure!(
713 T::DomainOwner::is_domain_owner(domain_id, domain_owner),
714 Error::<T>::NotDomainOwner
715 );
716
717 ensure!(
718 T::SelfChainId::get().is_consensus_chain(),
719 Error::<T>::OperationNotAllowed
720 );
721
722 if let Some(dst_domain_id) = update.chain_id().maybe_domain_chain() {
723 ensure!(dst_domain_id != domain_id, Error::<T>::InvalidAllowedChain);
724 }
725
726 if let ChainAllowlistUpdate::Add(ChainId::Domain(domain_id)) = update {
727 ensure!(
728 T::DomainRegistration::is_domain_registered(domain_id),
729 Error::<T>::InvalidChain
730 );
731 }
732
733 DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_domain_updates| {
734 let mut domain_updates = maybe_domain_updates.take().unwrap_or_default();
735 match update {
736 ChainAllowlistUpdate::Add(chain_id) => {
737 domain_updates.remove_chains.remove(&chain_id);
738 domain_updates.allow_chains.insert(chain_id);
739 }
740 ChainAllowlistUpdate::Remove(chain_id) => {
741 domain_updates.allow_chains.remove(&chain_id);
742 domain_updates.remove_chains.insert(chain_id);
743 }
744 }
745
746 *maybe_domain_updates = Some(domain_updates)
747 });
748 Ok(())
749 }
750
751 #[pallet::call_index(6)]
753 #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Mandatory))]
754 pub fn update_domain_allowlist(
755 origin: OriginFor<T>,
756 updates: DomainAllowlistUpdates,
757 ) -> DispatchResult {
758 ensure_none(origin)?;
759 ensure!(
760 !T::SelfChainId::get().is_consensus_chain(),
761 Error::<T>::OperationNotAllowed
762 );
763
764 let DomainAllowlistUpdates {
765 allow_chains,
766 remove_chains,
767 } = updates;
768
769 ChainAllowlist::<T>::mutate(|list| {
770 remove_chains.into_iter().for_each(|chain_id| {
772 list.remove(&chain_id);
773 });
774
775 allow_chains.into_iter().for_each(|chain_id| {
777 list.insert(chain_id);
778 });
779 });
780
781 Ok(())
782 }
783 }
784
785 #[pallet::inherent]
786 impl<T: Config> ProvideInherent for Pallet<T> {
787 type Call = Call<T>;
788 type Error = InherentError;
789 const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
790
791 fn create_inherent(data: &InherentData) -> Option<Self::Call> {
792 let inherent_data = data
793 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
794 .expect("Messenger inherent data not correctly encoded")
795 .expect("Messenger inherent data must be provided");
796
797 inherent_data
798 .maybe_updates
799 .map(|updates| Call::update_domain_allowlist { updates })
800 }
801
802 fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
803 let inherent_data = data
804 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
805 .expect("Messenger inherent data not correctly encoded")
806 .expect("Messenger inherent data must be provided");
807
808 Ok(if inherent_data.maybe_updates.is_none() {
809 None
810 } else {
811 Some(InherentError::MissingAllowlistUpdates)
812 })
813 }
814
815 fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
816 let inherent_data = data
817 .get_data::<InherentType>(&INHERENT_IDENTIFIER)
818 .expect("Messenger inherent data not correctly encoded")
819 .expect("Messenger inherent data must be provided");
820
821 if let Some(provided_updates) = inherent_data.maybe_updates {
822 if let Call::update_domain_allowlist { updates } = call {
823 if updates != &provided_updates {
824 return Err(InherentError::IncorrectAllowlistUpdates);
825 }
826 }
827 } else {
828 return Err(InherentError::MissingAllowlistUpdates);
829 }
830
831 Ok(())
832 }
833
834 fn is_inherent(call: &Self::Call) -> bool {
835 matches!(call, Call::update_domain_allowlist { .. })
836 }
837 }
838
839 impl<T: Config> Sender<T::AccountId> for Pallet<T> {
840 type MessageId = MessageId;
841
842 fn send_message(
843 sender: &T::AccountId,
844 dst_chain_id: ChainId,
845 req: EndpointRequest,
846 ) -> Result<Self::MessageId, DispatchError> {
847 let allowed_chains = ChainAllowlist::<T>::get();
848 ensure!(
849 allowed_chains.contains(&dst_chain_id),
850 Error::<T>::ChainNotAllowed
851 );
852
853 let channel_id =
854 Self::get_open_channel_for_chain(dst_chain_id).ok_or(Error::<T>::NoOpenChannel)?;
855
856 let src_endpoint = req.src_endpoint.clone();
857
858 let message_id = {
859 let collected_fee = Self::collect_fees_for_message_v1(sender, &src_endpoint)?;
861 let src_chain_fee = collected_fee.src_chain_fee;
862 let dst_chain_fee = collected_fee.dst_chain_fee;
863 let nonce = Self::new_outbox_message(
864 T::SelfChainId::get(),
865 dst_chain_id,
866 channel_id,
867 VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(
868 EndpointRequestWithCollectedFee { req, collected_fee },
869 ))),
870 )?;
871
872 let message_id = (channel_id, nonce);
874 Self::store_outbox_fee(dst_chain_id, message_id, src_chain_fee, dst_chain_fee)?;
875 message_id
876 };
877
878 Ok(message_id)
879 }
880
881 #[cfg(feature = "runtime-benchmarks")]
884 fn unchecked_open_channel(dst_chain_id: ChainId) -> Result<(), DispatchError> {
885 let init_params = ChannelOpenParamsV1 {
886 max_outgoing_messages: 100,
887 };
888 ChainAllowlist::<T>::mutate(|list| list.insert(dst_chain_id));
889 let channel_id =
890 Self::do_init_channel(dst_chain_id, init_params, None, true, Zero::zero())?;
891 Self::do_open_channel(dst_chain_id, channel_id)?;
892 Ok(())
893 }
894 }
895
896 impl<T: Config> Pallet<T> {
897 fn message_weight(weight_tag: &MessageWeightTag) -> Weight {
899 match weight_tag {
900 MessageWeightTag::ProtocolChannelOpen => T::WeightInfo::do_open_channel(),
901 MessageWeightTag::ProtocolChannelClose => T::WeightInfo::do_close_channel(),
902 MessageWeightTag::EndpointRequest(endpoint) => {
903 T::get_endpoint_handler(endpoint)
904 .map(|endpoint_handler| endpoint_handler.message_weight())
905 .unwrap_or(Weight::zero())
907 }
908 MessageWeightTag::EndpointResponse(endpoint) => {
909 T::get_endpoint_handler(endpoint)
910 .map(|endpoint_handler| endpoint_handler.message_response_weight())
911 .unwrap_or(Weight::zero())
913 }
914 MessageWeightTag::None => Weight::zero(),
915 }
916 }
917
918 pub fn get_open_channel_for_chain(dst_chain_id: ChainId) -> Option<ChannelId> {
920 let mut next_channel_id = NextChannelId::<T>::get(dst_chain_id);
921
922 while let Some(channel_id) = next_channel_id.checked_sub(ChannelId::one()) {
925 let message_count = OutboxMessageCount::<T>::get((dst_chain_id, channel_id));
926 if let Some(channel) = Channels::<T>::get(dst_chain_id, channel_id) {
927 if channel.state == ChannelState::Open
928 && message_count < channel.max_outgoing_messages
929 {
930 return Some(channel_id);
931 }
932 }
933
934 next_channel_id = channel_id
935 }
936
937 None
938 }
939
940 pub(crate) fn do_open_channel(chain_id: ChainId, channel_id: ChannelId) -> DispatchResult {
942 Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
943 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
944
945 ensure!(
946 channel.state == ChannelState::Initiated,
947 Error::<T>::InvalidChannelState
948 );
949
950 channel.state = ChannelState::Open;
951 Ok(())
952 })?;
953
954 Self::deposit_event(Event::ChannelOpen {
955 chain_id,
956 channel_id,
957 });
958
959 Ok(())
960 }
961
962 pub(crate) fn do_close_channel(
963 chain_id: ChainId,
964 channel_id: ChannelId,
965 close_channel_by: CloseChannelBy<T::AccountId>,
966 ) -> DispatchResult {
967 Channels::<T>::try_mutate(chain_id, channel_id, |maybe_channel| -> DispatchResult {
968 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
969
970 ensure!(
971 channel.state != ChannelState::Closed,
972 Error::<T>::InvalidChannelState
973 );
974
975 if let CloseChannelBy::Owner(owner) = close_channel_by {
976 ensure!(channel.maybe_owner == Some(owner), Error::<T>::ChannelOwner);
977 }
978
979 if let Some(owner) = &channel.maybe_owner {
980 let hold_id = T::HoldIdentifier::messenger_channel();
981 let locked_amount = channel.channel_reserve_fee;
982 let (amount_to_release, maybe_amount_to_burn) = {
983 if channel.state == ChannelState::Open {
984 (locked_amount, None)
985 } else {
986 let protocol_fee = T::ChannelInitReservePortion::get() * locked_amount;
987 let release_amount = locked_amount.saturating_sub(protocol_fee);
988 (release_amount, Some(protocol_fee))
989 }
990 };
991
992 with_storage_layer(|| {
993 if let Some(protocol_fee) = maybe_amount_to_burn {
994 T::Currency::burn_held(
995 &hold_id,
996 owner,
997 protocol_fee,
998 Precision::Exact,
999 Fortitude::Force,
1000 )?;
1001 T::OnXDMRewards::on_chain_protocol_fees(chain_id, protocol_fee);
1002 }
1003
1004 T::Currency::release(&hold_id, owner, amount_to_release, Precision::Exact)
1005 .map_err(|_| Error::<T>::BalanceUnlock)?;
1006
1007 Ok::<(), DispatchError>(())
1008 })?;
1009 }
1010
1011 channel.state = ChannelState::Closed;
1012 Ok(())
1013 })?;
1014
1015 Self::deposit_event(Event::ChannelClosed {
1016 chain_id,
1017 channel_id,
1018 });
1019
1020 Ok(())
1021 }
1022
1023 pub(crate) fn do_init_channel(
1024 dst_chain_id: ChainId,
1025 init_params: ChannelOpenParamsV1,
1026 maybe_owner: Option<T::AccountId>,
1027 check_allowlist: bool,
1028 channel_reserve_fee: BalanceOf<T>,
1029 ) -> Result<ChannelId, DispatchError> {
1030 ensure!(
1031 T::SelfChainId::get() != dst_chain_id,
1032 Error::<T>::InvalidChain,
1033 );
1034
1035 ensure!(
1037 init_params.max_outgoing_messages >= 1u32,
1038 Error::<T>::InvalidMaxOutgoingMessages
1039 );
1040
1041 ensure!(
1044 maybe_owner.is_none() || !channel_reserve_fee.is_zero(),
1045 Error::<T>::InvalidChannelReserveFee,
1046 );
1047
1048 if check_allowlist {
1049 let chain_allowlist = ChainAllowlist::<T>::get();
1050 ensure!(
1051 chain_allowlist.contains(&dst_chain_id),
1052 Error::<T>::ChainNotAllowed
1053 );
1054 }
1055
1056 let channel_id = NextChannelId::<T>::get(dst_chain_id);
1057 let next_channel_id = channel_id
1058 .checked_add(U256::one())
1059 .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1060
1061 Channels::<T>::insert(
1062 dst_chain_id,
1063 channel_id,
1064 Channel {
1065 channel_id,
1066 state: ChannelState::Initiated,
1067 next_inbox_nonce: Default::default(),
1068 next_outbox_nonce: Default::default(),
1069 latest_response_received_message_nonce: Default::default(),
1070 max_outgoing_messages: init_params.max_outgoing_messages,
1071 maybe_owner,
1072 channel_reserve_fee,
1073 },
1074 );
1075
1076 NextChannelId::<T>::insert(dst_chain_id, next_channel_id);
1077 Self::deposit_event(Event::ChannelInitiated {
1078 chain_id: dst_chain_id,
1079 channel_id,
1080 });
1081 Ok(channel_id)
1082 }
1083
1084 pub fn validate_relay_message(
1085 xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1086 consensus_state_root: StateRootOf<T>,
1087 ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1088 let (next_nonce, maybe_channel) =
1089 match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1090 None => {
1091 log::debug!(
1094 "Initiating new channel: {:?} to chain: {:?}",
1095 xdm.channel_id,
1096 xdm.src_chain_id
1097 );
1098 (Nonce::zero(), None)
1099 }
1100 Some(channel) => {
1101 log::debug!(
1102 "Message to channel: {:?} from chain: {:?}",
1103 xdm.channel_id,
1104 xdm.src_chain_id
1105 );
1106 (channel.next_inbox_nonce, Some(channel))
1107 }
1108 };
1109
1110 let key = StorageKey(
1112 T::StorageKeys::outbox_storage_key(
1113 xdm.src_chain_id,
1114 (T::SelfChainId::get(), xdm.channel_id, xdm.nonce),
1115 )
1116 .ok_or(UnknownTransaction::CannotLookup)?,
1117 );
1118
1119 let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1121
1122 let is_valid_call = match &msg.payload {
1123 VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(req))) => {
1124 match req {
1125 ProtocolMessageRequest::ChannelOpen(_) => maybe_channel.is_none(),
1127 ProtocolMessageRequest::ChannelClose => {
1129 if let Some(ref channel) = maybe_channel {
1130 !(channel.state == ChannelState::Closed)
1131 } else {
1132 false
1133 }
1134 }
1135 }
1136 }
1137 VersionedPayload::V1(PayloadV1::Endpoint(RequestResponse::Request(_))) => {
1143 if let Some(ref channel) = maybe_channel {
1144 !(channel.state == ChannelState::Initiated)
1145 } else {
1146 false
1147 }
1148 }
1149 _ => false,
1151 };
1152
1153 if !is_valid_call {
1154 log::error!("Unexpected XDM message: {:?}", msg,);
1155 return Err(InvalidTransaction::Call.into());
1156 }
1157
1158 if msg.nonce.cmp(&next_nonce) == Ordering::Less {
1160 return Err(InvalidTransaction::Stale.into());
1161 }
1162
1163 let validated_relay_msg = ValidatedRelayMessage {
1164 message: msg,
1165 should_init_channel: maybe_channel.is_none(),
1166 next_nonce,
1167 };
1168
1169 Ok(validated_relay_msg)
1170 }
1171
1172 pub(crate) fn pre_dispatch_relay_message(
1173 msg: Message<BalanceOf<T>>,
1174 should_init_channel: bool,
1175 ) -> Result<(), TransactionValidityError> {
1176 if should_init_channel {
1177 if let VersionedPayload::V1(PayloadV1::Protocol(RequestResponse::Request(
1178 ProtocolMessageRequest::ChannelOpen(params),
1179 ))) = msg.payload
1180 {
1181 Self::do_init_channel(msg.src_chain_id, params, None, false, Zero::zero())
1185 .map_err(|err| {
1186 log::error!(
1187 "Error initiating channel: {:?} with chain: {:?}: {:?}",
1188 msg.channel_id,
1189 msg.src_chain_id,
1190 err
1191 );
1192 InvalidTransaction::Call
1193 })?;
1194 } else {
1195 log::error!("Unexpected call instead of channel open request: {:?}", msg,);
1196 return Err(InvalidTransaction::Call.into());
1197 }
1198 }
1199
1200 let (dst_chain_id, channel_id, nonce) = (msg.src_chain_id, msg.channel_id, msg.nonce);
1201 Channels::<T>::mutate(
1204 dst_chain_id,
1205 channel_id,
1206 |maybe_channel| -> sp_runtime::DispatchResult {
1207 let channel = maybe_channel.as_mut().ok_or(Error::<T>::MissingChannel)?;
1208 channel.next_inbox_nonce = nonce
1209 .checked_add(Nonce::one())
1210 .ok_or(DispatchError::Arithmetic(ArithmeticError::Overflow))?;
1211 Ok(())
1212 },
1213 )
1214 .map_err(|err| {
1215 log::error!(
1216 "Failed to increment the next relay message nonce for Chain[{:?}] with Channel[{:?}]: {:?}",
1217 dst_chain_id,
1218 channel_id,
1219 err,
1220 );
1221 InvalidTransaction::Custom(crate::verification_errors::NEXT_NONCE_UPDATE)
1222 })?;
1223
1224 Self::deposit_event(Event::InboxMessage {
1225 chain_id: msg.src_chain_id,
1226 channel_id: msg.channel_id,
1227 nonce: msg.nonce,
1228 });
1229 Inbox::<T>::put(msg);
1230 Ok(())
1231 }
1232
1233 pub fn validate_relay_message_response(
1234 xdm: &CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1235 consensus_state_root: StateRootOf<T>,
1236 ) -> Result<ValidatedRelayMessage<T>, TransactionValidityError> {
1237 let next_nonce =
1239 match Channels::<T>::get(xdm.src_chain_id, xdm.channel_id) {
1240 None => {
1242 log::error!("Unexpected inbox message response: {:?}", xdm,);
1243 return Err(InvalidTransaction::Call.into());
1244 }
1245 Some(channel) => match channel.latest_response_received_message_nonce {
1246 None => Nonce::zero(),
1247 Some(last_nonce) => last_nonce.checked_add(Nonce::one()).ok_or(
1248 InvalidTransaction::Custom(crate::verification_errors::NONCE_OVERFLOW),
1249 )?,
1250 },
1251 };
1252
1253 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 let msg = Self::do_verify_xdm(next_nonce, key, consensus_state_root, xdm)?;
1264
1265 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 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 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 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 ensure!(
1331 xdm.nonce >= next_nonce,
1332 InvalidTransaction::Custom(crate::verification_errors::INVALID_NONCE)
1333 );
1334
1335 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 target: "runtime::messenger",
1359 "Failed to verify storage proof for confirmed Domain block: {:?}",
1360 err
1361 );
1362 TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1363 })?
1364 .final_state_root
1365 } else {
1366 consensus_state_root
1367 };
1368
1369 let msg =
1371 StorageProofVerifier::<T::Hashing>::get_decoded_value::<Message<BalanceOf<T>>>(
1372 &state_root,
1373 xdm.proof.message_proof(),
1374 storage_key,
1375 )
1376 .map_err(|err| {
1377 log::error!(
1378 target: "runtime::messenger",
1379 "Failed to verify storage proof for message: {:?}",
1380 err
1381 );
1382 TransactionValidityError::Invalid(InvalidTransaction::BadProof)
1383 })?;
1384
1385 Ok(msg)
1386 }
1387
1388 pub fn outbox_storage_key(message_key: MessageKey) -> Vec<u8> {
1389 Outbox::<T>::hashed_key_for(message_key)
1390 }
1391
1392 pub fn inbox_response_storage_key(message_key: MessageKey) -> Vec<u8> {
1393 InboxResponses::<T>::hashed_key_for(message_key)
1394 }
1395
1396 pub fn channel_storage_key(chain_id: ChainId, channel_id: ChannelId) -> Vec<u8> {
1397 Channels::<T>::hashed_key_for(chain_id, channel_id)
1398 }
1399
1400 pub fn domain_chains_allowlist_update(
1401 domain_id: DomainId,
1402 ) -> Option<DomainAllowlistUpdates> {
1403 DomainChainAllowlistUpdate::<T>::get(domain_id).filter(|updates| !updates.is_empty())
1404 }
1405
1406 pub fn domain_allow_list_update_storage_key(domain_id: DomainId) -> Vec<u8> {
1407 DomainChainAllowlistUpdate::<T>::hashed_key_for(domain_id)
1408 }
1409
1410 pub fn updated_channels() -> BTreeSet<(ChainId, ChannelId)> {
1411 UpdatedChannels::<T>::get()
1412 }
1413
1414 pub fn open_channels() -> BTreeSet<(ChainId, ChannelId)> {
1415 Channels::<T>::iter_keys().collect()
1416 }
1417
1418 pub fn channels_and_states() -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)> {
1419 crate::migrations::get_channels_and_states::<T>()
1420 }
1421
1422 pub fn channel_nonce(chain_id: ChainId, channel_id: ChannelId) -> Option<ChannelNonce> {
1423 crate::migrations::get_channel::<T>(chain_id, channel_id).map(|channel| {
1424 let last_inbox_nonce = channel.next_inbox_nonce.checked_sub(U256::one());
1425 ChannelNonce {
1426 relay_msg_nonce: last_inbox_nonce,
1427 relay_response_msg_nonce: channel.latest_response_received_message_nonce,
1428 }
1429 })
1430 }
1431
1432 pub fn store_inbox_fee(
1433 src_chain_id: ChainId,
1434 message_id: MessageId,
1435 inbox_fees: BalanceOf<T>,
1436 ) -> DispatchResult {
1437 if !InboxFeesOnHoldStartAt::<T>::contains_key(message_id.0) {
1438 InboxFeesOnHoldStartAt::<T>::insert(message_id.0, message_id.1);
1439 }
1440 InboxFeesOnHold::<T>::mutate(|inbox_fees_on_hold| {
1441 *inbox_fees_on_hold = inbox_fees_on_hold
1442 .checked_add(&inbox_fees)
1443 .ok_or(Error::<T>::BalanceOverflow)?;
1444
1445 let imbalance = T::Currency::issue(inbox_fees);
1448 core::mem::forget(imbalance);
1449
1450 Ok::<(), Error<T>>(())
1451 })?;
1452
1453 InboxFee::<T>::insert((src_chain_id, message_id), inbox_fees);
1454
1455 if !T::NoteChainTransfer::note_transfer_in(inbox_fees, src_chain_id) {
1457 return Err(Error::<T>::FailedToNoteTransferIn.into());
1458 }
1459
1460 Ok(())
1461 }
1462
1463 pub fn store_outbox_fee(
1464 dst_chain_id: ChainId,
1465 message_id: MessageId,
1466 outbox_fees: BalanceOf<T>,
1467 inbox_fees: BalanceOf<T>,
1468 ) -> DispatchResult {
1469 if !OutboxFeesOnHoldStartAt::<T>::contains_key(message_id.0) {
1470 OutboxFeesOnHoldStartAt::<T>::insert(message_id.0, message_id.1);
1471 }
1472 OutboxFeesOnHold::<T>::mutate(|outbox_fees_on_hold| {
1473 *outbox_fees_on_hold = outbox_fees_on_hold
1474 .checked_add(&outbox_fees)
1475 .ok_or(Error::<T>::BalanceOverflow)?;
1476
1477 let imbalance = T::Currency::issue(outbox_fees);
1480 core::mem::forget(imbalance);
1481
1482 Ok::<(), Error<T>>(())
1483 })?;
1484
1485 OutboxFee::<T>::insert((dst_chain_id, message_id), outbox_fees);
1486
1487 if !T::NoteChainTransfer::note_transfer_out(inbox_fees, dst_chain_id) {
1489 return Err(Error::<T>::FailedToNoteTransferOut.into());
1490 }
1491
1492 Ok(())
1493 }
1494 }
1495}
1496
1497impl<T> Pallet<T>
1498where
1499 T: Config + CreateUnsigned<Call<T>>,
1500{
1501 pub fn outbox_message_unsigned(
1502 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1503 ) -> Option<T::Extrinsic> {
1504 let call = Call::relay_message { msg };
1505 Some(T::create_unsigned(call.into()))
1506 }
1507
1508 pub fn inbox_response_message_unsigned(
1509 msg: CrossDomainMessage<BlockNumberFor<T>, T::Hash, T::MmrHash>,
1510 ) -> Option<T::Extrinsic> {
1511 let call = Call::relay_message_response { msg };
1512 Some(T::create_unsigned(call.into()))
1513 }
1514
1515 pub fn first_outbox_message_nonce_to_relay(
1517 dst_chain_id: ChainId,
1518 channel_id: ChannelId,
1519 from_nonce: Nonce,
1520 ) -> Option<Nonce> {
1521 Self::first_relay_message(
1522 dst_chain_id,
1523 channel_id,
1524 from_nonce,
1525 Outbox::<T>::contains_key,
1526 )
1527 }
1528
1529 pub fn first_inbox_message_response_nonce_to_relay(
1531 dst_chain_id: ChainId,
1532 channel_id: ChannelId,
1533 from_nonce: Nonce,
1534 ) -> Option<Nonce> {
1535 Self::first_relay_message(
1536 dst_chain_id,
1537 channel_id,
1538 from_nonce,
1539 InboxResponses::<T>::contains_key,
1540 )
1541 }
1542
1543 fn first_relay_message<Check>(
1544 dst_chain_id: ChainId,
1545 channel_id: ChannelId,
1546 from_nonce: Nonce,
1547 check: Check,
1548 ) -> Option<Nonce>
1549 where
1550 Check: Fn((ChainId, ChannelId, Nonce)) -> bool,
1551 {
1552 let mut nonce = from_nonce;
1553 let to_nonce = from_nonce.saturating_add(MAX_FUTURE_ALLOWED_NONCES.into());
1554 while nonce <= to_nonce {
1555 if check((dst_chain_id, channel_id, nonce)) {
1556 return Some(nonce);
1557 }
1558
1559 nonce = nonce.saturating_add(Nonce::one())
1560 }
1561
1562 None
1563 }
1564}
1565
1566impl<T: Config> sp_domains::DomainBundleSubmitted for Pallet<T> {
1567 fn domain_bundle_submitted(domain_id: DomainId) {
1568 DomainChainAllowlistUpdate::<T>::mutate(domain_id, |maybe_updates| {
1572 if let Some(ref mut updates) = maybe_updates {
1573 updates.clear();
1574 }
1575 });
1576 }
1577}
1578
1579impl<T: Config> sp_domains::OnDomainInstantiated for Pallet<T> {
1580 fn on_domain_instantiated(domain_id: DomainId) {
1581 DomainChainAllowlistUpdate::<T>::insert(domain_id, DomainAllowlistUpdates::default());
1582 }
1583}