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