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