xcm_simulator/
mock_message_queue.rs1use 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 #[allow(deprecated)]
39 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
40 type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
41 }
42
43 #[pallet::call]
44 impl<T: Config> Pallet<T> {}
45
46 #[pallet::pallet]
47 #[pallet::without_storage_info]
48 pub struct Pallet<T>(_);
49
50 #[pallet::storage]
51 pub type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;
52
53 #[pallet::storage]
54 pub type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
56
57 impl<T: Config> Get<ParaId> for Pallet<T> {
58 fn get() -> ParaId {
59 ParachainId::<T>::get()
60 }
61 }
62
63 pub type MessageId = [u8; 32];
64
65 #[pallet::event]
66 #[pallet::generate_deposit(pub(super) fn deposit_event)]
67 pub enum Event<T: Config> {
68 Success { message_id: Option<T::Hash> },
71 Fail { message_id: Option<T::Hash>, error: XcmError },
73 BadVersion { message_id: Option<T::Hash> },
75 BadFormat { message_id: Option<T::Hash> },
77
78 InvalidFormat { message_id: MessageId },
81 UnsupportedVersion { message_id: MessageId },
83 ExecutedDownward { message_id: MessageId, outcome: Outcome },
85 }
86
87 impl<T: Config> Pallet<T> {
88 pub fn set_para_id(para_id: ParaId) {
89 ParachainId::<T>::put(para_id);
90 }
91
92 fn handle_xcmp_message(
93 sender: ParaId,
94 _sent_at: RelayBlockNumber,
95 xcm: VersionedXcm<T::RuntimeCall>,
96 max_weight: xcm::latest::Weight,
97 ) -> Result<xcm::latest::Weight, XcmError> {
98 let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
99 let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
100 let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
101 Ok(xcm) => {
102 let location = (Parent, Parachain(sender.into()));
103 match T::XcmExecutor::prepare_and_execute(
104 location,
105 xcm,
106 &mut message_hash,
107 max_weight,
108 Weight::zero(),
109 ) {
110 Outcome::Error(InstructionError { error, .. }) => {
111 (Err(error), Event::Fail { message_id: Some(hash), error })
112 },
113 Outcome::Complete { used } => {
114 (Ok(used), Event::Success { message_id: Some(hash) })
115 },
116 Outcome::Incomplete {
119 used, error: InstructionError { error, .. }, ..
120 } => (Ok(used), Event::Fail { message_id: Some(hash), error }),
121 }
122 },
123 Err(()) => (
124 Err(XcmError::UnhandledXcmVersion),
125 Event::BadVersion { message_id: Some(hash) },
126 ),
127 };
128 Self::deposit_event(event);
129 result
130 }
131 }
132
133 impl<T: Config> XcmpMessageHandler for Pallet<T> {
134 fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
135 iter: I,
136 max_weight: xcm::latest::Weight,
137 ) -> xcm::latest::Weight {
138 for (sender, sent_at, data) in iter {
139 let mut data_ref = data;
140 let _ = XcmpMessageFormat::decode(&mut data_ref)
141 .expect("Simulator encodes with versioned xcm format; qed");
142
143 let mut remaining_fragments = data_ref;
144 while !remaining_fragments.is_empty() {
145 if let Ok(xcm) =
146 VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments)
147 {
148 let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
149 } else {
150 debug_assert!(false, "Invalid incoming XCMP message data");
151 }
152 }
153 }
154 max_weight
155 }
156 }
157
158 impl<T: Config> DmpMessageHandler for Pallet<T> {
159 fn handle_dmp_messages(
160 iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
161 limit: Weight,
162 ) -> Weight {
163 for (_sent_at, data) in iter {
164 let mut id = sp_io::hashing::blake2_256(&data[..]);
165 let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
166 match maybe_versioned {
167 Err(_) => {
168 Self::deposit_event(Event::InvalidFormat { message_id: id });
169 },
170 Ok(versioned) => match Xcm::try_from(versioned) {
171 Err(()) => {
172 Self::deposit_event(Event::UnsupportedVersion { message_id: id })
173 },
174 Ok(x) => {
175 let outcome = T::XcmExecutor::prepare_and_execute(
176 Parent,
177 x.clone(),
178 &mut id,
179 limit,
180 Weight::zero(),
181 );
182 ReceivedDmp::<T>::append(x);
183 Self::deposit_event(Event::ExecutedDownward {
184 message_id: id,
185 outcome,
186 });
187 },
188 },
189 }
190 }
191 limit
192 }
193 }
194}