diff --git a/evm-template/Cargo.lock b/evm-template/Cargo.lock index 75a5e85..9ae4468 100644 --- a/evm-template/Cargo.lock +++ b/evm-template/Cargo.lock @@ -3084,6 +3084,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", + "polkadot-runtime-parachains", "scale-info", "smallvec", "sp-api", @@ -3098,6 +3099,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-std", + "sp-tracing", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -3106,6 +3108,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "xcm-primitives", + "xcm-simulator", ] [[package]] @@ -6748,7 +6751,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 1.1.3", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 2.0.87", @@ -16046,6 +16049,28 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "xcm-simulator" +version = "16.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2407-1#16b0fd09d9e9281c20ee0c1d8b87d011e3e3454e" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "paste", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "xml-rs" version = "0.8.22" diff --git a/evm-template/Cargo.toml b/evm-template/Cargo.toml index 6ef56dc..ce56e0a 100644 --- a/evm-template/Cargo.toml +++ b/evm-template/Cargo.toml @@ -94,6 +94,7 @@ sp-session = { git = "https://github.com/paritytech/polkadot-sdk", default-featu sp-state-machine = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2407-1" } @@ -109,9 +110,11 @@ polkadot-cli = { git = "https://github.com/paritytech/polkadot-sdk", features = polkadot-parachain-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +xcm-simulator = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2407-1" } # Cumulus assets-common = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } diff --git a/evm-template/runtime/Cargo.toml b/evm-template/runtime/Cargo.toml index 2616d39..bba1b6d 100644 --- a/evm-template/runtime/Cargo.toml +++ b/evm-template/runtime/Cargo.toml @@ -112,7 +112,10 @@ xcm-primitives = { workspace = true } [dev-dependencies] +polkadot-runtime-parachains = { workspace = true } sp-io = { workspace = true } +sp-tracing = { workspace = true } +xcm-simulator = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true } diff --git a/evm-template/runtime/tests/xcm_mock/mod.rs b/evm-template/runtime/tests/xcm_mock/mod.rs new file mode 100644 index 0000000..b983bba --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/mod.rs @@ -0,0 +1,125 @@ +pub mod parachain; +pub mod relay_chain; + +use sp_runtime::BuildStorage; +use sp_tracing; +use xcm::prelude::*; +use xcm_executor::traits::ConvertLocation; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +pub const PARA_ALICE: [u8; 20] = [1u8; 20]; +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + ], + } +} + +pub fn parent_account_id() -> parachain::AccountId { + let location = (Parent,); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn child_account_id(para: u32) -> relay_chain::AccountId { + let location = (Parachain(para),); + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn child_account_account_id(para: u32, who: sp_runtime::AccountId32) -> relay_chain::AccountId { + let location = (Parachain(para), AccountId32 { network: None, id: who.into() }); + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn sibling_account_account_id(para: u32, who: sp_runtime::AccountId32) -> parachain::AccountId { + let location = (Parent, Parachain(para), AccountId32 { network: None, id: who.into() }); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn parent_account_account_id(who: sp_runtime::AccountId32) -> parachain::AccountId { + let location = (Parent, AccountId32 { network: None, id: who.into() }); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (PARA_ALICE.into(), INITIAL_BALANCE), + (parent_account_id(), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + sp_tracing::try_init_simple(); + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (child_account_id(1), INITIAL_BALANCE), + (child_account_id(2), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type ParachainPalletXcm = pallet_xcm::Pallet; diff --git a/evm-template/runtime/tests/xcm_mock/parachain/mod.rs b/evm-template/runtime/tests/xcm_mock/parachain/mod.rs new file mode 100644 index 0000000..db8a5ee --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/mod.rs @@ -0,0 +1,104 @@ +mod xcm_config; +use core::marker::PhantomData; + +use evm_runtime_template::configs::xcm_config::SignedToAccountId20; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ConstU128, ContainsPair, Everything, Nothing}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, +}; +use frame_system::EnsureRoot; +use sp_core::ConstU32; +use sp_runtime::traits::{Get, IdentityLookup}; +use xcm::latest::prelude::*; +use xcm_builder::EnsureXcmOrigin; +pub use xcm_config::*; +use xcm_executor::XcmExecutor; +use xcm_simulator::mock_message_queue; + +pub type AccountId = fp_account::AccountId20; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Block = Block; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); +} + +impl mock_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = + SignedToAccountId20; + +pub struct TrustedLockerCase(PhantomData); +impl> ContainsPair for TrustedLockerCase { + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } +} + +parameter_types! { + pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); +} + +pub type TrustedLockers = TrustedLockerCase; + +impl pallet_xcm::Config for Runtime { + type AdminOrigin = EnsureRoot; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = location_converter::LocationConverter; + type TrustedLockers = TrustedLockers; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type WeightInfo = pallet_xcm::TestWeightInfo; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_message_queue, + PolkadotXcm: pallet_xcm, + } +); diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs new file mode 100644 index 0000000..8da002c --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs @@ -0,0 +1,8 @@ +use xcm_builder::{FungibleAdapter, IsConcrete}; + +use crate::parachain::{ + constants::KsmLocation, location_converter::LocationConverter, AccountId, Balances, +}; + +pub type AssetTransactor = + FungibleAdapter, LocationConverter, AccountId, ()>; diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs new file mode 100644 index 0000000..ab45bec --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs @@ -0,0 +1,4 @@ +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom; diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs new file mode 100644 index 0000000..b871586 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs @@ -0,0 +1,16 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_simulator::mock_message_queue::ParachainId; + +use crate::xcm_mock::parachain::Runtime; + +parameter_types! { + pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainId::::get().into())].into(); +} diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs new file mode 100644 index 0000000..8a467d6 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs @@ -0,0 +1,10 @@ +use xcm_builder::{AccountKey20Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +use crate::xcm_mock::parachain::{constants::RelayNetwork, AccountId}; + +type LocationToAccountId = ( + HashedDescription>, + AccountKey20Aliases, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs new file mode 100644 index 0000000..a0e18d5 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs @@ -0,0 +1,47 @@ +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; + +use crate::xcm_mock::parachain::{MsgQueue, PolkadotXcm, RuntimeCall}; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Aliasers = Nothing; + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = PolkadotXcm; + type AssetTransactor = asset_transactor::AssetTransactor; + type AssetTrap = (); + type Barrier = barrier::Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type HrmpNewChannelOpenRequestHandler = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type OriginConverter = origin_converter::OriginConverter; + type PalletInstancesInfo = (); + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Everything; + type SubscriptionService = (); + type Trader = FixedRateOfFungible; + type TransactionalProcessor = FrameTransactionalProcessor; + type UniversalAliases = Nothing; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type XcmRecorder = PolkadotXcm; + type XcmSender = XcmRouter; +} diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs new file mode 100644 index 0000000..881c18c --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs @@ -0,0 +1,14 @@ +use pallet_xcm::XcmPassthrough; +use xcm_builder::{SignedAccountKey20AsNative, SovereignSignedViaLocation}; + +use crate::xcm_mock::parachain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; + +type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountKey20AsNative, + XcmPassthrough, +); + +pub type OriginConverter = XcmOriginToCallOrigin; diff --git a/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs new file mode 100644 index 0000000..92232af --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs @@ -0,0 +1,12 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +use crate::xcm_mock::parachain::RuntimeCall; + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds; diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/mod.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/mod.rs new file mode 100644 index 0000000..00e400f --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/mod.rs @@ -0,0 +1,136 @@ +mod xcm_config; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ConstU128, Everything, Nothing, ProcessMessage, ProcessMessageError}, + weights::{Weight, WeightMeter}, +}; +use frame_system::EnsureRoot; +use polkadot_runtime_parachains::{ + configuration, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, shared, +}; +use sp_core::ConstU32; +use sp_runtime::{traits::IdentityLookup, AccountId32}; +use xcm::latest::prelude::*; +use xcm_builder::{IsConcrete, SignedToAccountId32}; +pub use xcm_config::*; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Block = Block; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +pub type LocalOriginToLocation = + SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type AdminOrigin = EnsureRoot; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type SovereignAccountOf = location_converter::LocationConverter; + type TrustedLockers = (); + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type WeightInfo = pallet_xcm::TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Everything; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl origin::Config for Runtime {} + +type Block = frame_system::mocking::MockBlock; + +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter, id) + } +} + +impl pallet_message_queue::Config for Runtime { + type HeapSize = MessageQueueHeapSize; + type IdleMaxServiceWeight = (); + type MaxStale = MessageQueueMaxStale; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type QueuePausedQuery = (); + type RuntimeEvent = RuntimeEvent; + type ServiceWeight = MessageQueueServiceWeight; + type Size = u32; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + XcmPallet: pallet_xcm, + MessageQueue: pallet_message_queue, + } +); diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs new file mode 100644 index 0000000..af6e086 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs @@ -0,0 +1,8 @@ +use xcm_builder::{FungibleAdapter, IsConcrete}; + +use crate::xcm_mock::relay_chain::{ + constants::TokenLocation, location_converter::LocationConverter, AccountId, Balances, +}; + +pub type AssetTransactor = + FungibleAdapter, LocationConverter, AccountId, ()>; diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs new file mode 100644 index 0000000..ab45bec --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs @@ -0,0 +1,4 @@ +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom; diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs new file mode 100644 index 0000000..c298b3e --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs @@ -0,0 +1,15 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; + +parameter_types! { + pub TokensPerSecondPerByte: (AssetId, u128, u128) = + (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const TokenLocation: Location = Here.into_location(); + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); + pub UnitWeightCost: u64 = 1_000; +} diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs new file mode 100644 index 0000000..74ae04f --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs @@ -0,0 +1,10 @@ +use xcm_builder::{AccountId32Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +use crate::xcm_mock::relay_chain::{constants::RelayNetwork, AccountId}; + +type LocationToAccountId = ( + HashedDescription>, + AccountId32Aliases, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs new file mode 100644 index 0000000..2f664fc --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs @@ -0,0 +1,48 @@ +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; +use xcm_executor::Config; + +use crate::xcm_mock::relay_chain::{RuntimeCall, XcmPallet}; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Aliasers = Nothing; + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = XcmPallet; + type AssetTransactor = asset_transactor::AssetTransactor; + type AssetTrap = (); + type Barrier = barrier::Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type HrmpNewChannelOpenRequestHandler = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type OriginConverter = origin_converter::OriginConverter; + type PalletInstancesInfo = (); + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Everything; + type SubscriptionService = (); + type Trader = FixedRateOfFungible; + type TransactionalProcessor = FrameTransactionalProcessor; + type UniversalAliases = Nothing; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type XcmRecorder = XcmPallet; + type XcmSender = XcmRouter; +} diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs new file mode 100644 index 0000000..e3d8920 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs @@ -0,0 +1,19 @@ +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::origin; +use xcm_builder::{ + ChildParachainAsNative, ChildSystemParachainAsSuperuser, SignedAccountId32AsNative, + SovereignSignedViaLocation, +}; + +use crate::xcm_mock::relay_chain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +pub type OriginConverter = LocalOriginConverter; diff --git a/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs new file mode 100644 index 0000000..5178e87 --- /dev/null +++ b/evm-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs @@ -0,0 +1,12 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +use crate::xcm_mock::relay_chain::RuntimeCall; + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds; diff --git a/evm-template/runtime/tests/xcm_tests.rs b/evm-template/runtime/tests/xcm_tests.rs new file mode 100644 index 0000000..bead935 --- /dev/null +++ b/evm-template/runtime/tests/xcm_tests.rs @@ -0,0 +1,251 @@ +//! Runtime XCM Tests + +pub mod xcm_mock; + +use frame_support::{assert_ok, weights::Weight}; +use parity_scale_codec::Encode; +use xcm::prelude::*; +use xcm_mock::*; +use xcm_simulator::{mock_message_queue::ReceivedDmp, TestExt}; + +// Helper function for forming buy execution message +fn buy_execution(fees: impl Into) -> Instruction { + BuyExecution { fees: fees.into(), weight_limit: Unlimited } +} + +#[test] +fn remote_account_ids_work() { + child_account_account_id(1, ALICE); + sibling_account_account_id(1, ALICE); + parent_account_account_id(ALICE); +} + +#[test] +fn dmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::send_xcm( + Here, + Parachain(1), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaA::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn ump() { + MockNet::reset(); + + let remark = relay_chain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + Parent, + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + Relay::execute_with(|| { + use relay_chain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn xcmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + (Parent, Parachain(2)), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaB::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn remote_locking_and_unlocking() { + MockNet::reset(); + + let locked_amount = 100; + + ParaB::execute_with(|| { + let message = Xcm(vec![LockAsset { + asset: (Here, locked_amount).into(), + unlocker: Parachain(1).into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + assert_eq!( + relay_chain::Balances::locks(child_account_id(2)), + vec![BalanceLock { id: *b"py/xcmlk", amount: locked_amount, reasons: Reasons::All }] + ); + }); + + ParaA::execute_with(|| { + assert_eq!( + ReceivedDmp::::get(), + vec![Xcm(vec![NoteUnlockable { + owner: (Parent, Parachain(2)).into(), + asset: (Parent, locked_amount).into() + }])] + ); + }); + + ParaB::execute_with(|| { + // Request unlocking part of the funds on the relay chain + let message = Xcm(vec![RequestUnlock { + asset: (Parent, locked_amount - 50).into(), + locker: Parent.into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(1)), message)); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + // Lock is reduced + assert_eq!( + relay_chain::Balances::locks(child_account_id(2)), + vec![BalanceLock { + id: *b"py/xcmlk", + amount: locked_amount - 50, + reasons: Reasons::All + }] + ); + }); +} + +/// Scenario: +/// A parachain transfers funds on the relay chain to another parachain account. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn withdraw_and_deposit() { + MockNet::reset(); + + let send_amount = 10; + + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ]); + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); +} + +/// Scenario: +/// A parachain wants to be notified that a transfer worked correctly. +/// It sends a `QueryHolding` after the deposit to get notified on success. +/// +/// Asserts that the balances are updated correctly and the expected XCM is sent. +#[test] +fn query_holding() { + MockNet::reset(); + + let send_amount = 10; + let query_id_set = 1234; + + // Send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ReportHolding { + response_info: QueryResponseInfo { + destination: Parachain(1).into(), + query_id: query_id_set, + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + }, + assets: All.into(), + }, + ]); + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); + }); + + // Check that transfer was executed + Relay::execute_with(|| { + // Withdraw executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + // Deposit executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); + + // Check that QueryResponse message was received + ParaA::execute_with(|| { + assert_eq!( + ReceivedDmp::::get(), + vec![Xcm(vec![QueryResponse { + query_id: query_id_set, + response: Response::Assets(Assets::new()), + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + querier: Some(Here.into()), + }])], + ); + }); +} diff --git a/generic-template/Cargo.lock b/generic-template/Cargo.lock index bd6d37e..7f8d473 100644 --- a/generic-template/Cargo.lock +++ b/generic-template/Cargo.lock @@ -3703,6 +3703,7 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", + "polkadot-runtime-parachains", "scale-info", "smallvec", "sp-api", @@ -3717,6 +3718,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-std", + "sp-tracing", "sp-transaction-pool", "sp-version", "staging-parachain-info", @@ -3725,6 +3727,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "xcm-primitives", + "xcm-simulator", ] [[package]] @@ -15309,6 +15312,28 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "xcm-simulator" +version = "16.0.0" +source = "git+https://github.com/paritytech/polkadot-sdk?tag=polkadot-stable2407-1#16b0fd09d9e9281c20ee0c1d8b87d011e3e3454e" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "paste", + "polkadot-core-primitives", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "xml-rs" version = "0.8.21" diff --git a/generic-template/Cargo.toml b/generic-template/Cargo.toml index bff99bf..7b76a4e 100644 --- a/generic-template/Cargo.toml +++ b/generic-template/Cargo.toml @@ -91,6 +91,7 @@ sp-session = { git = "https://github.com/paritytech/polkadot-sdk", default-featu sp-state-machine = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-timestamp = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +sp-tracing = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2407-1" } @@ -106,9 +107,11 @@ polkadot-cli = { git = "https://github.com/paritytech/polkadot-sdk", features = polkadot-parachain-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } +xcm-simulator = { git = "https://github.com/paritytech/polkadot-sdk", tag = "polkadot-stable2407-1" } # Cumulus assets-common = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, tag = "polkadot-stable2407-1" } diff --git a/generic-template/runtime/Cargo.toml b/generic-template/runtime/Cargo.toml index 1887acf..024fddd 100644 --- a/generic-template/runtime/Cargo.toml +++ b/generic-template/runtime/Cargo.toml @@ -97,7 +97,10 @@ orml-xcm-support = { workspace = true } orml-xtokens = { workspace = true } [dev-dependencies] +polkadot-runtime-parachains = { workspace = true } sp-io = { workspace = true } +sp-tracing = { workspace = true } +xcm-simulator = { workspace = true } [build-dependencies] substrate-wasm-builder = { workspace = true } diff --git a/generic-template/runtime/tests/xcm_mock/mod.rs b/generic-template/runtime/tests/xcm_mock/mod.rs new file mode 100644 index 0000000..0ec3f02 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/mod.rs @@ -0,0 +1,121 @@ +pub mod parachain; +pub mod relay_chain; + +use sp_runtime::BuildStorage; +use sp_tracing; +use xcm::prelude::*; +use xcm_executor::traits::ConvertLocation; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; + +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000; + +decl_test_parachain! { + pub struct ParaA { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(1), + } +} + +decl_test_parachain! { + pub struct ParaB { + Runtime = parachain::Runtime, + XcmpMessageHandler = parachain::MsgQueue, + DmpMessageHandler = parachain::MsgQueue, + new_ext = para_ext(2), + } +} + +decl_test_relay_chain! { + pub struct Relay { + Runtime = relay_chain::Runtime, + RuntimeCall = relay_chain::RuntimeCall, + RuntimeEvent = relay_chain::RuntimeEvent, + XcmConfig = relay_chain::XcmConfig, + MessageQueue = relay_chain::MessageQueue, + System = relay_chain::System, + new_ext = relay_ext(), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = Relay, + parachains = vec![ + (1, ParaA), + (2, ParaB), + ], + } +} + +pub fn parent_account_id() -> parachain::AccountId { + let location = (Parent,); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn child_account_id(para: u32) -> relay_chain::AccountId { + let location = (Parachain(para),); + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn child_account_account_id(para: u32, who: sp_runtime::AccountId32) -> relay_chain::AccountId { + let location = (Parachain(para), AccountId32 { network: None, id: who.into() }); + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn sibling_account_account_id(para: u32, who: sp_runtime::AccountId32) -> parachain::AccountId { + let location = (Parent, Parachain(para), AccountId32 { network: None, id: who.into() }); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn parent_account_account_id(who: sp_runtime::AccountId32) -> parachain::AccountId { + let location = (Parent, AccountId32 { network: None, id: who.into() }); + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() +} + +pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { + use parachain::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INITIAL_BALANCE), (parent_account_id(), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + sp_tracing::try_init_simple(); + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn relay_ext() -> sp_io::TestExternalities { + use relay_chain::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (child_account_id(1), INITIAL_BALANCE), + (child_account_id(2), INITIAL_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub type RelayChainPalletXcm = pallet_xcm::Pallet; +pub type ParachainPalletXcm = pallet_xcm::Pallet; diff --git a/generic-template/runtime/tests/xcm_mock/parachain/mod.rs b/generic-template/runtime/tests/xcm_mock/parachain/mod.rs new file mode 100644 index 0000000..4d1f1cd --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/mod.rs @@ -0,0 +1,106 @@ +mod xcm_config; +use core::marker::PhantomData; + +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ConstU128, ContainsPair, Everything, Nothing}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, +}; +use frame_system::EnsureRoot; +use sp_core::ConstU32; +use sp_runtime::{ + traits::{Get, IdentityLookup}, + AccountId32, +}; +use xcm::latest::prelude::*; +use xcm_builder::{EnsureXcmOrigin, SignedToAccountId32}; +pub use xcm_config::*; +use xcm_executor::XcmExecutor; +use xcm_simulator::mock_message_queue; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Block = Block; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); +} + +impl mock_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = + SignedToAccountId32; + +pub struct TrustedLockerCase(PhantomData); +impl> ContainsPair for TrustedLockerCase { + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } +} + +parameter_types! { + pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); +} + +pub type TrustedLockers = TrustedLockerCase; + +impl pallet_xcm::Config for Runtime { + type AdminOrigin = EnsureRoot; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type ExecuteXcmOrigin = EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = EnsureXcmOrigin; + type SovereignAccountOf = location_converter::LocationConverter; + type TrustedLockers = TrustedLockers; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type WeightInfo = pallet_xcm::TestWeightInfo; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Nothing; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_message_queue, + PolkadotXcm: pallet_xcm, + } +); diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs new file mode 100644 index 0000000..8da002c --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/asset_transactor.rs @@ -0,0 +1,8 @@ +use xcm_builder::{FungibleAdapter, IsConcrete}; + +use crate::parachain::{ + constants::KsmLocation, location_converter::LocationConverter, AccountId, Balances, +}; + +pub type AssetTransactor = + FungibleAdapter, LocationConverter, AccountId, ()>; diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs new file mode 100644 index 0000000..ab45bec --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/barrier.rs @@ -0,0 +1,4 @@ +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom; diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs new file mode 100644 index 0000000..b871586 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/constants.rs @@ -0,0 +1,16 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_simulator::mock_message_queue::ParachainId; + +use crate::xcm_mock::parachain::Runtime; + +parameter_types! { + pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainId::::get().into())].into(); +} diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs new file mode 100644 index 0000000..04bcbb6 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/location_converter.rs @@ -0,0 +1,10 @@ +use xcm_builder::{AccountId32Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +use crate::xcm_mock::parachain::{constants::RelayNetwork, AccountId}; + +type LocationToAccountId = ( + HashedDescription>, + AccountId32Aliases, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs new file mode 100644 index 0000000..a0e18d5 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/mod.rs @@ -0,0 +1,47 @@ +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; + +use crate::xcm_mock::parachain::{MsgQueue, PolkadotXcm, RuntimeCall}; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Aliasers = Nothing; + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = PolkadotXcm; + type AssetTransactor = asset_transactor::AssetTransactor; + type AssetTrap = (); + type Barrier = barrier::Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type HrmpNewChannelOpenRequestHandler = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type OriginConverter = origin_converter::OriginConverter; + type PalletInstancesInfo = (); + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Everything; + type SubscriptionService = (); + type Trader = FixedRateOfFungible; + type TransactionalProcessor = FrameTransactionalProcessor; + type UniversalAliases = Nothing; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type XcmRecorder = PolkadotXcm; + type XcmSender = XcmRouter; +} diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs new file mode 100644 index 0000000..63c405b --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/origin_converter.rs @@ -0,0 +1,14 @@ +use pallet_xcm::XcmPassthrough; +use xcm_builder::{SignedAccountId32AsNative, SovereignSignedViaLocation}; + +use crate::xcm_mock::parachain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; + +type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountId32AsNative, + XcmPassthrough, +); + +pub type OriginConverter = XcmOriginToCallOrigin; diff --git a/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs new file mode 100644 index 0000000..92232af --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/parachain/xcm_config/weigher.rs @@ -0,0 +1,12 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +use crate::xcm_mock::parachain::RuntimeCall; + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds; diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/mod.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/mod.rs new file mode 100644 index 0000000..8c71ad0 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/mod.rs @@ -0,0 +1,138 @@ +//! Relay chain runtime mock. + +mod xcm_config; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ConstU128, Everything, Nothing, ProcessMessage, ProcessMessageError}, + weights::{Weight, WeightMeter}, +}; +use frame_system::EnsureRoot; +use polkadot_runtime_parachains::{ + configuration, + inclusion::{AggregateMessageOrigin, UmpQueueId}, + origin, shared, +}; +use sp_core::ConstU32; +use sp_runtime::{traits::IdentityLookup, AccountId32}; +use xcm::latest::prelude::*; +use xcm_builder::{IsConcrete, SignedToAccountId32}; +pub use xcm_config::*; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type Block = Block; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; +} + +impl shared::Config for Runtime { + type DisabledValidators = (); +} + +impl configuration::Config for Runtime { + type WeightInfo = configuration::TestWeightInfo; +} + +pub type LocalOriginToLocation = + SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type AdminOrigin = EnsureRoot; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = IsConcrete; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type SovereignAccountOf = location_converter::LocationConverter; + type TrustedLockers = (); + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type WeightInfo = pallet_xcm::TestWeightInfo; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmReserveTransferFilter = Everything; + type XcmRouter = XcmRouter; + type XcmTeleportFilter = Everything; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl origin::Config for Runtime {} + +type Block = frame_system::mocking::MockBlock; + +parameter_types! { + /// Amount of weight that can be spent per block to service messages. + pub MessageQueueServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); + pub const MessageQueueHeapSize: u32 = 65_536; + pub const MessageQueueMaxStale: u32 = 16; +} + +/// Message processor to handle any messages that were enqueued into the `MessageQueue` pallet. +pub struct MessageProcessor; +impl ProcessMessage for MessageProcessor { + type Origin = AggregateMessageOrigin; + + fn process_message( + message: &[u8], + origin: Self::Origin, + meter: &mut WeightMeter, + id: &mut [u8; 32], + ) -> Result { + let para = match origin { + AggregateMessageOrigin::Ump(UmpQueueId::Para(para)) => para, + }; + xcm_builder::ProcessXcmMessage::< + Junction, + xcm_executor::XcmExecutor, + RuntimeCall, + >::process_message(message, Junction::Parachain(para.into()), meter, id) + } +} + +impl pallet_message_queue::Config for Runtime { + type HeapSize = MessageQueueHeapSize; + type IdleMaxServiceWeight = (); + type MaxStale = MessageQueueMaxStale; + type MessageProcessor = MessageProcessor; + type QueueChangeHandler = (); + type QueuePausedQuery = (); + type RuntimeEvent = RuntimeEvent; + type ServiceWeight = MessageQueueServiceWeight; + type Size = u32; + type WeightInfo = (); +} + +construct_runtime!( + pub enum Runtime + { + System: frame_system, + Balances: pallet_balances, + ParasOrigin: origin, + XcmPallet: pallet_xcm, + MessageQueue: pallet_message_queue, + } +); diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs new file mode 100644 index 0000000..af6e086 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/asset_transactor.rs @@ -0,0 +1,8 @@ +use xcm_builder::{FungibleAdapter, IsConcrete}; + +use crate::xcm_mock::relay_chain::{ + constants::TokenLocation, location_converter::LocationConverter, AccountId, Balances, +}; + +pub type AssetTransactor = + FungibleAdapter, LocationConverter, AccountId, ()>; diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs new file mode 100644 index 0000000..ab45bec --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/barrier.rs @@ -0,0 +1,4 @@ +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom; diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs new file mode 100644 index 0000000..c298b3e --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/constants.rs @@ -0,0 +1,15 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; + +parameter_types! { + pub TokensPerSecondPerByte: (AssetId, u128, u128) = + (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const TokenLocation: Location = Here.into_location(); + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); + pub UnitWeightCost: u64 = 1_000; +} diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs new file mode 100644 index 0000000..74ae04f --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/location_converter.rs @@ -0,0 +1,10 @@ +use xcm_builder::{AccountId32Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +use crate::xcm_mock::relay_chain::{constants::RelayNetwork, AccountId}; + +type LocationToAccountId = ( + HashedDescription>, + AccountId32Aliases, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs new file mode 100644 index 0000000..2f664fc --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/mod.rs @@ -0,0 +1,48 @@ +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; +use xcm_executor::Config; + +use crate::xcm_mock::relay_chain::{RuntimeCall, XcmPallet}; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Aliasers = Nothing; + type AssetClaims = (); + type AssetExchanger = (); + type AssetLocker = XcmPallet; + type AssetTransactor = asset_transactor::AssetTransactor; + type AssetTrap = (); + type Barrier = barrier::Barrier; + type CallDispatcher = RuntimeCall; + type FeeManager = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type HrmpNewChannelOpenRequestHandler = (); + type IsReserve = (); + type IsTeleporter = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type OriginConverter = origin_converter::OriginConverter; + type PalletInstancesInfo = (); + type ResponseHandler = (); + type RuntimeCall = RuntimeCall; + type SafeCallFilter = Everything; + type SubscriptionService = (); + type Trader = FixedRateOfFungible; + type TransactionalProcessor = FrameTransactionalProcessor; + type UniversalAliases = Nothing; + type UniversalLocation = constants::UniversalLocation; + type Weigher = weigher::Weigher; + type XcmRecorder = XcmPallet; + type XcmSender = XcmRouter; +} diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs new file mode 100644 index 0000000..e3d8920 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/origin_converter.rs @@ -0,0 +1,19 @@ +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::origin; +use xcm_builder::{ + ChildParachainAsNative, ChildSystemParachainAsSuperuser, SignedAccountId32AsNative, + SovereignSignedViaLocation, +}; + +use crate::xcm_mock::relay_chain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +pub type OriginConverter = LocalOriginConverter; diff --git a/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs new file mode 100644 index 0000000..5178e87 --- /dev/null +++ b/generic-template/runtime/tests/xcm_mock/relay_chain/xcm_config/weigher.rs @@ -0,0 +1,12 @@ +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +use crate::xcm_mock::relay_chain::RuntimeCall; + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds; diff --git a/generic-template/runtime/tests/xcm_tests.rs b/generic-template/runtime/tests/xcm_tests.rs new file mode 100644 index 0000000..cc0dc28 --- /dev/null +++ b/generic-template/runtime/tests/xcm_tests.rs @@ -0,0 +1,248 @@ +pub mod xcm_mock; +use frame_support::{assert_ok, weights::Weight}; +use parity_scale_codec::Encode; +use xcm::prelude::*; +use xcm_mock::*; +use xcm_simulator::{mock_message_queue::ReceivedDmp, TestExt}; + +// Helper function for forming buy execution message +fn buy_execution(fees: impl Into) -> Instruction { + BuyExecution { fees: fees.into(), weight_limit: Unlimited } +} + +#[test] +fn remote_account_ids_work() { + child_account_account_id(1, ALICE); + sibling_account_account_id(1, ALICE); + parent_account_account_id(ALICE); +} + +#[test] +fn dmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::send_xcm( + Here, + Parachain(1), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaA::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn ump() { + MockNet::reset(); + + let remark = relay_chain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + Parent, + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + Relay::execute_with(|| { + use relay_chain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn xcmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + (Parent, Parachain(2)), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaB::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn remote_locking_and_unlocking() { + MockNet::reset(); + + let locked_amount = 100; + + ParaB::execute_with(|| { + let message = Xcm(vec![LockAsset { + asset: (Here, locked_amount).into(), + unlocker: Parachain(1).into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + assert_eq!( + relay_chain::Balances::locks(child_account_id(2)), + vec![BalanceLock { id: *b"py/xcmlk", amount: locked_amount, reasons: Reasons::All }] + ); + }); + + ParaA::execute_with(|| { + assert_eq!( + ReceivedDmp::::get(), + vec![Xcm(vec![NoteUnlockable { + owner: (Parent, Parachain(2)).into(), + asset: (Parent, locked_amount).into() + }])] + ); + }); + + ParaB::execute_with(|| { + // Request unlocking part of the funds on the relay chain + let message = Xcm(vec![RequestUnlock { + asset: (Parent, locked_amount - 50).into(), + locker: Parent.into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(1)), message)); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + // Lock is reduced + assert_eq!( + relay_chain::Balances::locks(child_account_id(2)), + vec![BalanceLock { + id: *b"py/xcmlk", + amount: locked_amount - 50, + reasons: Reasons::All + }] + ); + }); +} + +/// Scenario: +/// A parachain transfers funds on the relay chain to another parachain account. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn withdraw_and_deposit() { + MockNet::reset(); + + let send_amount = 10; + + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ]); + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); +} + +/// Scenario: +/// A parachain wants to be notified that a transfer worked correctly. +/// It sends a `QueryHolding` after the deposit to get notified on success. +/// +/// Asserts that the balances are updated correctly and the expected XCM is sent. +#[test] +fn query_holding() { + MockNet::reset(); + + let send_amount = 10; + let query_id_set = 1234; + + // Send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ReportHolding { + response_info: QueryResponseInfo { + destination: Parachain(1).into(), + query_id: query_id_set, + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + }, + assets: All.into(), + }, + ]); + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); + }); + + // Check that transfer was executed + Relay::execute_with(|| { + // Withdraw executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + // Deposit executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); + + // Check that QueryResponse message was received + ParaA::execute_with(|| { + assert_eq!( + ReceivedDmp::::get(), + vec![Xcm(vec![QueryResponse { + query_id: query_id_set, + response: Response::Assets(Assets::new()), + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + querier: Some(Here.into()), + }])], + ); + }); +}