orml_xcm_mock_message_queue/
lib.rs

1//! # Mock Message Queue for XCM Simulator tests
2
3#![cfg_attr(not(feature = "std"), no_std)]
4
5use cumulus_primitives_core::{ParaId, XcmpMessageFormat, XcmpMessageHandler};
6use frame_support::pallet_prelude::*;
7use parity_scale_codec::{Decode, Encode};
8use polkadot_parachain_primitives::primitives::DmpMessageHandler;
9use sp_std::prelude::*;
10use xcm::{
11	v5::{prelude::*, Weight},
12	VersionedXcm,
13};
14
15pub use module::*;
16
17#[frame_support::pallet]
18pub mod module {
19	use super::*;
20
21	type RelayBlockNumber = u32;
22
23	#[pallet::config]
24	pub trait Config: frame_system::Config {
25		type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
26	}
27
28	#[pallet::call]
29	impl<T: Config> Pallet<T> {}
30
31	#[pallet::pallet]
32	#[pallet::without_storage_info]
33	pub struct Pallet<T>(_);
34
35	#[pallet::storage]
36	#[pallet::getter(fn parachain_id)]
37	pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
38
39	#[pallet::storage]
40	#[pallet::getter(fn received_dmp)]
41	/// A queue of received DMP messages
42	pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
43
44	impl<T: Config> Get<ParaId> for Pallet<T> {
45		fn get() -> ParaId {
46			Self::parachain_id()
47		}
48	}
49
50	pub type MessageId = [u8; 32];
51
52	#[pallet::event]
53	#[pallet::generate_deposit(pub(super) fn deposit_event)]
54	pub enum Event<T: Config> {
55		// XCMP
56		/// Some XCM was executed OK.
57		Success(Option<T::Hash>),
58		/// Some XCM failed.
59		Fail(Option<T::Hash>, XcmError),
60		/// Bad XCM version used.
61		BadVersion(Option<T::Hash>),
62		/// Bad XCM format used.
63		BadFormat(Option<T::Hash>),
64
65		// DMP
66		/// Downward message is invalid XCM.
67		InvalidFormat(MessageId),
68		/// Downward message is unsupported version of XCM.
69		UnsupportedVersion(MessageId),
70		/// Downward message executed with the given outcome.
71		ExecutedDownward(MessageId, Outcome),
72	}
73
74	impl<T: Config> Pallet<T> {
75		pub fn set_para_id(para_id: ParaId) {
76			ParachainId::<T>::put(para_id);
77		}
78
79		fn handle_xcmp_message(
80			sender: ParaId,
81			_sent_at: RelayBlockNumber,
82			xcm: VersionedXcm<T::RuntimeCall>,
83			max_weight: Weight,
84		) -> Result<Weight, XcmError> {
85			use sp_runtime::traits::Hash;
86			let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
87			let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
88			let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
89				Ok(xcm) => {
90					let location = (Parent, Parachain(sender.into()));
91					match T::XcmExecutor::prepare_and_execute(
92						location,
93						xcm,
94						&mut message_hash,
95						max_weight,
96						Weight::zero(),
97					) {
98						Outcome::Error(InstructionError { error, .. }) => (Err(error), Event::Fail(Some(hash), error)),
99						Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))),
100						// As far as the caller is concerned, this was dispatched without error, so
101						// we just report the weight used.
102						Outcome::Incomplete {
103							used,
104							error: InstructionError { error, .. },
105						} => (Ok(used), Event::Fail(Some(hash), error)),
106					}
107				}
108				Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))),
109			};
110			Self::deposit_event(event);
111			result
112		}
113	}
114
115	impl<T: Config> XcmpMessageHandler for Pallet<T> {
116		fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
117			iter: I,
118			max_weight: Weight,
119		) -> Weight {
120			for (sender, sent_at, data) in iter {
121				let mut data_ref = data;
122				let _ =
123					XcmpMessageFormat::decode(&mut data_ref).expect("Simulator encodes with versioned xcm format; qed");
124
125				let mut remaining_fragments = data_ref;
126				while !remaining_fragments.is_empty() {
127					if let Ok(xcm) = VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments) {
128						let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
129					} else {
130						debug_assert!(false, "Invalid incoming XCMP message data");
131					}
132				}
133			}
134			max_weight
135		}
136	}
137
138	impl<T: Config> DmpMessageHandler for Pallet<T> {
139		fn handle_dmp_messages(iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>, limit: Weight) -> Weight {
140			for (_sent_at, data) in iter {
141				let mut id = sp_io::hashing::blake2_256(&data[..]);
142				let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
143				match maybe_versioned {
144					Err(_) => {
145						Self::deposit_event(Event::InvalidFormat(id));
146					}
147					Ok(versioned) => match Xcm::try_from(versioned) {
148						Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)),
149						Ok(x) => {
150							let outcome =
151								T::XcmExecutor::prepare_and_execute(Parent, x.clone(), &mut id, limit, Weight::zero());
152							<ReceivedDmp<T>>::append(x);
153							Self::deposit_event(Event::ExecutedDownward(id, outcome));
154						}
155					},
156				}
157			}
158			limit
159		}
160	}
161}