// Copyright 2023 Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . pub use codec::{Decode, Encode}; pub use log; pub use paste; pub use std::{collections::HashMap, error::Error, fmt, thread::LocalKey}; // Substrate pub use frame_support::{ assert_ok, traits::{EnqueueMessage, Get, Hooks, ProcessMessage, ProcessMessageError, ServiceQueues}, weights::{Weight, WeightMeter}, }; pub use frame_system::AccountInfo; pub use pallet_balances::AccountData; pub use sp_arithmetic::traits::Bounded; pub use sp_core::{storage::Storage, H256}; pub use sp_io; pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, fmt::Debug}; pub use sp_trie::StorageProof; //Cumulus pub use cumulus_pallet_dmp_queue; pub use cumulus_pallet_parachain_system; pub use cumulus_pallet_xcmp_queue; pub use cumulus_primitives_core::{ self, relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler, ParaId, PersistedValidationData, XcmpMessageHandler, }; pub use cumulus_primitives_parachain_inherent::ParachainInherentData; pub use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; pub use cumulus_test_service::get_account_id_from_seed; pub use pallet_message_queue; pub use parachain_info; pub use parachains_common::{AccountId, BlockNumber}; pub use polkadot_primitives; pub use polkadot_runtime_parachains::{ dmp, inclusion::{AggregateMessageOrigin, UmpQueueId}, }; // Polkadot pub use xcm::v3::prelude::*; thread_local! { /// Downward messages, each message is: `(to_para_id, [(relay_block_number, msg)])` #[allow(clippy::type_complexity)] pub static DOWNWARD_MESSAGES: RefCell)>)>>> = RefCell::new(HashMap::new()); /// Downward messages that already processed by parachains, each message is: `(to_para_id, relay_block_number, Vec)` #[allow(clippy::type_complexity)] pub static DMP_DONE: RefCell)>>> = RefCell::new(HashMap::new()); /// Horizontal messages, each message is: `(to_para_id, [(from_para_id, relay_block_number, msg)])` #[allow(clippy::type_complexity)] pub static HORIZONTAL_MESSAGES: RefCell)>)>>> = RefCell::new(HashMap::new()); /// Upward messages, each message is: `(from_para_id, msg)` pub static UPWARD_MESSAGES: RefCell)>>> = RefCell::new(HashMap::new()); /// Bridged messages, each message is: `BridgeMessage` pub static BRIDGED_MESSAGES: RefCell>> = RefCell::new(HashMap::new()); /// Global incremental relay chain block number pub static RELAY_BLOCK_NUMBER: RefCell> = RefCell::new(HashMap::new()); /// Parachains Ids a the Network pub static PARA_IDS: RefCell>> = RefCell::new(HashMap::new()); /// Flag indicating if global variables have been initialized for a certain Network pub static INITIALIZED: RefCell> = RefCell::new(HashMap::new()); } pub trait TestExt { fn build_new_ext(storage: Storage) -> sp_io::TestExternalities; fn new_ext() -> sp_io::TestExternalities; fn reset_ext(); fn execute_with(execute: impl FnOnce() -> R) -> R; fn ext_wrapper(func: impl FnOnce() -> R) -> R; } impl TestExt for () { fn build_new_ext(_storage: Storage) -> sp_io::TestExternalities { sp_io::TestExternalities::default() } fn new_ext() -> sp_io::TestExternalities { sp_io::TestExternalities::default() } fn reset_ext() {} fn execute_with(execute: impl FnOnce() -> R) -> R { execute() } fn ext_wrapper(func: impl FnOnce() -> R) -> R { func() } } pub trait Network { type Bridge: Bridge; fn init(); fn para_ids() -> Vec; fn relay_block_number() -> u32; fn set_relay_block_number(block_number: u32); fn process_messages(); fn has_unprocessed_messages() -> bool; fn process_downward_messages(); fn process_horizontal_messages(); fn process_upward_messages(); fn process_bridged_messages(); fn hrmp_channel_parachain_inherent_data( para_id: u32, relay_parent_number: u32, ) -> ParachainInherentData; } pub trait NetworkComponent { type Network: Network; fn network_name() -> &'static str; fn send_horizontal_messages)>>( to_para_id: u32, iter: I, ) { HORIZONTAL_MESSAGES.with(|b| { b.borrow_mut() .get_mut(Self::network_name()) .unwrap() .push_back((to_para_id, iter.collect())) }); } fn send_upward_message(from_para_id: u32, msg: Vec) { UPWARD_MESSAGES.with(|b| { b.borrow_mut() .get_mut(Self::network_name()) .unwrap() .push_back((from_para_id, msg)) }); } fn send_downward_messages( to_para_id: u32, iter: impl Iterator)>, ) { DOWNWARD_MESSAGES.with(|b| { b.borrow_mut() .get_mut(Self::network_name()) .unwrap() .push_back((to_para_id, iter.collect())) }); } fn send_bridged_messages(msg: BridgeMessage) { BRIDGED_MESSAGES .with(|b| b.borrow_mut().get_mut(Self::network_name()).unwrap().push_back(msg)); } } pub trait RelayChain: ProcessMessage { type Runtime; type RuntimeOrigin; type RuntimeCall; type RuntimeEvent; type XcmConfig; type SovereignAccountOf; type System; type Balances; } pub trait Parachain: XcmpMessageHandler + DmpMessageHandler { type Runtime; type RuntimeOrigin; type RuntimeCall; type RuntimeEvent; type XcmpMessageHandler; type DmpMessageHandler; type LocationToAccountId; type System; type Balances; type ParachainSystem; type ParachainInfo; } pub trait Bridge { type Source: TestExt; type Target: TestExt; type Handler: BridgeMessageHandler; fn init(); } impl Bridge for () { type Source = (); type Target = (); type Handler = (); fn init() {} } #[derive(Clone, Default, Debug)] pub struct BridgeMessage { pub id: u32, pub nonce: u64, pub payload: Vec, } pub trait BridgeMessageHandler { fn get_source_outbound_messages() -> Vec; fn dispatch_target_inbound_message( message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError>; fn notify_source_message_delivery(lane_id: u32); } impl BridgeMessageHandler for () { fn get_source_outbound_messages() -> Vec { Default::default() } fn dispatch_target_inbound_message( _message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError> { Err(BridgeMessageDispatchError(Box::new("Not a bridge"))) } fn notify_source_message_delivery(_lane_id: u32) {} } #[derive(Debug)] pub struct BridgeMessageDispatchError(pub Box); impl Error for BridgeMessageDispatchError {} impl fmt::Display for BridgeMessageDispatchError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) } } // Relay Chain Implementation #[macro_export] macro_rules! decl_test_relay_chains { ( $( #[api_version($api_version:tt)] pub struct $name:ident { genesis = $genesis:expr, on_init = $on_init:expr, runtime = { Runtime: $runtime:path, RuntimeOrigin: $runtime_origin:path, RuntimeCall: $runtime_call:path, RuntimeEvent: $runtime_event:path, MessageQueue: $mq:path, XcmConfig: $xcm_config:path, SovereignAccountOf: $sovereign_acc_of:path, System: $system:path, Balances: $balances:path, }, pallets_extra = { $($pallet_name:ident: $pallet_path:path,)* } } ), + ) => { $( pub struct $name; impl RelayChain for $name { type Runtime = $runtime; type RuntimeOrigin = $runtime_origin; type RuntimeCall = $runtime_call; type RuntimeEvent = $runtime_event; type XcmConfig = $xcm_config; type SovereignAccountOf = $sovereign_acc_of; type System = $system; type Balances = $balances; } $crate::paste::paste! { pub trait [<$name Pallet>] { $( type $pallet_name; )? } impl [<$name Pallet>] for $name { $( type $pallet_name = $pallet_path; )? } } impl $crate::ProcessMessage for $name { type Origin = $crate::ParaId; fn process_message( msg: &[u8], para: Self::Origin, meter: &mut $crate::WeightMeter, _id: &mut XcmHash ) -> Result { use $crate::{Weight, AggregateMessageOrigin, UmpQueueId, ServiceQueues, EnqueueMessage}; use $mq as message_queue; use $runtime_event as runtime_event; Self::ext_wrapper(|| { <$mq as EnqueueMessage>::enqueue_message( msg.try_into().expect("Message too long"), AggregateMessageOrigin::Ump(UmpQueueId::Para(para.clone())) ); <$system>::reset_events(); <$mq as ServiceQueues>::service_queues(Weight::MAX); let events = <$system>::events(); let event = events.last().expect("There must be at least one event"); match &event.event { runtime_event::MessageQueue( $crate::pallet_message_queue::Event::Processed {origin, ..}) => { assert_eq!(origin, &AggregateMessageOrigin::Ump(UmpQueueId::Para(para))); }, event => panic!("Unexpected event: {:#?}", event), } Ok(true) }) } } $crate::__impl_test_ext_for_relay_chain!($name, $genesis, $on_init, $api_version); )+ }; } #[macro_export] macro_rules! __impl_test_ext_for_relay_chain { // entry point: generate ext name ($name:ident, $genesis:expr, $on_init:expr, $api_version:tt) => { $crate::paste::paste! { $crate::__impl_test_ext_for_relay_chain!(@impl $name, $genesis, $on_init, [], []); } }; // impl (@impl $name:ident, $genesis:expr, $on_init:expr, $api_version:ident, $ext_name:ident) => { thread_local! { pub static $ext_name: $crate::RefCell<$crate::sp_io::TestExternalities> = $crate::RefCell::new(<$name>::build_new_ext($genesis)); } impl TestExt for $name { fn build_new_ext(storage: $crate::Storage) -> $crate::sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { #[allow(clippy::no_effect)] $on_init; sp_tracing::try_init_simple(); ::System::set_block_number(1); }); ext } fn new_ext() -> $crate::sp_io::TestExternalities { <$name>::build_new_ext($genesis) } fn reset_ext() { $ext_name.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis)); } fn execute_with(execute: impl FnOnce() -> R) -> R { use $crate::{NetworkComponent, Network}; // Make sure the Network is initialized <$name as NetworkComponent>::Network::init(); let r = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); // send messages if needed $ext_name.with(|v| { v.borrow_mut().execute_with(|| { use $crate::polkadot_primitives::runtime_api::runtime_decl_for_parachain_host::$api_version; //TODO: mark sent count & filter out sent msg for para_id in<$name as NetworkComponent>::Network::para_ids() { // downward messages let downward_messages = ::Runtime::dmq_contents(para_id.into()) .into_iter() .map(|inbound| (inbound.sent_at, inbound.msg)); if downward_messages.len() == 0 { continue; } <$name>::send_downward_messages(para_id, downward_messages.into_iter()); // Note: no need to handle horizontal messages, as the // simulator directly sends them to dest (not relayed). } }) }); <$name as NetworkComponent>::Network::process_messages(); r } fn ext_wrapper(func: impl FnOnce() -> R) -> R { $ext_name.with(|v| { v.borrow_mut().execute_with(|| { func() }) }) } } }; } #[macro_export] macro_rules! __impl_relay { ($network:ident, $relay_chain:ty) => { impl $crate::NetworkComponent for $relay_chain { type Network = $network; fn network_name() -> &'static str { stringify!($network) } } impl $relay_chain { pub fn child_location_of(id: $crate::ParaId) -> MultiLocation { (Ancestor(0), Parachain(id.into())).into() } pub fn account_id_of(seed: &str) -> $crate::AccountId { $crate::get_account_id_from_seed::(seed) } pub fn account_data_of(account: AccountId) -> $crate::AccountData { Self::ext_wrapper(|| ::System::account(account).data) } pub fn sovereign_account_id_of(location: $crate::MultiLocation) -> $crate::AccountId { ::SovereignAccountOf::convert_location(&location).unwrap() } pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) { Self::ext_wrapper(|| { for account in accounts { let _ = ::Balances::force_set_balance( ::RuntimeOrigin::root(), account.0.into(), account.1.into(), ); } }); } pub fn events() -> Vec<::RuntimeEvent> { ::System::events() .iter() .map(|record| record.event.clone()) .collect() } } }; } // Parachain Implementation #[macro_export] macro_rules! decl_test_parachains { ( $( pub struct $name:ident { genesis = $genesis:expr, on_init = $on_init:expr, runtime = { Runtime: $runtime:path, RuntimeOrigin: $runtime_origin:path, RuntimeCall: $runtime_call:path, RuntimeEvent: $runtime_event:path, XcmpMessageHandler: $xcmp_message_handler:path, DmpMessageHandler: $dmp_message_handler:path, LocationToAccountId: $location_to_account:path, System: $system:path, Balances: $balances_pallet:path, ParachainSystem: $parachain_system:path, ParachainInfo: $parachain_info:path, }, pallets_extra = { $($pallet_name:ident: $pallet_path:path,)* } } ), + ) => { $( pub struct $name; impl Parachain for $name { type Runtime = $runtime; type RuntimeOrigin = $runtime_origin; type RuntimeCall = $runtime_call; type RuntimeEvent = $runtime_event; type XcmpMessageHandler = $xcmp_message_handler; type DmpMessageHandler = $dmp_message_handler; type LocationToAccountId = $location_to_account; type System = $system; type Balances = $balances_pallet; type ParachainSystem = $parachain_system; type ParachainInfo = $parachain_info; } $crate::paste::paste! { pub trait [<$name Pallet>] { $( type $pallet_name; )* } impl [<$name Pallet>] for $name { $( type $pallet_name = $pallet_path; )* } } $crate::__impl_xcm_handlers_for_parachain!($name); $crate::__impl_test_ext_for_parachain!($name, $genesis, $on_init); )+ }; } #[macro_export] macro_rules! __impl_xcm_handlers_for_parachain { ($name:ident) => { impl $crate::XcmpMessageHandler for $name { fn handle_xcmp_messages< 'a, I: Iterator, >( iter: I, max_weight: $crate::Weight, ) -> $crate::Weight { use $crate::{TestExt, XcmpMessageHandler}; $name::ext_wrapper(|| { ::XcmpMessageHandler::handle_xcmp_messages(iter, max_weight) }) } } impl $crate::DmpMessageHandler for $name { fn handle_dmp_messages( iter: impl Iterator)>, max_weight: $crate::Weight, ) -> $crate::Weight { use $crate::{DmpMessageHandler, TestExt}; $name::ext_wrapper(|| { ::DmpMessageHandler::handle_dmp_messages(iter, max_weight) }) } } }; } #[macro_export] macro_rules! __impl_test_ext_for_parachain { // entry point: generate ext name ($name:ident, $genesis:expr, $on_init:expr) => { $crate::paste::paste! { $crate::__impl_test_ext_for_parachain!(@impl $name, $genesis, $on_init, []); } }; // impl (@impl $name:ident, $genesis:expr, $on_init:expr, $ext_name:ident) => { thread_local! { pub static $ext_name: $crate::RefCell<$crate::sp_io::TestExternalities> = $crate::RefCell::new(<$name>::build_new_ext($genesis)); } impl TestExt for $name { fn build_new_ext(storage: $crate::Storage) -> $crate::sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(storage); ext.execute_with(|| { #[allow(clippy::no_effect)] $on_init; sp_tracing::try_init_simple(); ::System::set_block_number(1); }); ext } fn new_ext() -> $crate::sp_io::TestExternalities { <$name>::build_new_ext($genesis) } fn reset_ext() { $ext_name.with(|v| *v.borrow_mut() = <$name>::build_new_ext($genesis)); } fn execute_with(execute: impl FnOnce() -> R) -> R { use $crate::{Get, Hooks, NetworkComponent, Network, Bridge}; // Make sure the Network is initialized <$name as NetworkComponent>::Network::init(); let mut relay_block_number = <$name as NetworkComponent>::Network::relay_block_number(); relay_block_number += 1; <$name as NetworkComponent>::Network::set_relay_block_number(relay_block_number); let para_id = <$name>::para_id().into(); $ext_name.with(|v| { v.borrow_mut().execute_with(|| { // Make sure it has been recorded properly let relay_block_number = <$name as NetworkComponent>::Network::relay_block_number(); let _ = ::ParachainSystem::set_validation_data( ::RuntimeOrigin::none(), <$name as NetworkComponent>::Network::hrmp_channel_parachain_inherent_data(para_id, relay_block_number), ); }) }); let r = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); // send messages if needed $ext_name.with(|v| { v.borrow_mut().execute_with(|| { use sp_runtime::traits::Header as HeaderT; let block_number = ::System::block_number(); let mock_header = HeaderT::new( 0, Default::default(), Default::default(), Default::default(), Default::default(), ); // get xcmp messages ::ParachainSystem::on_finalize(block_number); let collation_info = ::ParachainSystem::collect_collation_info(&mock_header); // send upward messages let relay_block_number = <$name as NetworkComponent>::Network::relay_block_number(); for msg in collation_info.upward_messages.clone() { <$name>::send_upward_message(para_id, msg); } // send horizontal messages for msg in collation_info.horizontal_messages { <$name>::send_horizontal_messages( msg.recipient.into(), vec![(para_id.into(), relay_block_number, msg.data)].into_iter(), ); } // get bridge messages type NetworkBridge = <<$name as NetworkComponent>::Network as Network>::Bridge; let bridge_messages = ::Handler::get_source_outbound_messages(); // send bridged messages for msg in bridge_messages { <$name>::send_bridged_messages(msg); } // clean messages ::ParachainSystem::on_initialize(block_number); }) }); <$name as NetworkComponent>::Network::process_messages(); r } fn ext_wrapper(func: impl FnOnce() -> R) -> R { $ext_name.with(|v| { v.borrow_mut().execute_with(|| { func() }) }) } } }; } #[macro_export] macro_rules! __impl_parachain { ($network:ident, $parachain:ty) => { impl $crate::NetworkComponent for $parachain { type Network = $network; fn network_name() -> &'static str { stringify!($network) } } impl $parachain { pub fn para_id() -> $crate::ParaId { Self::ext_wrapper(|| ::ParachainInfo::get()) } pub fn parent_location() -> $crate::MultiLocation { (Parent).into() } pub fn sibling_location_of(para_id: $crate::ParaId) -> $crate::MultiLocation { (Parent, X1(Parachain(para_id.into()))).into() } pub fn account_id_of(seed: &str) -> $crate::AccountId { $crate::get_account_id_from_seed::(seed) } pub fn account_data_of(account: AccountId) -> $crate::AccountData { Self::ext_wrapper(|| ::System::account(account).data) } pub fn sovereign_account_id_of(location: $crate::MultiLocation) -> $crate::AccountId { ::LocationToAccountId::convert_location(&location).unwrap() } pub fn fund_accounts(accounts: Vec<(AccountId, Balance)>) { Self::ext_wrapper(|| { for account in accounts { let _ = ::Balances::force_set_balance( ::RuntimeOrigin::root(), account.0.into(), account.1.into(), ); } }); } pub fn events() -> Vec<::RuntimeEvent> { ::System::events() .iter() .map(|record| record.event.clone()) .collect() } fn prepare_for_xcmp() { use $crate::{Network, NetworkComponent}; let para_id = Self::para_id(); ::ext_wrapper(|| { use $crate::{Get, Hooks}; let block_number = ::System::block_number(); let _ = ::ParachainSystem::set_validation_data( ::RuntimeOrigin::none(), ::Network::hrmp_channel_parachain_inherent_data( para_id.into(), 1, ), ); // set `AnnouncedHrmpMessagesPerCandidate` ::ParachainSystem::on_initialize(block_number); }); } } }; } // Network Implementation #[macro_export] macro_rules! decl_test_networks { ( $( pub struct $name:ident { relay_chain = $relay_chain:ty, parachains = vec![ $( $parachain:ty, )* ], bridge = $bridge:ty } ), + ) => { $( pub struct $name; impl $name { pub fn reset() { use $crate::{TestExt, VecDeque}; $crate::INITIALIZED.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::DMP_DONE.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().remove(stringify!($name))); $crate::RELAY_BLOCK_NUMBER.with(|b| b.borrow_mut().remove(stringify!($name))); <$relay_chain>::reset_ext(); $( <$parachain>::reset_ext(); )* } } impl $crate::Network for $name { type Bridge = $bridge; fn init() { // If Network has not been itialized yet, it gets initialized if $crate::INITIALIZED.with(|b| b.borrow_mut().get(stringify!($name)).is_none()) { $crate::INITIALIZED.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), true)); $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new())); $crate::DMP_DONE.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new())); $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new())); $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new())); $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), $crate::VecDeque::new())); $crate::RELAY_BLOCK_NUMBER.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), 1)); $crate::PARA_IDS.with(|b| b.borrow_mut().insert(stringify!($name).to_string(), Self::para_ids())); $( <$parachain>::prepare_for_xcmp(); )* } } fn para_ids() -> Vec { vec![$( <$parachain>::para_id().into(), )*] } fn relay_block_number() -> u32 { $crate::RELAY_BLOCK_NUMBER.with(|v| *v.clone().borrow().get(stringify!($name)).unwrap()) } fn set_relay_block_number(block_number: u32) { $crate::RELAY_BLOCK_NUMBER.with(|v| v.borrow_mut().insert(stringify!($name).to_string(), block_number)); } fn process_messages() { while Self::has_unprocessed_messages() { Self::process_upward_messages(); Self::process_horizontal_messages(); Self::process_downward_messages(); Self::process_bridged_messages(); } } fn has_unprocessed_messages() -> bool { $crate::DOWNWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty()) || $crate::HORIZONTAL_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty()) || $crate::UPWARD_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty()) || $crate::BRIDGED_MESSAGES.with(|b| !b.borrow_mut().get_mut(stringify!($name)).unwrap().is_empty()) } fn process_downward_messages() { use $crate::{DmpMessageHandler, Bounded}; use polkadot_parachain::primitives::RelayChainBlockNumber; while let Some((to_para_id, messages)) = $crate::DOWNWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) { $( let para_id: u32 = <$parachain>::para_id().into(); if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().contains(&to_para_id)) && para_id == to_para_id { let mut msg_dedup: Vec<(RelayChainBlockNumber, Vec)> = Vec::new(); for m in &messages { msg_dedup.push((m.0, m.1.clone())); } msg_dedup.dedup(); let msgs = msg_dedup.clone().into_iter().filter(|m| { !$crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap_or(&mut $crate::VecDeque::new()).contains(&(to_para_id, m.0, m.1.clone()))) }).collect::)>>(); if msgs.len() != 0 { <$parachain>::handle_dmp_messages(msgs.clone().into_iter(), $crate::Weight::max_value()); $crate::log::debug!(target: concat!("dmp::", stringify!($name)) , "DMP messages processed {:?} to para_id {:?}", msgs.clone(), &to_para_id); for m in msgs { $crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().push_back((to_para_id, m.0, m.1))); } } } )* } } fn process_horizontal_messages() { use $crate::{XcmpMessageHandler, Bounded}; while let Some((to_para_id, messages)) = $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) { let iter = messages.iter().map(|(p, b, m)| (*p, *b, &m[..])).collect::>().into_iter(); $( let para_id: u32 = <$parachain>::para_id().into(); if $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().contains(&to_para_id)) && para_id == to_para_id { <$parachain>::handle_xcmp_messages(iter.clone(), $crate::Weight::max_value()); $crate::log::debug!(target: concat!("hrmp::", stringify!($name)) , "HRMP messages processed {:?} to para_id {:?}", &messages, &to_para_id); } )* } } fn process_upward_messages() { use $crate::{Bounded, ProcessMessage, WeightMeter}; use sp_core::Encode; while let Some((from_para_id, msg)) = $crate::UPWARD_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) { let mut weight_meter = WeightMeter::max_limit(); let _ = <$relay_chain>::process_message( &msg[..], from_para_id.into(), &mut weight_meter, &mut msg.using_encoded(sp_core::blake2_256), ); $crate::log::debug!(target: concat!("ump::", stringify!($name)) , "Upward message processed {:?} from para_id {:?}", &msg, &from_para_id); } } fn process_bridged_messages() { use $crate::Bridge; // Make sure both, including the target `Network` are initialized ::init(); while let Some(msg) = $crate::BRIDGED_MESSAGES.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().pop_front()) { let dispatch_result = <::Target as TestExt>::ext_wrapper(|| { <::Handler as BridgeMessageHandler>::dispatch_target_inbound_message(msg.clone()) }); match dispatch_result { Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg.clone()), Ok(()) => { <::Source as TestExt>::ext_wrapper(|| { <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.id); }); $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg.clone()); } } } } fn hrmp_channel_parachain_inherent_data( para_id: u32, relay_parent_number: u32, ) -> $crate::ParachainInherentData { use $crate::cumulus_primitives_core::{relay_chain::HrmpChannelId, AbridgedHrmpChannel}; let mut sproof = $crate::RelayStateSproofBuilder::default(); sproof.para_id = para_id.into(); // egress channel let e_index = sproof.hrmp_egress_channel_index.get_or_insert_with(Vec::new); for recipient_para_id in $crate::PARA_IDS.with(|b| b.borrow_mut().get_mut(stringify!($name)).unwrap().clone()) { let recipient_para_id = $crate::ParaId::from(recipient_para_id); if let Err(idx) = e_index.binary_search(&recipient_para_id) { e_index.insert(idx, recipient_para_id); } sproof .hrmp_channels .entry(HrmpChannelId { sender: sproof.para_id, recipient: recipient_para_id, }) .or_insert_with(|| AbridgedHrmpChannel { max_capacity: 1024, max_total_size: 1024 * 1024, max_message_size: 1024 * 1024, msg_count: 0, total_size: 0, mqc_head: Option::None, }); } let (relay_storage_root, proof) = sproof.into_state_root_and_proof(); $crate::ParachainInherentData { validation_data: $crate::PersistedValidationData { parent_head: Default::default(), relay_parent_number, relay_parent_storage_root: relay_storage_root, max_pov_size: Default::default(), }, relay_chain_state: proof, downward_messages: Default::default(), horizontal_messages: Default::default(), } } } $crate::__impl_relay!($name, $relay_chain); $( $crate::__impl_parachain!($name, $parachain); )* )+ }; } #[macro_export] macro_rules! decl_test_bridges { ( $( pub struct $name:ident { source = $source:ty, target = $target:ty, handler = $handler:ty } ), + ) => { $( #[derive(Debug)] pub struct $name; impl $crate::Bridge for $name { type Source = $source; type Target = $target; type Handler = $handler; fn init() { use $crate::{NetworkComponent, Network}; // Make sure source and target `Network` has been initialized <$source as NetworkComponent>::Network::init(); <$target as NetworkComponent>::Network::init(); } } )+ }; } #[macro_export] macro_rules! assert_expected_events { ( $chain:ident, vec![$( $event_pat:pat => { $($attr:ident : $condition:expr, )* }, )*] ) => { let mut message: Vec = Vec::new(); $( let mut meet_conditions = true; let mut event_message: Vec = Vec::new(); let event_received = <$chain>::events().iter().any(|event| { $crate::log::debug!(target: concat!("events::", stringify!($chain)), "{:?}", event); match event { $event_pat => { $( if !$condition { event_message.push(format!(" - The attribute {:?} = {:?} did not met the condition {:?}\n", stringify!($attr), $attr, stringify!($condition))); meet_conditions &= $condition } )* true }, _ => false } }); if event_received && !meet_conditions { message.push(format!("\n\nEvent \x1b[31m{}\x1b[0m was received but some of its attributes did not meet the conditions:\n{}", stringify!($event_pat), event_message.concat())); } else if !event_received { message.push(format!("\n\nEvent \x1b[31m{}\x1b[0m was never received", stringify!($event_pat))); } )* if !message.is_empty() { panic!("{}", message.concat()) } } } #[macro_export] macro_rules! bx { ($e:expr) => { Box::new($e) }; } #[macro_export] macro_rules! decl_test_sender_receiver_accounts_parameter_types { ( $( $chain:ident { sender: $sender:expr, receiver: $receiver:expr }),+ ) => { $crate::paste::paste! { parameter_types! { $( pub [<$chain Sender>]: $crate::AccountId = <$chain>::account_id_of($sender); pub [<$chain Receiver>]: $crate::AccountId = <$chain>::account_id_of($receiver); )+ } } }; } pub mod helpers { use super::Weight; pub fn within_threshold(threshold: u64, expected_value: u64, current_value: u64) -> bool { let margin = (current_value * threshold) / 100; let lower_limit = expected_value - margin; let upper_limit = expected_value + margin; current_value >= lower_limit && current_value <= upper_limit } pub fn weight_within_threshold( (threshold_time, threshold_size): (u64, u64), expected_weight: Weight, weight: Weight, ) -> bool { let ref_time_within = within_threshold(threshold_time, expected_weight.ref_time(), weight.ref_time()); let proof_size_within = within_threshold(threshold_size, expected_weight.proof_size(), weight.proof_size()); ref_time_within && proof_size_within } }