1extern crate alloc;
18
19pub use array_bytes;
20pub use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};
21pub use paste;
22pub use std::{
23 any::type_name,
24 collections::HashMap,
25 error::Error,
26 fmt,
27 marker::PhantomData,
28 ops::Deref,
29 sync::{Arc, LazyLock, Mutex},
30};
31pub use tracing;
32
33pub use cumulus_primitives_core::relay_chain::Slot;
34pub use sp_consensus_aura::AURA_ENGINE_ID;
35pub use sp_runtime::DigestItem;
36pub use alloc::collections::vec_deque::VecDeque;
38pub use core::{cell::RefCell, fmt::Debug};
39pub use cumulus_primitives_core::AggregateMessageOrigin as CumulusAggregateMessageOrigin;
40pub use frame_support::{
41 assert_ok,
42 sp_runtime::{
43 traits::{Convert, Dispatchable, Header as HeaderT, Zero},
44 Digest, DispatchResult,
45 },
46 traits::{
47 EnqueueMessage, ExecuteOverweightError, Get, Hooks, OnFinalize, OnIdle, OnInitialize,
48 OriginTrait, ProcessMessage, ProcessMessageError, ServiceQueues,
49 },
50 weights::{Weight, WeightMeter},
51};
52pub use frame_system::{
53 limits::BlockWeights as BlockWeightsLimits, pallet_prelude::BlockNumberFor,
54 Config as SystemConfig, Pallet as SystemPallet,
55};
56pub use pallet_balances::AccountData;
57pub use pallet_message_queue;
58pub use pallet_timestamp::Call as TimestampCall;
59pub use sp_arithmetic::traits::Bounded;
60pub use sp_core::{
61 crypto::get_public_from_string_or_panic, parameter_types, sr25519, storage::Storage, Pair,
62};
63pub use sp_crypto_hashing::blake2_256;
64pub use sp_io::TestExternalities;
65pub use sp_runtime::BoundedSlice;
66pub use sp_tracing;
67
68pub use cumulus_pallet_parachain_system::{
70 parachain_inherent::{deconstruct_parachain_inherent_data, InboundMessagesData},
71 Call as ParachainSystemCall, Config as ParachainSystemConfig, Pallet as ParachainSystemPallet,
72};
73pub use cumulus_primitives_core::{
74 relay_chain::{BlockNumber as RelayBlockNumber, HeadData, HrmpChannelId},
75 AbridgedHrmpChannel, DmpMessageHandler, ParaId, PersistedValidationData, XcmpMessageHandler,
76};
77pub use cumulus_primitives_parachain_inherent::ParachainInherentData;
78pub use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
79pub use pallet_aura;
80pub use pallet_message_queue::{Config as MessageQueueConfig, Pallet as MessageQueuePallet};
81pub use parachains_common::{AccountId, Balance};
82pub use polkadot_primitives;
83pub use polkadot_runtime_parachains::inclusion::{AggregateMessageOrigin, UmpQueueId};
84
85pub use polkadot_parachain_primitives::primitives::RelayChainBlockNumber;
87use sp_core::{crypto::AccountId32, H256};
88pub use xcm::latest::prelude::{
89 AccountId32 as AccountId32Junction, Ancestor, Assets, Here, Location,
90 Parachain as ParachainJunction, Parent, WeightLimit, XcmHash,
91};
92pub use xcm_executor::traits::ConvertLocation;
93use xcm_simulator::helpers::TopicIdTracker;
94
95pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
96
97thread_local! {
98 #[allow(clippy::type_complexity)]
100 pub static DOWNWARD_MESSAGES: RefCell<HashMap<String, VecDeque<(u32, Vec<(RelayBlockNumber, Vec<u8>)>)>>>
101 = RefCell::new(HashMap::new());
102 #[allow(clippy::type_complexity)]
104 pub static DMP_DONE: RefCell<HashMap<String, VecDeque<(u32, RelayBlockNumber, Vec<u8>)>>>
105 = RefCell::new(HashMap::new());
106 #[allow(clippy::type_complexity)]
108 pub static HORIZONTAL_MESSAGES: RefCell<HashMap<String, VecDeque<(u32, Vec<(ParaId, RelayBlockNumber, Vec<u8>)>)>>>
109 = RefCell::new(HashMap::new());
110 pub static UPWARD_MESSAGES: RefCell<HashMap<String, VecDeque<(u32, Vec<u8>)>>> = RefCell::new(HashMap::new());
112 pub static BRIDGED_MESSAGES: RefCell<HashMap<String, VecDeque<BridgeMessage>>> = RefCell::new(HashMap::new());
114 pub static PARA_IDS: RefCell<HashMap<String, Vec<u32>>> = RefCell::new(HashMap::new());
116 pub static INITIALIZED: RefCell<HashMap<String, bool>> = RefCell::new(HashMap::new());
118 pub static LAST_HEAD: RefCell<HashMap<String, HashMap<u32, HeadData>>> = RefCell::new(HashMap::new());
120}
121pub trait CheckAssertion<Origin, Destination, Hops, Args>
122where
123 Origin: Chain + Clone,
124 Destination: Chain + Clone,
125 Origin::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Origin::Runtime>> + Clone,
126 Destination::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Destination::Runtime>> + Clone,
127 Hops: Clone,
128 Args: Clone,
129{
130 fn check_assertion(test: Test<Origin, Destination, Hops, Args>);
131}
132
133#[impl_trait_for_tuples::impl_for_tuples(5)]
134impl<Origin, Destination, Hops, Args> CheckAssertion<Origin, Destination, Hops, Args> for Tuple
135where
136 Origin: Chain + Clone,
137 Destination: Chain + Clone,
138 Origin::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Origin::Runtime>> + Clone,
139 Destination::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Destination::Runtime>> + Clone,
140 Hops: Clone,
141 Args: Clone,
142{
143 fn check_assertion(test: Test<Origin, Destination, Hops, Args>) {
144 for_tuples!( #(
145 Tuple::check_assertion(test.clone());
146 )* );
147 }
148}
149
150pub trait AdditionalInherentCode {
153 fn on_new_block() -> DispatchResult {
154 Ok(())
155 }
156}
157
158impl AdditionalInherentCode for () {}
159
160pub trait TestExt {
161 fn build_new_ext(storage: Storage) -> TestExternalities;
162 fn new_ext() -> TestExternalities;
163 fn move_ext_out(id: &'static str);
164 fn move_ext_in(id: &'static str);
165 fn reset_ext();
166 fn execute_with<R>(execute: impl FnOnce() -> R) -> R;
167 fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R;
168}
169
170impl TestExt for () {
171 fn build_new_ext(_storage: Storage) -> TestExternalities {
172 TestExternalities::default()
173 }
174 fn new_ext() -> TestExternalities {
175 TestExternalities::default()
176 }
177 fn move_ext_out(_id: &'static str) {}
178 fn move_ext_in(_id: &'static str) {}
179 fn reset_ext() {}
180 fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
181 execute()
182 }
183 fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R {
184 func()
185 }
186}
187
188pub trait Network {
189 type Relay: RelayChain;
190 type Bridge: Bridge;
191
192 fn name() -> &'static str;
193 fn init();
194 fn reset();
195 fn para_ids() -> Vec<u32>;
196 fn relay_block_number() -> u32;
197 fn set_relay_block_number(number: u32);
198 fn process_messages();
199 fn has_unprocessed_messages() -> bool;
200 fn process_downward_messages();
201 fn process_horizontal_messages();
202 fn process_upward_messages();
203 fn process_bridged_messages();
204 fn hrmp_channel_parachain_inherent_data(
205 para_id: u32,
206 relay_parent_number: u32,
207 parent_head_data: HeadData,
208 relay_parent_offset: u64,
209 ) -> ParachainInherentData;
210 fn send_horizontal_messages<I: Iterator<Item = (ParaId, RelayBlockNumber, Vec<u8>)>>(
211 to_para_id: u32,
212 iter: I,
213 ) {
214 HORIZONTAL_MESSAGES.with(|b| {
215 b.borrow_mut()
216 .get_mut(Self::name())
217 .unwrap()
218 .push_back((to_para_id, iter.collect()))
219 });
220 }
221
222 fn send_upward_message(from_para_id: u32, msg: Vec<u8>) {
223 UPWARD_MESSAGES
224 .with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().push_back((from_para_id, msg)));
225 }
226
227 fn send_downward_messages(
228 to_para_id: u32,
229 iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
230 ) {
231 DOWNWARD_MESSAGES.with(|b| {
232 b.borrow_mut()
233 .get_mut(Self::name())
234 .unwrap()
235 .push_back((to_para_id, iter.collect()))
236 });
237 }
238
239 fn send_bridged_messages(msg: BridgeMessage) {
240 BRIDGED_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().push_back(msg));
241 }
242}
243
244pub trait Chain: TestExt {
245 type Network: Network;
246 type Runtime: SystemConfig;
247 type RuntimeCall: Clone + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>;
248 type RuntimeOrigin;
249 type RuntimeEvent;
250 type System;
251 type OriginCaller;
252
253 fn account_id_of(seed: &str) -> AccountId {
254 get_public_from_string_or_panic::<sr25519::Public>(seed).into()
255 }
256
257 fn account_data_of(account: AccountIdOf<Self::Runtime>) -> AccountData<Balance>;
258
259 fn events() -> Vec<<Self as Chain>::RuntimeEvent>;
260}
261
262pub trait RelayChain: Chain {
263 type SovereignAccountOf: ConvertLocation<AccountIdOf<Self::Runtime>>;
264 type MessageProcessor: ProcessMessage<Origin = ParaId> + ServiceQueues;
265
266 fn init();
267
268 fn child_location_of(id: ParaId) -> Location {
269 (Ancestor(0), ParachainJunction(id.into())).into()
270 }
271
272 fn sovereign_account_id_of(location: Location) -> AccountIdOf<Self::Runtime> {
273 Self::SovereignAccountOf::convert_location(&location).unwrap()
274 }
275
276 fn sovereign_account_id_of_child_para(id: ParaId) -> AccountIdOf<Self::Runtime> {
277 Self::sovereign_account_id_of(Self::child_location_of(id))
278 }
279}
280
281pub trait Parachain: Chain {
282 type XcmpMessageHandler: XcmpMessageHandler;
283 type LocationToAccountId: ConvertLocation<AccountIdOf<Self::Runtime>>;
284 type ParachainInfo: Get<ParaId>;
285 type ParachainSystem;
286 type MessageProcessor: ProcessMessage + ServiceQueues;
287 type DigestProvider: Convert<
288 (BlockNumberFor<Self::Runtime>, BlockNumberFor<Self::Runtime>),
289 Digest,
290 >;
291 type AdditionalInherentCode: AdditionalInherentCode;
292
293 fn init();
294
295 fn new_block();
296
297 fn finalize_block();
298
299 fn set_last_head();
300
301 fn para_id() -> ParaId {
302 Self::ext_wrapper(|| Self::ParachainInfo::get())
303 }
304
305 fn parent_location() -> Location {
306 (Parent).into()
307 }
308
309 fn sibling_location_of(para_id: ParaId) -> Location {
310 (Parent, ParachainJunction(para_id.into())).into()
311 }
312
313 fn sovereign_account_id_of(location: Location) -> AccountIdOf<Self::Runtime> {
314 Self::LocationToAccountId::convert_location(&location).unwrap()
315 }
316}
317
318pub trait Bridge {
319 type Source: TestExt;
320 type Target: TestExt;
321 type Handler: BridgeMessageHandler;
322
323 fn init();
324}
325
326impl Bridge for () {
327 type Source = ();
328 type Target = ();
329 type Handler = ();
330
331 fn init() {}
332}
333
334pub type BridgeLaneId = Vec<u8>;
335
336#[derive(Clone, Default, Debug)]
337pub struct BridgeMessage {
338 pub lane_id: BridgeLaneId,
339 pub nonce: u64,
340 pub payload: Vec<u8>,
341}
342
343pub trait BridgeMessageHandler {
344 fn get_source_outbound_messages() -> Vec<BridgeMessage>;
345
346 fn dispatch_target_inbound_message(
347 message: BridgeMessage,
348 ) -> Result<(), BridgeMessageDispatchError>;
349
350 fn notify_source_message_delivery(lane_id: BridgeLaneId);
351}
352
353impl BridgeMessageHandler for () {
354 fn get_source_outbound_messages() -> Vec<BridgeMessage> {
355 Default::default()
356 }
357
358 fn dispatch_target_inbound_message(
359 _message: BridgeMessage,
360 ) -> Result<(), BridgeMessageDispatchError> {
361 Err(BridgeMessageDispatchError(Box::new("Not a bridge")))
362 }
363
364 fn notify_source_message_delivery(_lane_id: BridgeLaneId) {}
365}
366
367#[derive(Debug)]
368pub struct BridgeMessageDispatchError(pub Box<dyn Debug>);
369
370impl Error for BridgeMessageDispatchError {}
371
372impl fmt::Display for BridgeMessageDispatchError {
373 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374 write!(f, "{:?}", self.0)
375 }
376}
377
378#[macro_export]
380macro_rules! decl_test_relay_chains {
381 (
382 $(
383 #[api_version($api_version:tt)]
384 pub struct $name:ident {
385 genesis = $genesis:expr,
386 on_init = $on_init:expr,
387 runtime = $runtime:ident,
388 core = {
389 SovereignAccountOf: $sovereign_acc_of:path,
390 },
391 pallets = {
392 $($pallet_name:ident: $pallet_path:path,)*
393 }
394 }
395 ),
396 +
397 $(,)?
398 ) => {
399 $(
400 #[derive(Clone)]
401 pub struct $name<N>($crate::PhantomData<N>);
402
403 impl<N: $crate::Network> $crate::Chain for $name<N> {
404 type Network = N;
405 type Runtime = $runtime::Runtime;
406 type RuntimeCall = $runtime::RuntimeCall;
407 type RuntimeOrigin = $runtime::RuntimeOrigin;
408 type RuntimeEvent = $runtime::RuntimeEvent;
409 type System = $crate::SystemPallet::<Self::Runtime>;
410 type OriginCaller = $runtime::OriginCaller;
411
412 fn account_data_of(account: $crate::AccountIdOf<Self::Runtime>) -> $crate::AccountData<$crate::Balance> {
413 <Self as $crate::TestExt>::ext_wrapper(|| $crate::SystemPallet::<Self::Runtime>::account(account).data.into())
414 }
415
416 fn events() -> Vec<<Self as $crate::Chain>::RuntimeEvent> {
417 Self::System::events()
418 .iter()
419 .map(|record| record.event.clone())
420 .collect()
421 }
422 }
423
424 impl<N: $crate::Network> $crate::RelayChain for $name<N> {
425 type SovereignAccountOf = $sovereign_acc_of;
426 type MessageProcessor = $crate::DefaultRelayMessageProcessor<$name<N>>;
427
428 fn init() {
429 use $crate::TestExt;
430 $crate::paste::paste! {
432 [<LOCAL_EXT_ $name:upper>].with(|v| *v.borrow_mut() = Self::build_new_ext($genesis));
433 }
434 }
435 }
436
437 $crate::paste::paste! {
438 pub trait [<$name RelayPallet>] {
439 $(
440 type $pallet_name;
441 )?
442 }
443
444 impl<N: $crate::Network> [<$name RelayPallet>] for $name<N> {
445 $(
446 type $pallet_name = $pallet_path;
447 )?
448 }
449 }
450
451 $crate::__impl_test_ext_for_relay_chain!($name, N, $genesis, $on_init, $api_version);
452 $crate::__impl_check_assertion!($name, N);
453 )+
454 };
455}
456
457#[macro_export]
458macro_rules! __impl_test_ext_for_relay_chain {
459 ($name:ident, $network:ident, $genesis:expr, $on_init:expr, $api_version:tt) => {
461 $crate::paste::paste! {
462 $crate::__impl_test_ext_for_relay_chain!(
463 @impl $name,
464 $network,
465 $genesis,
466 $on_init,
467 [<ParachainHostV $api_version>],
468 [<LOCAL_EXT_ $name:upper>],
469 [<GLOBAL_EXT_ $name:upper>]
470 );
471 }
472 };
473 (@impl $name:ident, $network:ident, $genesis:expr, $on_init:expr, $api_version:ident, $local_ext:ident, $global_ext:ident) => {
475 thread_local! {
476 pub static $local_ext: $crate::RefCell<$crate::TestExternalities>
477 = $crate::RefCell::new($crate::TestExternalities::new($genesis));
478 }
479
480 pub static $global_ext: $crate::LazyLock<$crate::Mutex<$crate::RefCell<$crate::HashMap<String, $crate::TestExternalities>>>>
481 = $crate::LazyLock::new(|| $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new())));
482
483 impl<$network: $crate::Network> $crate::TestExt for $name<$network> {
484 fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities {
485 use $crate::{sp_tracing, Network, Chain, TestExternalities};
486
487 let mut ext = TestExternalities::new(storage);
488
489 ext.execute_with(|| {
490 #[allow(clippy::no_effect)]
491 $on_init;
492 sp_tracing::try_init_simple();
493
494 let mut block_number = <Self as Chain>::System::block_number();
495 block_number = std::cmp::max(1, block_number);
496 <Self as Chain>::System::set_block_number(block_number);
497 });
498 ext
499 }
500
501 fn new_ext() -> $crate::TestExternalities {
502 Self::build_new_ext($genesis)
503 }
504
505 fn move_ext_out(id: &'static str) {
506 use $crate::Deref;
507
508 let local_ext = $local_ext.with(|v| {
510 v.take()
511 });
512
513 let global_ext_guard = $global_ext.lock().unwrap();
515
516 global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext);
518 }
519
520 fn move_ext_in(id: &'static str) {
521 use $crate::Deref;
522
523 let mut global_ext_unlocked = false;
524
525 while !global_ext_unlocked {
528 let global_ext_result = $global_ext.try_lock();
530
531 if let Ok(global_ext_guard) = global_ext_result {
532 if !global_ext_guard.deref().borrow().contains_key(id) {
534 drop(global_ext_guard);
535 } else {
536 global_ext_unlocked = true;
537 }
538 }
539 }
540
541 let mut global_ext_guard = $global_ext.lock().unwrap();
543
544 let global_ext = global_ext_guard.deref();
546
547 $local_ext.with(|v| {
548 v.replace(global_ext.take().remove(id).unwrap());
549 });
550 }
551
552 fn reset_ext() {
553 $local_ext.with(|v| *v.borrow_mut() = Self::build_new_ext($genesis));
554 }
555
556 fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
557 use $crate::{Chain, Network};
558 <$network>::init();
560
561 let r = $local_ext.with(|v| {
563 $crate::tracing::info!(target: "xcm::emulator::execute_with", "Executing as {}", stringify!($name));
564 v.borrow_mut().execute_with(execute)
565 });
566
567 $local_ext.with(|v| {
569 v.borrow_mut().execute_with(|| {
570 use $crate::polkadot_primitives::runtime_api::runtime_decl_for_parachain_host::$api_version;
571
572 for para_id in <$network>::para_ids() {
574 let downward_messages = <Self as $crate::Chain>::Runtime::dmq_contents(para_id.into())
576 .into_iter()
577 .map(|inbound| (inbound.sent_at, inbound.msg));
578 if downward_messages.len() == 0 {
579 continue;
580 }
581 <$network>::send_downward_messages(para_id, downward_messages.into_iter());
582
583 }
586
587 Self::events().iter().for_each(|event| {
589 $crate::tracing::info!(target: concat!("events::", stringify!($name)), ?event, "Event emitted");
590 });
591
592 <Self as Chain>::System::reset_events();
594 })
595 });
596
597 <$network>::process_messages();
598
599 r
600 }
601
602 fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R {
603 $local_ext.with(|v| {
604 v.borrow_mut().execute_with(|| {
605 func()
606 })
607 })
608 }
609 }
610 };
611}
612
613#[macro_export]
615macro_rules! decl_test_parachains {
616 (
617 $(
618 pub struct $name:ident {
619 genesis = $genesis:expr,
620 on_init = $on_init:expr,
621 runtime = $runtime:ident,
622 core = {
623 XcmpMessageHandler: $xcmp_message_handler:path,
624 LocationToAccountId: $location_to_account:path,
625 ParachainInfo: $parachain_info:path,
626 MessageOrigin: $message_origin:path,
627 $( DigestProvider: $digest_provider:ty,)?
628 $( AdditionalInherentCode: $additional_inherent_code:ty,)?
629 },
630 pallets = {
631 $($pallet_name:ident: $pallet_path:path,)*
632 }
633 }
634 ),
635 +
636 $(,)?
637 ) => {
638 $(
639 #[derive(Clone)]
640 pub struct $name<N>($crate::PhantomData<N>);
641
642 impl<N: $crate::Network> $crate::Chain for $name<N> {
643 type Runtime = $runtime::Runtime;
644 type RuntimeCall = $runtime::RuntimeCall;
645 type RuntimeOrigin = $runtime::RuntimeOrigin;
646 type RuntimeEvent = $runtime::RuntimeEvent;
647 type System = $crate::SystemPallet::<Self::Runtime>;
648 type OriginCaller = $runtime::OriginCaller;
649 type Network = N;
650
651 fn account_data_of(account: $crate::AccountIdOf<Self::Runtime>) -> $crate::AccountData<$crate::Balance> {
652 <Self as $crate::TestExt>::ext_wrapper(|| $crate::SystemPallet::<Self::Runtime>::account(account).data.into())
653 }
654
655 fn events() -> Vec<<Self as $crate::Chain>::RuntimeEvent> {
656 Self::System::events()
657 .iter()
658 .map(|record| record.event.clone())
659 .collect()
660 }
661 }
662
663 impl<N: $crate::Network> $crate::Parachain for $name<N> {
664 type XcmpMessageHandler = $xcmp_message_handler;
665 type LocationToAccountId = $location_to_account;
666 type ParachainSystem = $crate::ParachainSystemPallet<<Self as $crate::Chain>::Runtime>;
667 type ParachainInfo = $parachain_info;
668 type MessageProcessor = $crate::DefaultParaMessageProcessor<$name<N>, $message_origin>;
669 $crate::decl_test_parachains!(@inner_digest_provider $($digest_provider)?);
670 $crate::decl_test_parachains!(@inner_additional_inherent_code $($additional_inherent_code)?);
671
672 fn init() {
675 use $crate::{Chain, TestExt};
676
677 $crate::paste::paste! {
679 [<LOCAL_EXT_ $name:upper>].with(|v| *v.borrow_mut() = Self::build_new_ext($genesis));
680 }
681 Self::set_last_head();
683 Self::new_block();
685 Self::finalize_block();
687 }
688
689 fn new_block() {
690 use $crate::{
691 Dispatchable, Chain, Convert, TestExt, Zero, AdditionalInherentCode
692 };
693
694 let para_id = Self::para_id().into();
695
696 Self::ext_wrapper(|| {
697 let mut relay_block_number = N::relay_block_number();
699 relay_block_number += 1;
700 N::set_relay_block_number(relay_block_number);
701
702 let mut block_number = <Self as Chain>::System::block_number();
704 block_number += 1;
705 let parent_head_data = $crate::LAST_HEAD.with(|b| b.borrow_mut()
706 .get_mut(N::name())
707 .expect("network not initialized?")
708 .get(¶_id)
709 .expect("network not initialized?")
710 .clone()
711 );
712
713 let digest = <Self as Parachain>::DigestProvider::convert((block_number, relay_block_number));
715 let slot_duration = $crate::pallet_aura::Pallet::<$runtime::Runtime>::slot_duration();
716 <Self as Chain>::System::initialize(&block_number, &parent_head_data.hash(), &digest);
717
718 let _ = $runtime::AllPalletsWithoutSystem::on_initialize(block_number);
722
723 let timestamp_set: <Self as Chain>::RuntimeCall = $crate::TimestampCall::set {
727 now: relay_block_number as u64 * slot_duration,
730 }.into();
731 $crate::assert_ok!(
732 timestamp_set.dispatch(<Self as Chain>::RuntimeOrigin::none())
733 );
734
735 let relay_parent_offset = <<<Self as $crate::Chain>::Runtime as $crate::ParachainSystemConfig>::RelayParentOffset as $crate::Get<u32>>::get();
737
738 let data = N::hrmp_channel_parachain_inherent_data(para_id, relay_block_number, parent_head_data, relay_parent_offset as u64);
740 let (data, mut downward_messages, mut horizontal_messages) =
741 $crate::deconstruct_parachain_inherent_data(data);
742 let inbound_messages_data = $crate::InboundMessagesData::new(
743 downward_messages.into_abridged(&mut usize::MAX.clone()),
744 horizontal_messages.into_abridged(&mut usize::MAX.clone()),
745 );
746 let set_validation_data: <Self as Chain>::RuntimeCall = $crate::ParachainSystemCall::set_validation_data {
747 data,
748 inbound_messages_data
749 }.into();
750 $crate::assert_ok!(
751 set_validation_data.dispatch(<Self as Chain>::RuntimeOrigin::none())
752 );
753
754 $crate::assert_ok!(
755 <Self as Parachain>::AdditionalInherentCode::on_new_block()
756 );
757 });
758 }
759
760 fn finalize_block() {
761 use $crate::{BlockWeightsLimits, Chain, OnFinalize, OnIdle, SystemConfig, TestExt, Weight};
762
763 Self::ext_wrapper(|| {
764 let block_number = <Self as Chain>::System::block_number();
765
766 let weight = <Self as Chain>::System::block_weight();
768 let max_weight: Weight = <<<Self as Chain>::Runtime as SystemConfig>::BlockWeights as frame_support::traits::Get<BlockWeightsLimits>>::get().max_block;
769 let remaining_weight = max_weight.saturating_sub(weight.total());
770 if remaining_weight.all_gt(Weight::zero()) {
771 let _ = $runtime::AllPalletsWithSystem::on_idle(block_number, remaining_weight);
772 }
773
774 $runtime::AllPalletsWithoutSystem::on_finalize(block_number);
776 });
777
778 Self::set_last_head();
779 }
780
781
782 fn set_last_head() {
783 use $crate::{Chain, Encode, HeadData, TestExt};
784
785 let para_id = Self::para_id().into();
786
787 Self::ext_wrapper(|| {
788 let created_header = <Self as Chain>::System::finalize();
790 $crate::LAST_HEAD.with(|b| b.borrow_mut()
791 .get_mut(N::name())
792 .expect("network not initialized?")
793 .insert(para_id, HeadData(created_header.encode()))
794 );
795 });
796 }
797 }
798
799 $crate::paste::paste! {
800 pub trait [<$name ParaPallet>] {
801 $(
802 type $pallet_name;
803 )*
804 }
805
806 impl<N: $crate::Network> [<$name ParaPallet>] for $name<N> {
807 $(
808 type $pallet_name = $pallet_path;
809 )*
810 }
811 }
812
813 $crate::__impl_test_ext_for_parachain!($name, N, $genesis, $on_init);
814 $crate::__impl_check_assertion!($name, N);
815 )+
816 };
817 ( @inner_digest_provider $digest_provider:ty ) => { type DigestProvider = $digest_provider; };
818 ( @inner_digest_provider ) => { type DigestProvider = (); };
819 ( @inner_additional_inherent_code $additional_inherent_code:ty ) => { type AdditionalInherentCode = $additional_inherent_code; };
820 ( @inner_additional_inherent_code ) => { type AdditionalInherentCode = (); };
821}
822
823#[macro_export]
824macro_rules! __impl_test_ext_for_parachain {
825 ($name:ident, $network:ident, $genesis:expr, $on_init:expr) => {
827 $crate::paste::paste! {
828 $crate::__impl_test_ext_for_parachain!(@impl $name, $network, $genesis, $on_init, [<LOCAL_EXT_ $name:upper>], [<GLOBAL_EXT_ $name:upper>]);
829 }
830 };
831 (@impl $name:ident, $network:ident, $genesis:expr, $on_init:expr, $local_ext:ident, $global_ext:ident) => {
833 thread_local! {
834 pub static $local_ext: $crate::RefCell<$crate::TestExternalities>
835 = $crate::RefCell::new($crate::TestExternalities::new($genesis));
836 }
837
838 pub static $global_ext: $crate::LazyLock<$crate::Mutex<$crate::RefCell<$crate::HashMap<String, $crate::TestExternalities>>>>
839 = $crate::LazyLock::new(|| $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new())));
840
841 impl<$network: $crate::Network> $crate::TestExt for $name<$network> {
842 fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities {
843 let mut ext = $crate::TestExternalities::new(storage);
844
845 ext.execute_with(|| {
846 #[allow(clippy::no_effect)]
847 $on_init;
848 $crate::sp_tracing::try_init_simple();
849
850 let mut block_number = <Self as $crate::Chain>::System::block_number();
851 block_number = std::cmp::max(1, block_number);
852 <Self as $crate::Chain>::System::set_block_number(block_number);
853 });
854 ext
855 }
856
857 fn new_ext() -> $crate::TestExternalities {
858 Self::build_new_ext($genesis)
859 }
860
861 fn move_ext_out(id: &'static str) {
862 use $crate::Deref;
863
864 let local_ext = $local_ext.with(|v| {
866 v.take()
867 });
868
869 let global_ext_guard = $global_ext.lock().unwrap();
871
872 global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext);
874 }
875
876 fn move_ext_in(id: &'static str) {
877 use $crate::Deref;
878
879 let mut global_ext_unlocked = false;
880
881 while !global_ext_unlocked {
884 let global_ext_result = $global_ext.try_lock();
886
887 if let Ok(global_ext_guard) = global_ext_result {
888 if !global_ext_guard.deref().borrow().contains_key(id) {
890 drop(global_ext_guard);
891 } else {
892 global_ext_unlocked = true;
893 }
894 }
895 }
896
897 let mut global_ext_guard = $global_ext.lock().unwrap();
899
900 let global_ext = global_ext_guard.deref();
902
903 $local_ext.with(|v| {
904 v.replace(global_ext.take().remove(id).unwrap());
905 });
906 }
907
908 fn reset_ext() {
909 $local_ext.with(|v| *v.borrow_mut() = Self::build_new_ext($genesis));
910 }
911
912 fn execute_with<R>(execute: impl FnOnce() -> R) -> R {
913 use $crate::{Chain, Get, Hooks, Network, Parachain, Encode};
914
915 <$network>::init();
917
918 Self::new_block();
920
921 let r = $local_ext.with(|v| {
923 $crate::tracing::info!(target: "xcm::emulator::execute_with", "Executing as {}", stringify!($name));
924 v.borrow_mut().execute_with(execute)
925 });
926
927 Self::finalize_block();
929
930 let para_id = Self::para_id().into();
931
932 $local_ext.with(|v| {
934 v.borrow_mut().execute_with(|| {
935 let mock_header = $crate::HeaderT::new(
936 0,
937 Default::default(),
938 Default::default(),
939 Default::default(),
940 Default::default(),
941 );
942
943 let collation_info = <Self as Parachain>::ParachainSystem::collect_collation_info(&mock_header);
944
945 let relay_block_number = <$network>::relay_block_number();
947 for msg in collation_info.upward_messages.clone() {
948 <$network>::send_upward_message(para_id, msg);
949 }
950
951 for msg in collation_info.horizontal_messages {
953 <$network>::send_horizontal_messages(
954 msg.recipient.into(),
955 vec![(para_id.into(), relay_block_number, msg.data)].into_iter(),
956 );
957 }
958
959 type NetworkBridge<$network> = <$network as $crate::Network>::Bridge;
961
962 let bridge_messages = <<NetworkBridge<$network> as $crate::Bridge>::Handler as $crate::BridgeMessageHandler>::get_source_outbound_messages();
963
964 for msg in bridge_messages {
966 <$network>::send_bridged_messages(msg);
967 }
968
969 <Self as $crate::Chain>::events().iter().for_each(|event| {
971 $crate::tracing::info!(target: concat!("events::", stringify!($name)), ?event, "Event emitted");
972 });
973
974 <Self as $crate::Chain>::System::reset_events();
976 })
977 });
978
979 <$network>::process_messages();
983
984 r
985 }
986
987 fn ext_wrapper<R>(func: impl FnOnce() -> R) -> R {
988 $local_ext.with(|v| {
989 v.borrow_mut().execute_with(|| {
990 func()
991 })
992 })
993 }
994 }
995 };
996}
997
998#[macro_export]
1000macro_rules! decl_test_networks {
1001 (
1002 $(
1003 pub struct $name:ident {
1004 relay_chain = $relay_chain:ident,
1005 parachains = vec![ $( $parachain:ident, )* ],
1006 bridge = $bridge:ty
1007 }
1008 ),
1009 +
1010 $(,)?
1011 ) => {
1012 $(
1013 #[derive(Clone)]
1014 pub struct $name;
1015
1016 impl $crate::Network for $name {
1017 type Relay = $relay_chain<Self>;
1018 type Bridge = $bridge;
1019
1020 fn name() -> &'static str {
1021 $crate::type_name::<Self>()
1022 }
1023
1024 fn reset() {
1025 use $crate::{TestExt};
1026
1027 $crate::INITIALIZED.with(|b| b.borrow_mut().remove(Self::name()));
1028 $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
1029 $crate::DMP_DONE.with(|b| b.borrow_mut().remove(Self::name()));
1030 $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
1031 $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
1032 $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().remove(Self::name()));
1033 $crate::LAST_HEAD.with(|b| b.borrow_mut().remove(Self::name()));
1034
1035 <$relay_chain<Self>>::reset_ext();
1036 $( <$parachain<Self>>::reset_ext(); )*
1037 }
1038
1039 fn init() {
1040 if $crate::INITIALIZED.with(|b| b.borrow_mut().get(Self::name()).is_none()) {
1042 $crate::INITIALIZED.with(|b| b.borrow_mut().insert(Self::name().to_string(), true));
1043 $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
1044 $crate::DMP_DONE.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
1045 $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
1046 $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
1047 $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::VecDeque::new()));
1048 $crate::PARA_IDS.with(|b| b.borrow_mut().insert(Self::name().to_string(), Self::para_ids()));
1049 $crate::LAST_HEAD.with(|b| b.borrow_mut().insert(Self::name().to_string(), $crate::HashMap::new()));
1050
1051 <$relay_chain<Self> as $crate::RelayChain>::init();
1052 $( <$parachain<Self> as $crate::Parachain>::init(); )*
1053 }
1054 }
1055
1056 fn para_ids() -> Vec<u32> {
1057 vec![$(
1058 <$parachain<Self> as $crate::Parachain>::para_id().into(),
1059 )*]
1060 }
1061
1062 fn relay_block_number() -> u32 {
1063 <Self::Relay as $crate::TestExt>::ext_wrapper(|| {
1064 <Self::Relay as $crate::Chain>::System::block_number()
1065 })
1066 }
1067
1068 fn set_relay_block_number(number: u32) {
1069 <Self::Relay as $crate::TestExt>::ext_wrapper(|| {
1070 <Self::Relay as $crate::Chain>::System::set_block_number(number);
1071 })
1072 }
1073
1074 fn process_messages() {
1075 while Self::has_unprocessed_messages() {
1076 Self::process_upward_messages();
1077 Self::process_horizontal_messages();
1078 Self::process_downward_messages();
1079 Self::process_bridged_messages();
1080 }
1081 }
1082
1083 fn has_unprocessed_messages() -> bool {
1084 $crate::DOWNWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
1085 || $crate::HORIZONTAL_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
1086 || $crate::UPWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
1087 || $crate::BRIDGED_MESSAGES.with(|b| !b.borrow_mut().get_mut(Self::name()).unwrap().is_empty())
1088 }
1089
1090 fn process_downward_messages() {
1091 use $crate::{DmpMessageHandler, Bounded, Parachain, RelayChainBlockNumber, TestExt, Encode};
1092
1093 while let Some((to_para_id, messages))
1094 = $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
1095 $(
1096 let para_id: u32 = <$parachain<Self>>::para_id().into();
1097
1098 if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().contains(&to_para_id)) && para_id == to_para_id {
1099 let mut msg_dedup: Vec<(RelayChainBlockNumber, Vec<u8>)> = Vec::new();
1100 for m in &messages {
1101 msg_dedup.push((m.0, m.1.clone()));
1102 }
1103 msg_dedup.dedup();
1104
1105 let msgs = msg_dedup.clone().into_iter().filter(|m| {
1106 !$crate::DMP_DONE.with(|b| b.borrow().get(Self::name())
1107 .unwrap_or(&mut $crate::VecDeque::new())
1108 .contains(&(to_para_id, m.0, m.1.clone()))
1109 )
1110 }).collect::<Vec<(RelayChainBlockNumber, Vec<u8>)>>();
1111
1112 use $crate::{ProcessMessage, CumulusAggregateMessageOrigin, BoundedSlice, WeightMeter};
1113 for (block, msg) in msgs.clone().into_iter() {
1114 let mut weight_meter = WeightMeter::new();
1115 <$parachain<Self>>::ext_wrapper(|| {
1116 let _ = <$parachain<Self> as Parachain>::MessageProcessor::process_message(
1117 &msg[..],
1118 $crate::CumulusAggregateMessageOrigin::Parent.into(),
1119 &mut weight_meter,
1120 &mut msg.using_encoded($crate::blake2_256),
1121 );
1122 });
1123 let messages = msgs.clone().iter().map(|(block, message)| {
1124 (*block, $crate::array_bytes::bytes2hex("0x", message))
1125 }).collect::<Vec<_>>();
1126 $crate::tracing::info!(target: concat!("xcm::dmp::", stringify!($name)), ?to_para_id, ?messages, "Downward messages processed");
1127 $crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().push_back((to_para_id, block, msg)));
1128 }
1129 }
1130 )*
1131 }
1132 }
1133
1134 fn process_horizontal_messages() {
1135 use $crate::{XcmpMessageHandler, ServiceQueues, Bounded, Parachain, TestExt};
1136
1137 while let Some((to_para_id, messages))
1138 = $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
1139 let iter = messages.iter().map(|(para_id, relay_block_number, message)| (*para_id, *relay_block_number, &message[..])).collect::<Vec<_>>().into_iter();
1140 $(
1141 let para_id: u32 = <$parachain<Self>>::para_id().into();
1142
1143 if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().contains(&to_para_id)) && para_id == to_para_id {
1144 <$parachain<Self>>::ext_wrapper(|| {
1145 <$parachain<Self> as Parachain>::XcmpMessageHandler::handle_xcmp_messages(iter.clone(), $crate::Weight::MAX);
1146 let _ = <$parachain<Self> as Parachain>::MessageProcessor::service_queues($crate::Weight::MAX);
1148 });
1149 let messages = messages.clone().iter().map(|(para_id, relay_block_number, message)| {
1150 (*para_id, *relay_block_number, $crate::array_bytes::bytes2hex("0x", message))
1151 }).collect::<Vec<_>>();
1152 $crate::tracing::info!(target: concat!("xcm::hrmp::", stringify!($name)), ?to_para_id, ?messages, "Horizontal messages processed");
1153 }
1154 )*
1155 }
1156 }
1157
1158 fn process_upward_messages() {
1159 use $crate::{Encode, ProcessMessage, TestExt, WeightMeter};
1160
1161 while let Some((from_para_id, msg)) = $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
1162 let mut weight_meter = WeightMeter::new();
1163 <$relay_chain<Self>>::ext_wrapper(|| {
1164 let _ = <$relay_chain<Self> as $crate::RelayChain>::MessageProcessor::process_message(
1165 &msg[..],
1166 from_para_id.into(),
1167 &mut weight_meter,
1168 &mut msg.using_encoded($crate::blake2_256),
1169 );
1170 });
1171 let message = $crate::array_bytes::bytes2hex("0x", msg.clone());
1172 $crate::tracing::info!(target: concat!("xcm::ump::", stringify!($name)), ?from_para_id, ?message, "Upward message processed");
1173 }
1174 }
1175
1176 fn process_bridged_messages() {
1177 use $crate::{Bridge, BridgeMessageHandler, TestExt};
1178 <Self::Bridge as Bridge>::init();
1180
1181 while let Some(msg) = $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) {
1182 let dispatch_result = <<Self::Bridge as Bridge>::Target as TestExt>::ext_wrapper(|| {
1183 <<Self::Bridge as Bridge>::Handler as BridgeMessageHandler>::dispatch_target_inbound_message(msg.clone())
1184 });
1185
1186 match dispatch_result {
1187 Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg),
1188 Ok(()) => {
1189 <<Self::Bridge as Bridge>::Source as TestExt>::ext_wrapper(|| {
1190 <<Self::Bridge as Bridge>::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.lane_id.clone());
1191 });
1192 $crate::tracing::info!(target: concat!("bridge::", stringify!($name)), ?msg, "Bridged message processed");
1193 }
1194 }
1195 }
1196 }
1197
1198 fn hrmp_channel_parachain_inherent_data(
1199 para_id: u32,
1200 relay_parent_number: u32,
1201 parent_head_data: $crate::HeadData,
1202 relay_parent_offset: u64,
1203 ) -> $crate::ParachainInherentData {
1204 let mut sproof = $crate::RelayStateSproofBuilder::default();
1205 sproof.para_id = para_id.into();
1206 sproof.current_slot = $crate::polkadot_primitives::Slot::from(relay_parent_number as u64);
1207 sproof.host_config.max_upward_message_size = 1024 * 1024;
1208 sproof.num_authorities = relay_parent_offset + 1;
1209
1210 let e_index = sproof.hrmp_egress_channel_index.get_or_insert_with(Vec::new);
1212 for recipient_para_id in $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().clone()) {
1213 let recipient_para_id = $crate::ParaId::from(recipient_para_id);
1214 if let Err(idx) = e_index.binary_search(&recipient_para_id) {
1215 e_index.insert(idx, recipient_para_id);
1216 }
1217
1218 sproof.included_para_head = parent_head_data.clone().into();
1219
1220 sproof
1221 .hrmp_channels
1222 .entry($crate::HrmpChannelId {
1223 sender: sproof.para_id,
1224 recipient: recipient_para_id,
1225 })
1226 .or_insert_with(|| $crate::AbridgedHrmpChannel {
1227 max_capacity: 1024,
1228 max_total_size: 1024 * 1024,
1229 max_message_size: 1024 * 1024,
1230 msg_count: 0,
1231 total_size: 0,
1232 mqc_head: Option::None,
1233 });
1234 }
1235
1236 let (relay_storage_root, proof, relay_parent_descendants) =
1237 sproof.into_state_root_proof_and_descendants(relay_parent_offset);
1238
1239 $crate::ParachainInherentData {
1240 validation_data: $crate::PersistedValidationData {
1241 parent_head: parent_head_data.clone(),
1242 relay_parent_number,
1243 relay_parent_storage_root: relay_storage_root,
1244 max_pov_size: Default::default(),
1245 },
1246 relay_chain_state: proof,
1247 downward_messages: Default::default(),
1248 horizontal_messages: Default::default(),
1249 relay_parent_descendants,
1250 collator_peer_id: None,
1251 }
1252 }
1253 }
1254
1255 $crate::paste::paste! {
1256 pub type [<$relay_chain Relay>] = $relay_chain<$name>;
1257 }
1258
1259 $(
1260 $crate::paste::paste! {
1261 pub type [<$parachain Para>] = $parachain<$name>;
1262 }
1263 )*
1264 )+
1265 };
1266}
1267
1268#[macro_export]
1269macro_rules! decl_test_bridges {
1270 (
1271 $(
1272 pub struct $name:ident {
1273 source = $source:ident,
1274 target = $target:ident,
1275 handler = $handler:ident
1276 }
1277 ),
1278 +
1279 $(,)?
1280 ) => {
1281 $(
1282 #[derive(Debug)]
1283 pub struct $name;
1284
1285 impl $crate::Bridge for $name {
1286 type Source = $source;
1287 type Target = $target;
1288 type Handler = $handler;
1289
1290 fn init() {
1291 use $crate::{Network, Parachain};
1292 <$source as Chain>::Network::init();
1294 <$target as Chain>::Network::init();
1295 }
1296 }
1297 )+
1298 };
1299}
1300
1301#[macro_export]
1302macro_rules! __impl_check_assertion {
1303 ($chain:ident, $network:ident) => {
1304 impl<$network, Origin, Destination, Hops, Args>
1305 $crate::CheckAssertion<Origin, Destination, Hops, Args> for $chain<$network>
1306 where
1307 $network: $crate::Network,
1308 Origin: $crate::Chain + Clone,
1309 Destination: $crate::Chain + Clone,
1310 Origin::RuntimeOrigin:
1311 $crate::OriginTrait<AccountId = $crate::AccountIdOf<Origin::Runtime>> + Clone,
1312 Destination::RuntimeOrigin:
1313 $crate::OriginTrait<AccountId = $crate::AccountIdOf<Destination::Runtime>> + Clone,
1314 Hops: Clone,
1315 Args: Clone,
1316 {
1317 fn check_assertion(test: $crate::Test<Origin, Destination, Hops, Args>) {
1318 use $crate::{Dispatchable, TestExt};
1319
1320 let chain_name = std::any::type_name::<$chain<$network>>();
1321
1322 <$chain<$network>>::execute_with(|| {
1323 if let Some(dispatchable) = test.hops_dispatchable.get(chain_name) {
1324 $crate::assert_ok!(dispatchable(test.clone()));
1325 }
1326 if let Some(call) = test.hops_calls.get(chain_name) {
1327 $crate::assert_ok!(
1328 match call.clone().dispatch(test.signed_origin.clone()) {
1329 Ok(_) => Ok(()),
1331 Err(error_with_post_info) => Err(error_with_post_info.error),
1332 }
1333 );
1334 }
1335 if let Some(assertion) = test.hops_assertion.get(chain_name) {
1336 assertion(test);
1337 }
1338 });
1339 }
1340 }
1341 };
1342}
1343
1344#[macro_export]
1345macro_rules! assert_expected_events {
1346 ( $chain:ident, vec![$( $event_pat:pat => { $($attr:ident : $condition:expr, )* }, )*] ) => {
1347 let mut messages: Vec<String> = Vec::new();
1348 let mut events = <$chain as $crate::Chain>::events();
1349
1350 $(
1352 let mut failure_message: Option<String> = None;
1354 let mut event_received = false;
1355 for index in 0..events.len() {
1356 let event = &events[index];
1357 match event {
1358 $event_pat => {
1359 let mut event_meets_conditions = true;
1360 let mut conditions_message: Vec<String> = Vec::new();
1361 event_received = true;
1362
1363 $(
1364 if !$condition {
1365 conditions_message.push(
1366 format!(
1367 " - The attribute {} = {:?} did not meet the condition {}\n",
1368 stringify!($attr),
1369 $attr,
1370 stringify!($condition)
1371 )
1372 );
1373 }
1374 event_meets_conditions &= $condition;
1375 )*
1376
1377 if failure_message.is_none() && !conditions_message.is_empty() {
1378 failure_message = Some(format!(
1380 "\n\n{}::\x1b[31m{}\x1b[0m was received but some of its attributes did not meet the conditions.\n\
1381 Actual event:\n{:#?}\n\
1382 Failures:\n{}",
1383 stringify!($chain),
1384 stringify!($event_pat),
1385 event,
1386 conditions_message.concat()
1387 ));
1388 }
1389
1390 if event_meets_conditions {
1391 failure_message = None;
1393 events.remove(index);
1394 break;
1395 }
1396 },
1397 _ => {}
1398 }
1399 }
1400
1401 if !event_received || failure_message.is_some() {
1402 messages.push(
1404 format!(
1405 "\n\n{}::\x1b[31m{}\x1b[0m was never received. All events:\n{:#?}",
1406 stringify!($chain),
1407 stringify!($event_pat),
1408 <$chain as $crate::Chain>::events(),
1409 )
1410 );
1411 }
1412 )*
1413
1414 if !messages.is_empty() {
1415 <$chain as $crate::Chain>::events().iter().for_each(|event| {
1417 $crate::tracing::info!(target: concat!("events::", stringify!($chain)), ?event, "Event emitted");
1418 });
1419 panic!("{}", messages.concat())
1420 }
1421 }
1422}
1423
1424#[macro_export]
1425macro_rules! bx {
1426 ($e:expr) => {
1427 Box::new($e)
1428 };
1429}
1430
1431#[macro_export]
1432macro_rules! decl_test_sender_receiver_accounts_parameter_types {
1433 ( $( $chain:ident { sender: $sender:expr, receiver: $receiver:expr }),+ ) => {
1434 $crate::paste::paste! {
1435 $crate::parameter_types! {
1436 $(
1437 pub [<$chain Sender>]: $crate::AccountId = <$chain as $crate::Chain>::account_id_of($sender);
1438 pub [<$chain Receiver>]: $crate::AccountId = <$chain as $crate::Chain>::account_id_of($receiver);
1439 )+
1440 }
1441 }
1442 };
1443}
1444
1445pub struct DefaultParaMessageProcessor<T, M>(PhantomData<(T, M)>);
1446impl<T, M> ProcessMessage for DefaultParaMessageProcessor<T, M>
1448where
1449 M: codec::FullCodec
1450 + MaxEncodedLen
1451 + Clone
1452 + Eq
1453 + PartialEq
1454 + frame_support::pallet_prelude::TypeInfo
1455 + Debug,
1456 T: Parachain,
1457 T::Runtime: MessageQueueConfig,
1458 <<T::Runtime as MessageQueueConfig>::MessageProcessor as ProcessMessage>::Origin: PartialEq<M>,
1459 MessageQueuePallet<T::Runtime>: EnqueueMessage<M> + ServiceQueues,
1460{
1461 type Origin = M;
1462
1463 fn process_message(
1464 msg: &[u8],
1465 orig: Self::Origin,
1466 _meter: &mut WeightMeter,
1467 _id: &mut XcmHash,
1468 ) -> Result<bool, ProcessMessageError> {
1469 MessageQueuePallet::<T::Runtime>::enqueue_message(
1470 msg.try_into().expect("Message too long"),
1471 orig.clone(),
1472 );
1473 MessageQueuePallet::<T::Runtime>::service_queues(Weight::MAX);
1474
1475 Ok(true)
1476 }
1477}
1478impl<T, M> ServiceQueues for DefaultParaMessageProcessor<T, M>
1479where
1480 M: MaxEncodedLen,
1481 T: Parachain,
1482 T::Runtime: MessageQueueConfig,
1483 <<T::Runtime as MessageQueueConfig>::MessageProcessor as ProcessMessage>::Origin: PartialEq<M>,
1484 MessageQueuePallet<T::Runtime>: EnqueueMessage<M> + ServiceQueues,
1485{
1486 type OverweightMessageAddress = ();
1487
1488 fn service_queues(weight_limit: Weight) -> Weight {
1489 MessageQueuePallet::<T::Runtime>::service_queues(weight_limit)
1490 }
1491
1492 fn execute_overweight(
1493 _weight_limit: Weight,
1494 _address: Self::OverweightMessageAddress,
1495 ) -> Result<Weight, ExecuteOverweightError> {
1496 unimplemented!()
1497 }
1498}
1499
1500pub type MessageOriginFor<T> =
1501 <<<T as Chain>::Runtime as MessageQueueConfig>::MessageProcessor as ProcessMessage>::Origin;
1502
1503pub struct DefaultRelayMessageProcessor<T>(PhantomData<T>);
1504impl<T> ProcessMessage for DefaultRelayMessageProcessor<T>
1506where
1507 T: RelayChain,
1508 T::Runtime: MessageQueueConfig,
1509 MessageOriginFor<T>: From<AggregateMessageOrigin>,
1510 MessageQueuePallet<T::Runtime>: EnqueueMessage<MessageOriginFor<T>> + ServiceQueues,
1511{
1512 type Origin = ParaId;
1513
1514 fn process_message(
1515 msg: &[u8],
1516 para: Self::Origin,
1517 _meter: &mut WeightMeter,
1518 _id: &mut XcmHash,
1519 ) -> Result<bool, ProcessMessageError> {
1520 MessageQueuePallet::<T::Runtime>::enqueue_message(
1521 msg.try_into().expect("Message too long"),
1522 AggregateMessageOrigin::Ump(UmpQueueId::Para(para)).into(),
1523 );
1524 MessageQueuePallet::<T::Runtime>::service_queues(Weight::MAX);
1525
1526 Ok(true)
1527 }
1528}
1529
1530impl<T> ServiceQueues for DefaultRelayMessageProcessor<T>
1531where
1532 T: RelayChain,
1533 T::Runtime: MessageQueueConfig,
1534 MessageOriginFor<T>: From<AggregateMessageOrigin>,
1535 MessageQueuePallet<T::Runtime>: EnqueueMessage<MessageOriginFor<T>> + ServiceQueues,
1536{
1537 type OverweightMessageAddress = ();
1538
1539 fn service_queues(weight_limit: Weight) -> Weight {
1540 MessageQueuePallet::<T::Runtime>::service_queues(weight_limit)
1541 }
1542
1543 fn execute_overweight(
1544 _weight_limit: Weight,
1545 _address: Self::OverweightMessageAddress,
1546 ) -> Result<Weight, ExecuteOverweightError> {
1547 unimplemented!()
1548 }
1549}
1550
1551#[derive(Clone)]
1553pub struct TestAccount<R: Chain> {
1554 pub account_id: AccountIdOf<R::Runtime>,
1555 pub balance: Balance,
1556}
1557
1558#[derive(Clone)]
1560pub struct TestArgs {
1561 pub dest: Location,
1562 pub beneficiary: Location,
1563 pub amount: Balance,
1564 pub assets: Assets,
1565 pub asset_id: Option<u32>,
1566 pub fee_asset_item: u32,
1567 pub weight_limit: WeightLimit,
1568}
1569
1570impl TestArgs {
1571 pub fn new_relay(dest: Location, beneficiary_id: AccountId32, amount: Balance) -> Self {
1573 Self {
1574 dest,
1575 beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
1576 amount,
1577 assets: (Here, amount).into(),
1578 asset_id: None,
1579 fee_asset_item: 0,
1580 weight_limit: WeightLimit::Unlimited,
1581 }
1582 }
1583
1584 pub fn new_para(
1586 dest: Location,
1587 beneficiary_id: AccountId32,
1588 amount: Balance,
1589 assets: Assets,
1590 asset_id: Option<u32>,
1591 fee_asset_item: u32,
1592 ) -> Self {
1593 Self {
1594 dest,
1595 beneficiary: AccountId32Junction { network: None, id: beneficiary_id.into() }.into(),
1596 amount,
1597 assets,
1598 asset_id,
1599 fee_asset_item,
1600 weight_limit: WeightLimit::Unlimited,
1601 }
1602 }
1603}
1604
1605pub struct TestContext<T, Origin: Chain, Destination: Chain> {
1607 pub sender: AccountIdOf<Origin::Runtime>,
1608 pub receiver: AccountIdOf<Destination::Runtime>,
1609 pub args: T,
1610}
1611
1612#[derive(Clone)]
1621pub struct Test<Origin, Destination, Hops = (), Args = TestArgs>
1622where
1623 Origin: Chain + Clone,
1624 Destination: Chain + Clone,
1625 Origin::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Origin::Runtime>> + Clone,
1626 Destination::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Destination::Runtime>> + Clone,
1627 Hops: Clone,
1628{
1629 pub sender: TestAccount<Origin>,
1630 pub receiver: TestAccount<Destination>,
1631 pub signed_origin: Origin::RuntimeOrigin,
1632 pub root_origin: Origin::RuntimeOrigin,
1633 pub hops_assertion: HashMap<String, fn(Self)>,
1634 pub hops_dispatchable: HashMap<String, fn(Self) -> DispatchResult>,
1635 pub hops_calls: HashMap<String, Origin::RuntimeCall>,
1636 pub args: Args,
1637 pub topic_id_tracker: Arc<Mutex<TopicIdTracker>>,
1638 _marker: PhantomData<(Destination, Hops)>,
1639}
1640
1641impl<Origin, Destination, Hops, Args> Test<Origin, Destination, Hops, Args>
1643where
1644 Args: Clone,
1645 Origin: Chain + Clone,
1646 Destination: Chain + Clone,
1647 Origin::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Origin::Runtime>> + Clone,
1648 Destination::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Destination::Runtime>> + Clone,
1649 Hops: Clone,
1650{
1651 pub fn assert_unique_topic_id(&self) {
1653 self.topic_id_tracker.lock().unwrap().assert_unique();
1654 }
1655 pub fn insert_unique_topic_id(&mut self, chain: &str, id: H256) {
1657 self.topic_id_tracker.lock().unwrap().insert_and_assert_unique(chain, id);
1658 }
1659}
1660impl<Origin, Destination, Hops, Args> Test<Origin, Destination, Hops, Args>
1661where
1662 Args: Clone,
1663 Origin: Chain + Clone + CheckAssertion<Origin, Destination, Hops, Args>,
1664 Destination: Chain + Clone + CheckAssertion<Origin, Destination, Hops, Args>,
1665 Origin::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Origin::Runtime>> + Clone,
1666 Destination::RuntimeOrigin: OriginTrait<AccountId = AccountIdOf<Destination::Runtime>> + Clone,
1667 Hops: Clone + CheckAssertion<Origin, Destination, Hops, Args>,
1668{
1669 pub fn new(test_args: TestContext<Args, Origin, Destination>) -> Self {
1671 Test {
1672 sender: TestAccount {
1673 account_id: test_args.sender.clone(),
1674 balance: Origin::account_data_of(test_args.sender.clone()).free,
1675 },
1676 receiver: TestAccount {
1677 account_id: test_args.receiver.clone(),
1678 balance: Destination::account_data_of(test_args.receiver.clone()).free,
1679 },
1680 signed_origin: <Origin as Chain>::RuntimeOrigin::signed(test_args.sender),
1681 root_origin: <Origin as Chain>::RuntimeOrigin::root(),
1682 hops_assertion: Default::default(),
1683 hops_dispatchable: Default::default(),
1684 hops_calls: Default::default(),
1685 args: test_args.args,
1686 topic_id_tracker: Arc::new(Mutex::new(TopicIdTracker::new())),
1687 _marker: Default::default(),
1688 }
1689 }
1690 pub fn set_assertion<Hop>(&mut self, assertion: fn(Self)) {
1692 let chain_name = std::any::type_name::<Hop>();
1693 self.hops_assertion.insert(chain_name.to_string(), assertion);
1694 }
1695 pub fn set_dispatchable<Hop>(&mut self, dispatchable: fn(Self) -> DispatchResult) {
1697 let chain_name = std::any::type_name::<Hop>();
1698 self.hops_dispatchable.insert(chain_name.to_string(), dispatchable);
1699 }
1700 pub fn set_call(&mut self, call: Origin::RuntimeCall) {
1702 let chain_name = std::any::type_name::<Origin>();
1703 self.hops_calls.insert(chain_name.to_string(), call);
1704 }
1705 pub fn assert(&mut self) {
1707 Origin::check_assertion(self.clone());
1708 Hops::check_assertion(self.clone());
1709 Destination::check_assertion(self.clone());
1710 Self::update_balances(self);
1711 }
1712 fn update_balances(&mut self) {
1714 self.sender.balance = Origin::account_data_of(self.sender.account_id.clone()).free;
1715 self.receiver.balance = Destination::account_data_of(self.receiver.account_id.clone()).free;
1716 }
1717}
1718
1719pub mod helpers {
1720 use super::*;
1721
1722 pub fn within_threshold(threshold: u64, expected_value: u64, current_value: u64) -> bool {
1723 let margin = (current_value * threshold) / 100;
1724 let lower_limit = expected_value.checked_sub(margin).unwrap_or(u64::MIN);
1725 let upper_limit = expected_value.checked_add(margin).unwrap_or(u64::MAX);
1726
1727 current_value >= lower_limit && current_value <= upper_limit
1728 }
1729
1730 pub fn weight_within_threshold(
1731 (threshold_time, threshold_size): (u64, u64),
1732 expected_weight: Weight,
1733 weight: Weight,
1734 ) -> bool {
1735 let ref_time_within =
1736 within_threshold(threshold_time, expected_weight.ref_time(), weight.ref_time());
1737 let proof_size_within =
1738 within_threshold(threshold_size, expected_weight.proof_size(), weight.proof_size());
1739
1740 ref_time_within && proof_size_within
1741 }
1742}