xcm_simulator/
mock_message_queue.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Simple mock message queue.
18
19use codec::{Decode, Encode};
20
21use polkadot_parachain_primitives::primitives::{
22	DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler,
23};
24use polkadot_primitives::BlockNumber as RelayBlockNumber;
25use sp_runtime::traits::{Get, Hash};
26
27use xcm::{latest::prelude::*, VersionedXcm};
28
29pub use pallet::*;
30
31#[frame_support::pallet]
32pub mod pallet {
33	use super::*;
34	use frame_support::pallet_prelude::*;
35
36	#[pallet::config]
37	pub trait Config: frame_system::Config {
38		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
39		type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
40	}
41
42	#[pallet::call]
43	impl<T: Config> Pallet<T> {}
44
45	#[pallet::pallet]
46	#[pallet::without_storage_info]
47	pub struct Pallet<T>(_);
48
49	#[pallet::storage]
50	pub type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
51
52	#[pallet::storage]
53	/// A queue of received DMP messages
54	pub type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
55
56	impl<T: Config> Get<ParaId> for Pallet<T> {
57		fn get() -> ParaId {
58			ParachainId::<T>::get()
59		}
60	}
61
62	pub type MessageId = [u8; 32];
63
64	#[pallet::event]
65	#[pallet::generate_deposit(pub(super) fn deposit_event)]
66	pub enum Event<T: Config> {
67		// XCMP
68		/// Some XCM was executed OK.
69		Success { message_id: Option<T::Hash> },
70		/// Some XCM failed.
71		Fail { message_id: Option<T::Hash>, error: XcmError },
72		/// Bad XCM version used.
73		BadVersion { message_id: Option<T::Hash> },
74		/// Bad XCM format used.
75		BadFormat { message_id: Option<T::Hash> },
76
77		// DMP
78		/// Downward message is invalid XCM.
79		InvalidFormat { message_id: MessageId },
80		/// Downward message is unsupported version of XCM.
81		UnsupportedVersion { message_id: MessageId },
82		/// Downward message executed with the given outcome.
83		ExecutedDownward { message_id: MessageId, outcome: Outcome },
84	}
85
86	impl<T: Config> Pallet<T> {
87		pub fn set_para_id(para_id: ParaId) {
88			ParachainId::<T>::put(para_id);
89		}
90
91		fn handle_xcmp_message(
92			sender: ParaId,
93			_sent_at: RelayBlockNumber,
94			xcm: VersionedXcm<T::RuntimeCall>,
95			max_weight: xcm::latest::Weight,
96		) -> Result<xcm::latest::Weight, XcmError> {
97			let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
98			let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
99			let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
100				Ok(xcm) => {
101					let location = (Parent, Parachain(sender.into()));
102					match T::XcmExecutor::prepare_and_execute(
103						location,
104						xcm,
105						&mut message_hash,
106						max_weight,
107						Weight::zero(),
108					) {
109						Outcome::Error { error } =>
110							(Err(error), Event::Fail { message_id: Some(hash), error }),
111						Outcome::Complete { used } =>
112							(Ok(used), Event::Success { message_id: Some(hash) }),
113						// As far as the caller is concerned, this was dispatched without error, so
114						// we just report the weight used.
115						Outcome::Incomplete { used, error } =>
116							(Ok(used), Event::Fail { message_id: Some(hash), error }),
117					}
118				},
119				Err(()) => (
120					Err(XcmError::UnhandledXcmVersion),
121					Event::BadVersion { message_id: Some(hash) },
122				),
123			};
124			Self::deposit_event(event);
125			result
126		}
127	}
128
129	impl<T: Config> XcmpMessageHandler for Pallet<T> {
130		fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
131			iter: I,
132			max_weight: xcm::latest::Weight,
133		) -> xcm::latest::Weight {
134			for (sender, sent_at, data) in iter {
135				let mut data_ref = data;
136				let _ = XcmpMessageFormat::decode(&mut data_ref)
137					.expect("Simulator encodes with versioned xcm format; qed");
138
139				let mut remaining_fragments = data_ref;
140				while !remaining_fragments.is_empty() {
141					if let Ok(xcm) =
142						VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments)
143					{
144						let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
145					} else {
146						debug_assert!(false, "Invalid incoming XCMP message data");
147					}
148				}
149			}
150			max_weight
151		}
152	}
153
154	impl<T: Config> DmpMessageHandler for Pallet<T> {
155		fn handle_dmp_messages(
156			iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
157			limit: Weight,
158		) -> Weight {
159			for (_sent_at, data) in iter {
160				let mut id = sp_io::hashing::blake2_256(&data[..]);
161				let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
162				match maybe_versioned {
163					Err(_) => {
164						Self::deposit_event(Event::InvalidFormat { message_id: id });
165					},
166					Ok(versioned) => match Xcm::try_from(versioned) {
167						Err(()) =>
168							Self::deposit_event(Event::UnsupportedVersion { message_id: id }),
169						Ok(x) => {
170							let outcome = T::XcmExecutor::prepare_and_execute(
171								Parent,
172								x.clone(),
173								&mut id,
174								limit,
175								Weight::zero(),
176							);
177							ReceivedDmp::<T>::append(x);
178							Self::deposit_event(Event::ExecutedDownward {
179								message_id: id,
180								outcome,
181							});
182						},
183					},
184				}
185			}
186			limit
187		}
188	}
189}