From 3cd312fc72475529620d100ef3590931ecadbb0d Mon Sep 17 00:00:00 2001 From: Diego Date: Mon, 2 Dec 2024 19:39:21 +0900 Subject: [PATCH] Configure dancelight xcm mocknets --- .../dancelight/src/tests/common/mod.rs | 22 ++ .../common/xcm/expected_event_checker.rs | 103 ++++++++ .../src/tests/common/xcm/mocknets.rs | 224 ++++++++++++++++++ .../dancelight/src/tests/common/xcm/mod.rs | 2 + .../runtime/dancelight/src/xcm_config.rs | 26 +- 5 files changed, 373 insertions(+), 4 deletions(-) create mode 100644 solo-chains/runtime/dancelight/src/tests/common/xcm/expected_event_checker.rs create mode 100644 solo-chains/runtime/dancelight/src/tests/common/xcm/mocknets.rs diff --git a/solo-chains/runtime/dancelight/src/tests/common/mod.rs b/solo-chains/runtime/dancelight/src/tests/common/mod.rs index 618cd2af3..40a186899 100644 --- a/solo-chains/runtime/dancelight/src/tests/common/mod.rs +++ b/solo-chains/runtime/dancelight/src/tests/common/mod.rs @@ -331,6 +331,7 @@ pub struct ExtBuilder { own_para_id: Option, next_free_para_id: ParaId, keystore: Option, + safe_xcm_version: Option, } impl Default for ExtBuilder { @@ -360,6 +361,7 @@ impl Default for ExtBuilder { own_para_id: Default::default(), next_free_para_id: Default::default(), keystore: None, + safe_xcm_version: Default::default(), } } } @@ -430,6 +432,11 @@ impl ExtBuilder { self } + pub fn with_safe_xcm_version(mut self, safe_xcm_version: u32) -> Self { + self.safe_xcm_version = Some(safe_xcm_version); + self + } + // Maybe change to with_collators_config? pub fn with_relay_config( mut self, @@ -550,6 +557,13 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); + pallet_xcm::GenesisConfig:: { + safe_xcm_version: self.safe_xcm_version, + ..Default::default() + } + .assimilate_storage(&mut t) + .unwrap(); + runtime_parachains::configuration::GenesisConfig:: { config: self.relay_config, } @@ -658,6 +672,13 @@ impl ExtBuilder { pallet_sudo::GenesisConfig:: { key: self.sudo } .assimilate_storage(&mut t) .unwrap(); + + if self.safe_xcm_version.is_some() { + // Disable run_block checks in XCM tests, because the XCM emulator runs on_initialize and + // on_finalize automatically + t.top.insert(b"__mock_is_xcm_test".to_vec(), b"1".to_vec()); + } + t } @@ -735,6 +756,7 @@ pub const CHARLIE: [u8; 32] = [6u8; 32]; pub const DAVE: [u8; 32] = [7u8; 32]; pub const EVE: [u8; 32] = [8u8; 32]; pub const FERDIE: [u8; 32] = [9u8; 32]; +pub const RANDOM: [u8; 32] = [3u8; 32]; fn take_new_inherent_data() -> Option { let data: Option = diff --git a/solo-chains/runtime/dancelight/src/tests/common/xcm/expected_event_checker.rs b/solo-chains/runtime/dancelight/src/tests/common/xcm/expected_event_checker.rs new file mode 100644 index 000000000..8d387aef7 --- /dev/null +++ b/solo-chains/runtime/dancelight/src/tests/common/xcm/expected_event_checker.rs @@ -0,0 +1,103 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see + +#[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 events = <$chain as xcm_emulator::Chain>::events(); + + $( + let mut event_received = false; + let mut meet_conditions = true; + let mut index_match = 0; + let mut event_message: Vec = Vec::new(); + + for (index, event) in events.iter().enumerate() { + // Variable to record current event's meet conditions + #[allow(unused_mut)] // To suppress warning in case no conditions are declared + let mut current_event_meet_conditions = true; + match event { + $event_pat => { + event_received = true; + + #[allow(unused_mut)] // To suppress warning in case no conditions are declared + let mut conditions_message: Vec = Vec::new(); + + $( + // We only want to record condition error messages in case it did not happened before + // Only the first partial match is recorded + if !$condition && event_message.is_empty() { + conditions_message.push( + format!( + " - The attribute {:?} = {:?} did not met the condition {:?}\n", + stringify!($attr), + $attr, + stringify!($condition) + ) + ); + } + current_event_meet_conditions &= $condition; + )* + + // Set the variable to latest matched event's condition evaluation result + meet_conditions = current_event_meet_conditions; + + // Set the index where we found a perfect match + if event_received && meet_conditions { + index_match = index; + break; + } else { + event_message.extend(conditions_message); + } + }, + _ => {} + } + } + + if event_received && !meet_conditions { + message.push( + format!( + "\n\n{}::\x1b[31m{}\x1b[0m was received but some of its attributes did not meet the conditions:\n{}", + stringify!($chain), + stringify!($event_pat), + event_message.concat() + ) + ); + } else if !event_received { + message.push( + format!( + "\n\n{}::\x1b[31m{}\x1b[0m was never received. All events:\n{:#?}", + stringify!($chain), + stringify!($event_pat), + <$chain as xcm_emulator::Chain>::events(), + ) + ); + } else { + // If we find a perfect match we remove the event to avoid being potentially assessed multiple times + events.remove(index_match); + } + )* + + if !message.is_empty() { + // Log events as they will not be logged after the panic + <$chain as xcm_emulator::Chain>::events().iter().for_each(|event| { + log::debug!(target: concat!("events::", stringify!($chain)), "{:?}", event); + }); + panic!("{}", message.concat()) + } + } +} diff --git a/solo-chains/runtime/dancelight/src/tests/common/xcm/mocknets.rs b/solo-chains/runtime/dancelight/src/tests/common/xcm/mocknets.rs new file mode 100644 index 000000000..70429b10f --- /dev/null +++ b/solo-chains/runtime/dancelight/src/tests/common/xcm/mocknets.rs @@ -0,0 +1,224 @@ +// Copyright (C) Moondance Labs Ltd. +// This file is part of Tanssi. + +// Tanssi 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. + +// Tanssi 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 Tanssi. If not, see +pub use sp_core::Get; +use { + super::constants::{ + accounts::{ALICE, BOB, RANDOM}, + frontier_template, rococo, simple_template, westend, + }, + crate::tests::common::ExtBuilder, + emulated_integration_tests_common::{ + impl_assert_events_helpers_for_parachain, xcm_emulator::decl_test_parachains, + }, + frame_support::parameter_types, + xcm_emulator::{decl_test_networks, decl_test_relay_chains, Chain}, +}; + +decl_test_relay_chains! { + #[api_version(11)] + pub struct Westend { + genesis = westend::genesis(), + on_init = (), + runtime = westend_runtime, + core = { + SovereignAccountOf: westend_runtime::xcm_config::LocationConverter, + }, + pallets = { + System: westend_runtime::System, + Balances: westend_runtime::Balances, + XcmPallet: westend_runtime::XcmPallet, + Sudo: westend_runtime::Sudo, + } + }, + #[api_version(11)] + pub struct Rococo { + genesis = rococo::genesis(), + on_init = (), + runtime = rococo_runtime, + core = { + SovereignAccountOf: rococo_runtime::xcm_config::LocationConverter, + }, + pallets = { + System: rococo_runtime::System, + Session: rococo_runtime::Session, + Configuration: rococo_runtime::Configuration, + Balances: rococo_runtime::Balances, + Registrar: rococo_runtime::Registrar, + ParasSudoWrapper: rococo_runtime::ParasSudoWrapper, + OnDemandAssignmentProvider: rococo_runtime::OnDemandAssignmentProvider, + XcmPallet: rococo_runtime::XcmPallet, + Sudo: rococo_runtime::Sudo, + } + }, + #[api_version(11)] + pub struct Dancelight { + genesis = ExtBuilder::default() + .with_balances(vec![ + // Alice gets 10k extra tokens for her mapping deposit + (crate::AccountId::from(crate::tests::common::ALICE), 210_000 * dancelight_runtime_constants::currency::UNITS), + (crate::AccountId::from(crate::tests::common::BOB), 100_000 * dancelight_runtime_constants::currency::UNITS), + ]) + .with_safe_xcm_version(3) + .build_storage(), + on_init = (), + runtime = crate, + core = { + SovereignAccountOf: crate::xcm_config::LocationConverter, + }, + pallets = { + System: crate::System, + Session: crate::Session, + Configuration: crate::Configuration, + Balances: crate::Balances, + Registrar: crate::Registrar, + ParasSudoWrapper: crate::ParasSudoWrapper, + OnDemandAssignmentProvider: crate::OnDemandAssignmentProvider, + XcmPallet: crate::XcmPallet, + Sudo: crate::Sudo, + } + } +} + +decl_test_parachains! { + pub struct FrontierTemplate { + genesis = frontier_template::genesis(), + on_init = (), + runtime = container_chain_template_frontier_runtime, + core = { + XcmpMessageHandler: container_chain_template_frontier_runtime::XcmpQueue, + LocationToAccountId: container_chain_template_frontier_runtime::xcm_config::LocationToAccountId, + ParachainInfo: container_chain_template_frontier_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + System: container_chain_template_frontier_runtime::System, + Balances: container_chain_template_frontier_runtime::Balances, + ParachainSystem: container_chain_template_frontier_runtime::ParachainSystem, + PolkadotXcm: container_chain_template_frontier_runtime::PolkadotXcm, + ForeignAssets: container_chain_template_frontier_runtime::ForeignAssets, + AssetRate: container_chain_template_frontier_runtime::AssetRate, + ForeignAssetsCreator: container_chain_template_frontier_runtime::ForeignAssetsCreator, + } + }, + pub struct SimpleTemplate { + genesis = simple_template::genesis(), + on_init = (), + runtime = container_chain_template_simple_runtime, + core = { + XcmpMessageHandler: container_chain_template_simple_runtime::XcmpQueue, + LocationToAccountId: container_chain_template_simple_runtime::xcm_config::LocationToAccountId, + ParachainInfo: container_chain_template_simple_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + System: container_chain_template_simple_runtime::System, + Balances: container_chain_template_simple_runtime::Balances, + ParachainSystem: container_chain_template_simple_runtime::ParachainSystem, + PolkadotXcm: container_chain_template_simple_runtime::PolkadotXcm, + ForeignAssets: container_chain_template_simple_runtime::ForeignAssets, + AssetRate: container_chain_template_simple_runtime::AssetRate, + ForeignAssetsCreator: container_chain_template_simple_runtime::ForeignAssetsCreator, + } + }, + pub struct FrontierTemplateRococo { + genesis = frontier_template::genesis(), + on_init = (), + runtime = container_chain_template_frontier_runtime, + core = { + XcmpMessageHandler: container_chain_template_frontier_runtime::XcmpQueue, + LocationToAccountId: container_chain_template_frontier_runtime::xcm_config::LocationToAccountId, + ParachainInfo: container_chain_template_frontier_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + System: container_chain_template_frontier_runtime::System, + Balances: container_chain_template_frontier_runtime::Balances, + ParachainSystem: container_chain_template_frontier_runtime::ParachainSystem, + PolkadotXcm: container_chain_template_frontier_runtime::PolkadotXcm, + ForeignAssets: container_chain_template_frontier_runtime::ForeignAssets, + AssetRate: container_chain_template_frontier_runtime::AssetRate, + ForeignAssetsCreator: container_chain_template_frontier_runtime::ForeignAssetsCreator, + } + }, + pub struct SimpleTemplateRococo { + genesis = simple_template::genesis(), + on_init = (), + runtime = container_chain_template_simple_runtime, + core = { + XcmpMessageHandler: container_chain_template_simple_runtime::XcmpQueue, + LocationToAccountId: container_chain_template_simple_runtime::xcm_config::LocationToAccountId, + ParachainInfo: container_chain_template_simple_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + System: container_chain_template_simple_runtime::System, + Balances: container_chain_template_simple_runtime::Balances, + ParachainSystem: container_chain_template_simple_runtime::ParachainSystem, + PolkadotXcm: container_chain_template_simple_runtime::PolkadotXcm, + ForeignAssets: container_chain_template_simple_runtime::ForeignAssets, + AssetRate: container_chain_template_simple_runtime::AssetRate, + ForeignAssetsCreator: container_chain_template_simple_runtime::ForeignAssetsCreator, + } + } +} + +impl_assert_events_helpers_for_parachain!(FrontierTemplate); +impl_assert_events_helpers_for_parachain!(SimpleTemplate); + +decl_test_networks! { + pub struct WestendMockNet { + relay_chain = Westend, + parachains = vec![ + FrontierTemplate, + SimpleTemplate, + ], + bridge = () + }, + pub struct RococoMockNet { + relay_chain = Rococo, + parachains = vec![ + FrontierTemplateRococo, + SimpleTemplateRococo, + ], + bridge = () + } +} + +parameter_types! { + // Westend + pub WestendSender: cumulus_primitives_core::relay_chain::AccountId = WestendRelay::account_id_of(ALICE); + pub WestendReceiver: cumulus_primitives_core::relay_chain::AccountId = WestendRelay::account_id_of(BOB); + pub WestendEmptyReceiver: cumulus_primitives_core::relay_chain::AccountId = WestendRelay::account_id_of(RANDOM); + + // Rococo + pub RococoSender: cumulus_primitives_core::relay_chain::AccountId = RococoRelay::account_id_of(ALICE); + pub RococoReceiver: cumulus_primitives_core::relay_chain::AccountId = RococoRelay::account_id_of(BOB); + pub RococoEmptyReceiver: cumulus_primitives_core::relay_chain::AccountId = RococoRelay::account_id_of(RANDOM); + + // Dancelight + pub DancelightSender: crate::AccountId = crate::AccountId::from(crate::tests::common::ALICE); + pub DancelightReceiver: crate::AccountId = crate::AccountId::from(crate::tests::common::BOB); + pub DancelightEmptyReceiver: crate::AccountId = crate::AccountId::from(crate::tests::common::RANDOM); + + // SimpleTemplate + pub SimpleTemplateSender: container_chain_template_simple_runtime::AccountId = SimpleTemplatePara::account_id_of(ALICE); + pub SimpleTemplateReceiver: container_chain_template_simple_runtime::AccountId = SimpleTemplatePara::account_id_of(BOB); + pub SimpleTemplateEmptyReceiver: container_chain_template_simple_runtime::AccountId = SimpleTemplatePara::account_id_of(RANDOM); + + pub EthereumSender: container_chain_template_frontier_runtime::AccountId = frontier_template::pre_funded_accounts()[0]; + pub EthereumReceiver: container_chain_template_frontier_runtime::AccountId = frontier_template::pre_funded_accounts()[1]; + pub EthereumEmptyReceiver: container_chain_template_frontier_runtime::AccountId = [1u8; 20].into(); +} diff --git a/solo-chains/runtime/dancelight/src/tests/common/xcm/mod.rs b/solo-chains/runtime/dancelight/src/tests/common/xcm/mod.rs index b963a9da8..462f34d63 100644 --- a/solo-chains/runtime/dancelight/src/tests/common/xcm/mod.rs +++ b/solo-chains/runtime/dancelight/src/tests/common/xcm/mod.rs @@ -15,3 +15,5 @@ // along with Tanssi. If not, see mod constants; +mod expected_event_checker; +mod mocknets; diff --git a/solo-chains/runtime/dancelight/src/xcm_config.rs b/solo-chains/runtime/dancelight/src/xcm_config.rs index 252abd663..996f91e4f 100644 --- a/solo-chains/runtime/dancelight/src/xcm_config.rs +++ b/solo-chains/runtime/dancelight/src/xcm_config.rs @@ -43,10 +43,10 @@ use { AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, DescribeAllTerminal, DescribeFamily, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsChildSystemParachain, - IsConcrete, MintLocation, OriginToPluralityVoice, SendXcmFeeToAccount, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, - TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, - WithUniqueTopic, XcmFeeManagerFromComponents, + IsConcrete, MintLocation, OriginToPluralityVoice, ParentIsPreset, SendXcmFeeToAccount, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }, xcm_executor::XcmExecutor, }; @@ -137,6 +137,23 @@ impl frame_support::traits::ContainsPair for NativeAssetReserve } } +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // If we receive a Location of type AccountKey20, just generate a native account + AccountId32Aliases, + // Generate remote accounts according to polkadot standards + xcm_builder::HashedDescription< + AccountId, + xcm_builder::DescribeFamily, + >, +); + pub trait Parse { /// Returns the "chain" location part. It could be parent, sibling /// parachain, or child parachain. @@ -192,6 +209,7 @@ parameter_types! { pub StarForBridgeHub: (AssetFilter, Location) = (Star::get(), BridgeHub::get()); pub StarForPeople: (AssetFilter, Location) = (Star::get(), People::get()); pub StarForBroker: (AssetFilter, Location) = (Star::get(), Broker::get()); + pub const RelayNetwork: NetworkId = NetworkId::Westend; pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; }