From fde986a2c55a000faf8f031b0dbfd66954c8eed7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 18:59:32 +0200 Subject: [PATCH 01/32] init --- rust-toolchain | 5 ++ xcm/xcm-executor/src/lib.rs | 1 + xcm/xcm-simulator/example/src/lib.rs | 64 +++++++++++++++++++- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- 4 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 rust-toolchain diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000000..ae16315aa35e --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,5 @@ +[toolchain] +channel = "nightly-2021-02-24" +components = [ "rustfmt", "clippy" ] +targets = [ "wasm32-unknown-unknown" ] +profile = "minimal" diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8f8a5c9ee617..2760bb23999e 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,6 +15,7 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] +#![feature(or_patterns)] // revert before PRing use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index f318409bf187..b0d999dbc5e1 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -19,6 +19,8 @@ mod relay_chain; use sp_runtime::AccountId32; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -79,10 +81,10 @@ pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { pub fn relay_ext() -> sp_io::TestExternalities { use relay_chain::{Runtime, System}; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE)] } + pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)] } .assimilate_storage(&mut t) .unwrap(); @@ -99,13 +101,14 @@ mod tests { use super::*; use codec::Encode; - use frame_support::assert_ok; + use frame_support::{assert_ok, weights::Weight}; use xcm::v0::{ Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, NetworkId, OriginKind, Xcm::*, + Order, }; use xcm_simulator::TestExt; @@ -212,4 +215,59 @@ mod tests { ); }); } + + // Helper function for forming buy execution message + fn buy_execution(debt: Weight) -> Order { + use xcm::opaque::v0::prelude::*; + Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + } + + fn last_relay_chain_event() -> relay_chain::Event { + relay_chain::System::events().pop().expect("Event expected").event + } + + fn last_parachain_event() -> parachain::Event { + parachain::System::events().pop().expect("Event expected").event + } + + /// Scenario: + /// A parachain transfers funds on the relaychain to another parachain's account. + /// + /// Asserts that the parachain accounts are updated as expected. + #[test] + fn withdraw_and_deposit() { + use xcm::opaque::v0::prelude::*; + use sp_runtime::traits::AccountIdConversion; + use polkadot_parachain::primitives::Id as ParaId; + MockNet::reset(); + + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { + assets: vec![All], + dest: Parachain(2).into(), + }, + ], + }; + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm( + Null, + X1(Parent), + message.clone(), + )); + }); + + Relay::execute_with(|| { + let amount = 10; + let para_account_one: relay_chain::AccountId = ParaId::from(1).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_one), INITIAL_BALANCE - amount); + let para_account: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account), 10); + }); + } } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index c69f20d05eaf..486054f5feeb 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -143,7 +143,7 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = (); + type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; From f806a95bcb42727916a03ca0f62339fe7e468bb7 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 19:27:00 +0200 Subject: [PATCH 02/32] reserve trasfer asset and query holding tests without message checking --- xcm/xcm-simulator/example/src/lib.rs | 130 ++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 24 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index b0d999dbc5e1..1ef731faf88f 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -17,10 +17,10 @@ mod parachain; mod relay_chain; -use sp_runtime::AccountId32; -use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; use polkadot_parachain::primitives::Id as ParaId; -use sp_runtime::traits::AccountIdConversion; +use sp_runtime::{traits::AccountIdConversion, AccountId32}; +use xcm::opaque::v0::prelude::*; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -84,9 +84,11 @@ pub fn relay_ext() -> sp_io::TestExternalities { let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)] } - .assimilate_storage(&mut t) - .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_a, INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); @@ -106,9 +108,8 @@ mod tests { Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, - NetworkId, OriginKind, + NetworkId, Order, OriginKind, Xcm::*, - Order, }; use xcm_simulator::TestExt; @@ -193,8 +194,12 @@ mod tests { }); } + /// Scenario: + /// A user Alice sends funds from the relaychain to a parachain. + /// + /// Asserts that the correct XCM is sent and the balances are set as expected. #[test] - fn reserve_transfer() { + fn reserve_transfer_assets() { MockNet::reset(); Relay::execute_with(|| { @@ -205,6 +210,8 @@ mod tests { vec![ConcreteFungible { id: Null, amount: 123 }], 123, )); + let para_account_a = ParaId::from(1).into_account(); + assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); }); ParaA::execute_with(|| { @@ -236,38 +243,113 @@ mod tests { /// Asserts that the parachain accounts are updated as expected. #[test] fn withdraw_and_deposit() { - use xcm::opaque::v0::prelude::*; - use sp_runtime::traits::AccountIdConversion; - use polkadot_parachain::primitives::Id as ParaId; MockNet::reset(); ParaA::execute_with(|| { let amount = 10; let weight = 3 * relay_chain::BaseXcmWeight::get(); + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { assets: vec![All], dest: Parachain(2).into() }, + ], + }; + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); + }); + + Relay::execute_with(|| { + let amount = 10; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - amount + ); + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + }); + } + + /// 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(); + + // First send a message which fails on the relay chain + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + let query_id = 1234; let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount }], effects: vec![ buy_execution(weight), Order::DepositAsset { assets: vec![All], - dest: Parachain(2).into(), + dest: OnlyChild.into(), // invalid destination }, + // is not triggered becasue the deposit fails + Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, ], }; - // Send withdraw and deposit - assert_ok!(ParachainPalletXcm::send_xcm( - Null, - X1(Parent), - message.clone(), - )); + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); - + + // Check that no transfer was executed and no response message was sent Relay::execute_with(|| { let amount = 10; - let para_account_one: relay_chain::AccountId = ParaId::from(1).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_one), INITIAL_BALANCE - amount); - let para_account: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account), 10); + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + // withdraw did execute + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - amount + ); + // but deposit did not execute + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); + // TODO: verify no message was sent, but mock XCM router is not mock... + }); + + // Now send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let amount = 10; + let weight = 3 * relay_chain::BaseXcmWeight::get(); + let query_id = 1234; + let message = WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::DepositAsset { + assets: vec![All], + dest: Parachain(2).into(), // valid destination + }, + Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + ], + }; + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); + }); + + // Check that transfer was executed and response message was sent + Relay::execute_with(|| { + let spent = 20; + let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); + // withdraw did execute + assert_eq!( + relay_chain::Balances::free_balance(para_account_a), + INITIAL_BALANCE - spent + ); + // and deposit did execute + let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + // TODO: check the sent message is a QueryResponse to the Parachain(1) }); } } From eb4224be40fc63a729caa6711404e95d22397e77 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 19:56:22 +0200 Subject: [PATCH 03/32] XcmSender short circuits on first successful impl for tuples apparently --- xcm/xcm-simulator/example/src/lib.rs | 4 ++-- xcm/xcm-simulator/example/src/relay_chain.rs | 23 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 1ef731faf88f..bdff2de239d1 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -314,7 +314,7 @@ mod tests { // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); - // TODO: verify no message was sent, but mock XCM router is not mock... + assert_eq!(relay_chain::sent_xcm(), vec![]); }); // Now send a message which fully succeeds on the relay chain @@ -349,7 +349,7 @@ mod tests { // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); - // TODO: check the sent message is a QueryResponse to the Parachain(1) + assert_eq!(relay_chain::sent_xcm(), vec![]); }); } } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 486054f5feeb..43ed0d39ce9b 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,7 +26,11 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v0::{MultiAsset, MultiLocation, NetworkId}; +use sp_std::cell::RefCell; +use xcm::{ + opaque::v0::{Result as XcmResult, SendXcm, Xcm}, + v0::{MultiAsset, MultiLocation, NetworkId}, +}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, @@ -120,10 +124,25 @@ parameter_types! { pub type XcmRouter = super::RelayChainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom>; +thread_local! { + pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); +} +pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} +/// Sender that never returns error, always sends +pub struct TestSendXcm; +impl SendXcm for TestSendXcm { + fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); + Ok(()) + } +} + pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; - type XcmSender = XcmRouter; + type XcmSender = (TestSendXcm, XcmRouter); type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); From c65624dd592e343822dea194c7ee8a01cf49f7ec Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:10:29 +0200 Subject: [PATCH 04/32] add TestSendXcm XcmRouter to track sent messages --- rustfmt.toml | 20 ------------------ xcm/xcm-executor/src/lib.rs | 1 - xcm/xcm-simulator/example/src/lib.rs | 22 +++++++------------- xcm/xcm-simulator/example/src/relay_chain.rs | 7 +++---- 4 files changed, 10 insertions(+), 40 deletions(-) delete mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 15e9bdcdf10f..000000000000 --- a/rustfmt.toml +++ /dev/null @@ -1,20 +0,0 @@ -# Basic -hard_tabs = true -max_width = 100 -use_small_heuristics = "Max" -# Imports -imports_granularity = "Crate" -reorder_imports = true -# Consistency -newline_style = "Unix" -# Misc -chain_width = 80 -spaces_around_ranges = false -binop_separator = "Back" -reorder_impl_items = false -match_arm_leading_pipes = "Preserve" -match_arm_blocks = false -match_block_trailing_comma = true -trailing_comma = "Vertical" -trailing_semicolon = false -use_field_init_shorthand = true diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 2760bb23999e..8f8a5c9ee617 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . #![cfg_attr(not(feature = "std"), no_std)] -#![feature(or_patterns)] // revert before PRing use sp_std::{prelude::*, marker::PhantomData}; use frame_support::{ diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index bdff2de239d1..a49698e9425c 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -19,7 +19,6 @@ mod relay_chain; use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::{traits::AccountIdConversion, AccountId32}; -use xcm::opaque::v0::prelude::*; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; pub const ALICE: AccountId32 = AccountId32::new([0u8; 32]); @@ -105,6 +104,7 @@ mod tests { use codec::Encode; use frame_support::{assert_ok, weights::Weight}; use xcm::v0::{ + prelude::OnlyChild, Junction::{self, Parachain, Parent}, MultiAsset::*, MultiLocation::*, @@ -113,6 +113,12 @@ mod tests { }; use xcm_simulator::TestExt; + // Helper function for forming buy execution message + fn buy_execution(debt: Weight) -> Order { + use xcm::opaque::v0::prelude::*; + Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } + } + #[test] fn dmp() { MockNet::reset(); @@ -223,20 +229,6 @@ mod tests { }); } - // Helper function for forming buy execution message - fn buy_execution(debt: Weight) -> Order { - use xcm::opaque::v0::prelude::*; - Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } - } - - fn last_relay_chain_event() -> relay_chain::Event { - relay_chain::System::events().pop().expect("Event expected").event - } - - fn last_parachain_event() -> parachain::Event { - parachain::System::events().pop().expect("Event expected").event - } - /// Scenario: /// A parachain transfers funds on the relaychain to another parachain's account. /// diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 43ed0d39ce9b..1de6aa1bc310 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -127,10 +127,9 @@ pub type Barrier = AllowUnpaidExecutionFrom>; thread_local! { pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); } -pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { +pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } -/// Sender that never returns error, always sends pub struct TestSendXcm; impl SendXcm for TestSendXcm { fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { @@ -142,7 +141,7 @@ impl SendXcm for TestSendXcm { pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; - type XcmSender = (TestSendXcm, XcmRouter); + type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); @@ -159,7 +158,7 @@ pub type LocalOriginToLocation = SignedToAccountId32; - type XcmRouter = XcmRouter; + type XcmRouter = (XcmRouter, TestSendXcm); // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; From e9da5eadc50bf12c41e9be031cbea8bbb660f083 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:12:26 +0200 Subject: [PATCH 05/32] fix mistake removing local env stuff --- rust-toolchain | 5 ----- rustfmt.toml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) delete mode 100644 rust-toolchain create mode 100644 rustfmt.toml diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index ae16315aa35e..000000000000 --- a/rust-toolchain +++ /dev/null @@ -1,5 +0,0 @@ -[toolchain] -channel = "nightly-2021-02-24" -components = [ "rustfmt", "clippy" ] -targets = [ "wasm32-unknown-unknown" ] -profile = "minimal" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000000..6005fb8234cd --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,20 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true \ No newline at end of file From abc999fbbaca8d0c77a67aa15222ca28323d9339 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Sun, 1 Aug 2021 20:16:58 +0200 Subject: [PATCH 06/32] fix --- rustfmt.toml | 2 +- xcm/xcm-simulator/example/src/relay_chain.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 6005fb8234cd..15e9bdcdf10f 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -17,4 +17,4 @@ match_arm_blocks = false match_block_trailing_comma = true trailing_comma = "Vertical" trailing_semicolon = false -use_field_init_shorthand = true \ No newline at end of file +use_field_init_shorthand = true diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 1de6aa1bc310..1796af898759 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -161,7 +161,7 @@ impl pallet_xcm::Config for Runtime { type XcmRouter = (XcmRouter, TestSendXcm); // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = All<(MultiLocation, xcm::v0::Xcm)>; + type XcmExecuteFilter = (); type XcmExecutor = XcmExecutor; type XcmTeleportFilter = All<(MultiLocation, Vec)>; type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; From 63328548330fd376db6c7c62e3b38b059d3e5d73 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Sun, 1 Aug 2021 21:25:45 +0200 Subject: [PATCH 07/32] add xcm simulator scenario for statemine-like chain --- Cargo.lock | 38 ++ Cargo.toml | 1 + .../statemine-like-example/Cargo.toml | 28 ++ .../statemine-like-example/src/karura_like.rs | 337 ++++++++++++++++++ .../statemine-like-example/src/kusama_like.rs | 243 +++++++++++++ .../statemine-like-example/src/lib.rs | 210 +++++++++++ .../src/statemine_like.rs | 321 +++++++++++++++++ 7 files changed, 1178 insertions(+) create mode 100644 xcm/xcm-simulator/statemine-like-example/Cargo.toml create mode 100644 xcm/xcm-simulator/statemine-like-example/src/karura_like.rs create mode 100644 xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs create mode 100644 xcm/xcm-simulator/statemine-like-example/src/lib.rs create mode 100644 xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs diff --git a/Cargo.lock b/Cargo.lock index 3918fd8ae846..0556c1874754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4699,6 +4699,19 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13370dae44474229701bb69b90b4f4dca6404cb0357a2d50d635f1171dc3aa7b" +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#4d28ebeb8b027ca0227fe7779c5beb70a7b56467" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" @@ -12277,6 +12290,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", + "log", "pallet-transaction-payment", "parity-scale-codec", "polkadot-parachain", @@ -12343,6 +12357,30 @@ dependencies = [ "xcm-simulator", ] +[[package]] +name = "xcm-statemine-like-example" +version = "0.9.8" +dependencies = [ + "frame-support", + "frame-system", + "pallet-assets", + "pallet-balances", + "pallet-xcm", + "parity-scale-codec", + "paste", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-runtime-parachains", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "xcm", + "xcm-builder", + "xcm-executor", + "xcm-simulator", +] + [[package]] name = "yamux" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 03712f321752..7ef60e6f6d2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "xcm/xcm-executor", "xcm/xcm-simulator", "xcm/xcm-simulator/example", + "xcm/xcm-simulator/statemine-like-example", "xcm/pallet-xcm", "node/client", "node/collation-generation", diff --git a/xcm/xcm-simulator/statemine-like-example/Cargo.toml b/xcm/xcm-simulator/statemine-like-example/Cargo.toml new file mode 100644 index 000000000000..b1dab1491380 --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "xcm-statemine-like-example" +version = "0.9.8" +authors = ["Parity Technologies "] +description = "Examples of XCM interactions with a Statemine-like chain." +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0" } +paste = "1.0.5" + +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-assets = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } + +xcm = { path = "../../" } +xcm-simulator = { path = "../" } +xcm-executor = { path = "../../xcm-executor" } +xcm-builder = { path = "../../xcm-builder" } +pallet-xcm = { path = "../../pallet-xcm" } +polkadot-core-primitives = { path = "../../../core-primitives"} +polkadot-runtime-parachains = { path = "../../../runtime/parachains" } +polkadot-parachain = { path = "../../../parachain" } diff --git a/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs new file mode 100644 index 000000000000..21cddad9e961 --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs @@ -0,0 +1,337 @@ +// Copyright 2021 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 . + +// Copyright 2021 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 . + +//! Parachain runtime mock. + +use codec::{Decode, Encode}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{All, AllowAll}, + weights::{constants::WEIGHT_PER_SECOND, Weight}, +}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{Hash, IdentityLookup}, + AccountId32, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +use pallet_xcm::XcmPassthrough; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{ + DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, +}; +use xcm::{ + v0::{ + Error as XcmError, ExecuteXcm, + Junction::{Parachain, Parent}, + MultiAsset, + MultiLocation::{self, X1}, + NetworkId, Outcome, Xcm, + }, + VersionedXcm, +}; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter, + EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter, + NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4; + pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4; +} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::X1(Parent); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); +} + +pub type LocationToAccountId = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, +); + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const UnitWeightCost: Weight = 1; + pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1); +} + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>; + +pub type XcmRouter = super::ParachainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed ok. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::execute_xcm(location.into(), xcm, max_weight) { + Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), + Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = + VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + }, + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + } + } + limit + } + } +} + +impl mock_msg_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = All<(MultiLocation, Xcm)>; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = (); + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + } +); diff --git a/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs b/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs new file mode 100644 index 000000000000..3e5ec132f5c1 --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs @@ -0,0 +1,243 @@ +// Copyright 2021 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 . + +use frame_support::{ + construct_runtime, parameter_types, + traits::{All, AllowAll}, + weights::Weight, +}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use xcm::opaque::v0::MultiAsset::{self, AllConcreteFungible}; +use xcm::v0::{Junction::*, MultiLocation::{self, *}, NetworkId, Xcm}; +use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, ChildParachainAsNative, + ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, FixedWeightBounds, + IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, IsChildSystemParachain, AllowUnpaidExecutionFrom +}; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +// copied from kusama constants +pub const UNITS: Balance = 1_000_000_000_000; +pub const CENTS: Balance = UNITS / 30_000; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1 * CENTS; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl shared::Config for Runtime {} + +impl configuration::Config for Runtime {} + +// aims to closely emulate the Kusama XcmConfig +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::Null; + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub Ancestry: MultiLocation = MultiLocation::Null; + pub CheckAccount: AccountId = XcmPallet::check_account(); +} + +pub type SovereignAccountOf = + (ChildParachainConvertsVia, AccountId32Aliases); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, CheckAccount>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = 1_000_000_000; + pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); +} + +parameter_types! { + pub const KusamaForStatemint: (MultiAsset, MultiLocation) = + (AllConcreteFungible { id: Null }, X1(Parachain(1000))); +} +pub type TrustedTeleporters = ( + xcm_builder::Case, +); + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom>, + // Unused/Untested + AllowUnpaidExecutionFrom>, +); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = super::RelayChainXcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = TrustedTeleporters; + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +pub struct OnlyWithdrawTeleportForAccounts; +impl frame_support::traits::Contains<(MultiLocation, Xcm)> for OnlyWithdrawTeleportForAccounts { + fn contains((ref origin, ref msg): &(MultiLocation, Xcm)) -> bool { + use xcm::v0::{ + Xcm::WithdrawAsset, Order::{BuyExecution, InitiateTeleport, DepositAsset}, + MultiAsset::{All, ConcreteFungible}, Junction::AccountId32, + }; + match origin { + // Root and council are are allowed to execute anything. + Null | X1(Plurality { .. }) => true, + X1(AccountId32 { .. }) => { + // An account ID trying to send a message. We ensure that it's sensible. + // This checks that it's of the form: + // WithdrawAsset { + // assets: [ ConcreteFungible { id: Null } ], + // effects: [ BuyExecution, InitiateTeleport { + // assets: All, + // dest: Parachain, + // effects: [ BuyExecution, DepositAssets { + // assets: All, + // dest: AccountId32, + // } ] + // } ] + // } + matches!(msg, WithdrawAsset { ref assets, ref effects } + if assets.len() == 1 + && matches!(assets[0], ConcreteFungible { id: Null, .. }) + && effects.len() == 2 + && matches!(effects[0], BuyExecution { .. }) + && matches!(effects[1], InitiateTeleport { ref assets, dest: X1(Parachain(..)), ref effects } + if assets.len() == 1 + && matches!(assets[0], All) + && effects.len() == 2 + && matches!(effects[0], BuyExecution { .. }) + && matches!(effects[1], DepositAsset { ref assets, dest: X1(AccountId32{..}) } + if assets.len() == 1 + && matches!(assets[0], All) + ) + ) + ) + } + // Nobody else is allowed to execute anything. + _ => false, + } + } +} + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = super::RelayChainXcmRouter; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + // ...but they must match our filter, which requires them to be a simple withdraw + teleport. + type XcmExecuteFilter = OnlyWithdrawTeleportForAccounts; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = All<(MultiLocation, Vec)>; + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl ump::Config for Runtime { + type Event = Event; + type UmpSink = ump::XcmSink, Runtime>; + type FirstMessageFactorPercent = FirstMessageFactorPercent; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParasOrigin: origin::{Pallet, Origin}, + ParasUmp: ump::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + } +); diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs new file mode 100644 index 000000000000..318f13c0729c --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -0,0 +1,210 @@ +// Copyright 2021 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 . + +mod karura_like; +mod kusama_like; +mod statemine_like; + +use sp_runtime::AccountId32; +use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; + +pub const ALICE: AccountId32 = AccountId32::new([42u8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000; + +pub const KARURA_ID: u32 = 2000; +pub const STATEMINE_ID: u32 = 1000; + +decl_test_parachain! { + pub struct KaruraLike { + Runtime = karura_like::Runtime, + XcmpMessageHandler = karura_like::MsgQueue, + DmpMessageHandler = karura_like::MsgQueue, + new_ext = karura_like_ext(KARURA_ID), + } +} + +decl_test_relay_chain! { + pub struct KusamaLike { + Runtime = kusama_like::Runtime, + XcmConfig = kusama_like::XcmConfig, + new_ext = kusama_like_ext(), + } +} + +decl_test_parachain! { + pub struct StatemineLike { + Runtime = statemine_like::Runtime, + XcmpMessageHandler = statemine_like::MsgQueue, + DmpMessageHandler = statemine_like::MsgQueue, + new_ext = statemine_like_ext(STATEMINE_ID), + } +} + +decl_test_network! { + pub struct MockNet { + relay_chain = KusamaLike, + parachains = vec![ + (STATEMINE_ID, StatemineLike), + (KARURA_ID, KaruraLike), + ], + } +} + +pub fn karura_like_ext(para_id: u32) -> sp_io::TestExternalities { + use karura_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let balances = vec![ + (ALICE, INITIAL_BALANCE) + ]; + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub fn kusama_like_ext() -> sp_io::TestExternalities { + use kusama_like::{AccountId, Runtime, System}; + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let karura_acc: AccountId = ParaId::from(KARURA_ID).into_account(); + let statemine_acc: AccountId = ParaId::from(STATEMINE_ID).into_account(); + + let balances = vec![ + (ALICE, INITIAL_BALANCE), + (statemine_acc, INITIAL_BALANCE), + (karura_acc, INITIAL_BALANCE), + ]; + + pallet_balances::GenesisConfig:: { + balances, + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub fn statemine_like_ext(para_id: u32) -> sp_io::TestExternalities { + use statemine_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let balances = vec![ + (ALICE, INITIAL_BALANCE) + ]; + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + +pub type KaruraPalletXcm = pallet_xcm::Pallet; +pub type KusamaPalletXcm = pallet_xcm::Pallet; +pub type StateminePalletXcm = pallet_xcm::Pallet; + +#[cfg(test)] +mod tests { + use super::*; + + use frame_support::{assert_ok, weights::Weight}; + use xcm::v0::{ + Junction::{Parachain, Parent}, + MultiAsset::*, + MultiLocation::*, + Order, + }; + use xcm::opaque::v0::prelude::*; + use xcm_simulator::TestExt; + + // Construct a `BuyExecution` order. + fn buy_execution(debt: Weight) -> Order { + use xcm::opaque::v0::prelude::*; + Order::BuyExecution { + fees: All, + weight: 0, + debt, + halt_on_error: false, + xcm: vec![], + } + } + + /// Scenario: + /// A parachain wants to move KSM from Kusama to Statemine. + /// It withdraws funds and then teleports them to the destination. + /// + /// Asserts that the balances are updated accordingly and the correct XCM is sent. + #[test] + fn teleport_to_statemine_works() { + let amount = 10 * kusama_like::ExistentialDeposit::get(); + + KaruraLike::execute_with(|| { + let weight = 3 * kusama_like::BaseXcmWeight::get(); + let message = Xcm::WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount }], + effects: vec![ + buy_execution(weight), + Order::InitiateTeleport { + assets: vec![All], + dest: Parachain(STATEMINE_ID).into(), + effects: vec![ + buy_execution(weight), + Order::DepositAsset { assets: vec![All], dest: X2(Parent, Parachain(KARURA_ID)) }, + ], + }, + ], + }; + assert_ok!(KaruraPalletXcm::send_xcm( + Null, + X1(Parent), + message.clone(), + )); + }); + + KusamaLike::execute_with(|| { + let karura_acc: kusama_like::AccountId = ParaId::from(KARURA_ID).into_account(); + assert_eq!(kusama_like::Balances::free_balance(karura_acc), INITIAL_BALANCE - amount); + }); + + StatemineLike::execute_with(|| { + use polkadot_parachain::primitives::Sibling; + use xcm_builder::SiblingParachainConvertsVia; + use xcm_executor::traits::Convert; + let karura_acc = SiblingParachainConvertsVia::::convert( + X2(Parent, Parachain(KARURA_ID))).unwrap(); + assert_eq!(statemine_like::Balances::free_balance(karura_acc), amount); + }); + } +} diff --git a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs new file mode 100644 index 000000000000..e0b17db98cfa --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs @@ -0,0 +1,321 @@ +// Copyright 2021 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 . + +//! Statemine-like runtime mock. + +use codec::{Decode, Encode}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{All, AllowAll}, + weights::{constants::WEIGHT_PER_SECOND, Weight}, +}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{Hash, IdentityLookup}, + AccountId32, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +use pallet_xcm::XcmPassthrough; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{ + DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, +}; +use xcm::{ + v0::{ + Error as XcmError, ExecuteXcm, + Junction::{Parachain, Parent}, + MultiAsset, + MultiLocation::{self, X1}, + NetworkId, Outcome, Xcm, + }, + VersionedXcm, +}; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter, + EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter, + NativeAsset, ParentIsDefault, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4; + pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4; +} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::X1(Parent); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); +} + +pub type LocationToAccountId = ( + ParentIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, +); + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const UnitWeightCost: Weight = 1; + pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1); +} + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>; + +pub type XcmRouter = super::ParachainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = NativeAsset; + type IsTeleporter = NativeAsset; + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed ok. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::execute_xcm(location.into(), xcm, max_weight) { + Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), + Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = + VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + }, + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + } + } + limit + } + } +} + +impl mock_msg_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = All<(MultiLocation, Xcm)>; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = (); + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + } +); From 0ca47e0117fbbb4f07ab38d5c637db0973243903 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Sun, 1 Aug 2021 21:35:35 +0200 Subject: [PATCH 08/32] update cargo.lock --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0556c1874754..183f623b5632 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12290,7 +12290,6 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", - "log", "pallet-transaction-payment", "parity-scale-codec", "polkadot-parachain", From 8b5f212492f24667ab126c6c9cb594d14e8bb4b1 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 10:57:28 +0200 Subject: [PATCH 09/32] add ReceivedDmp storage value to MockMsgQueue pallet to store all received messages for testing purposes and to test QueryHolding as a success response message --- xcm/xcm-simulator/example/src/lib.rs | 43 +++++++++++++++++--- xcm/xcm-simulator/example/src/parachain.rs | 9 +++- xcm/xcm-simulator/example/src/relay_chain.rs | 22 +--------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index a49698e9425c..53fe7946a5fc 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -221,7 +221,33 @@ mod tests { }); ParaA::execute_with(|| { - // free execution, full amount received + use xcm::opaque::v0::NetworkId; + use xcm_simulator::{BuyExecution, DepositAsset}; + // check message received + let expected_message = ( + X1(Parent), + ReserveAssetDeposit { + assets: vec![ConcreteFungible { id: X1(Parent), amount: 123 }], + effects: vec![ + BuyExecution { + fees: All, + weight: 0, + debt: 123, + halt_on_error: false, + xcm: vec![], + }, + DepositAsset { + assets: vec![All], + dest: X1(Junction::AccountId32 { + network: NetworkId::Any, + id: ALICE.into(), + }), + }, + ], + }, + ); + assert_eq!(parachain::MsgQueue::received_dmp(), vec![expected_message]); + // check message execution with full amount received assert_eq!( pallet_balances::Pallet::::free_balance(&ALICE), INITIAL_BALANCE + 123 @@ -286,7 +312,7 @@ mod tests { assets: vec![All], dest: OnlyChild.into(), // invalid destination }, - // is not triggered becasue the deposit fails + // is not triggered because the deposit fails Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, ], }; @@ -306,7 +332,6 @@ mod tests { // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 0); - assert_eq!(relay_chain::sent_xcm(), vec![]); }); // Now send a message which fully succeeds on the relay chain @@ -329,7 +354,7 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); - // Check that transfer was executed and response message was sent + // Check that transfer was executed Relay::execute_with(|| { let spent = 20; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); @@ -341,7 +366,15 @@ mod tests { // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); - assert_eq!(relay_chain::sent_xcm(), vec![]); + }); + + // Check that QueryResponse message was received + ParaB::execute_with(|| { + use xcm::opaque::v0::Response::Assets; + assert_eq!( + parachain::MsgQueue::received_dmp(), + vec![(X1(Parent), QueryResponse { query_id: 1234, response: Assets(vec![]) })] + ); }); } } diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index f4ad471ff697..854bf1906eeb 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -175,6 +175,12 @@ pub mod mock_msg_queue { #[pallet::getter(fn parachain_id)] pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn received_dmp)] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = + StorageValue<_, Vec<(MultiLocation, Xcm)>, ValueQuery>; + impl Get for Pallet { fn get() -> ParaId { Self::parachain_id() @@ -275,7 +281,8 @@ pub mod mock_msg_queue { Self::deposit_event(Event::UnsupportedVersion(id)); }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); + >::mutate(|y| y.push((Parent.into(), x))); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, } diff --git a/xcm/xcm-simulator/example/src/relay_chain.rs b/xcm/xcm-simulator/example/src/relay_chain.rs index 1796af898759..c69f20d05eaf 100644 --- a/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/xcm/xcm-simulator/example/src/relay_chain.rs @@ -26,11 +26,7 @@ use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use polkadot_parachain::primitives::Id as ParaId; use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use sp_std::cell::RefCell; -use xcm::{ - opaque::v0::{Result as XcmResult, SendXcm, Xcm}, - v0::{MultiAsset, MultiLocation, NetworkId}, -}; +use xcm::v0::{MultiAsset, MultiLocation, NetworkId}; use xcm_builder::{ AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, @@ -124,20 +120,6 @@ parameter_types! { pub type XcmRouter = super::RelayChainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom>; -thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); -} -pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { - SENT_XCM.with(|q| (*q.borrow()).clone()) -} -pub struct TestSendXcm; -impl SendXcm for TestSendXcm { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); - Ok(()) - } -} - pub struct XcmConfig; impl Config for XcmConfig { type Call = Call; @@ -158,7 +140,7 @@ pub type LocalOriginToLocation = SignedToAccountId32; - type XcmRouter = (XcmRouter, TestSendXcm); + type XcmRouter = XcmRouter; // Anyone can execute XCM messages locally... type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = (); From 6e4e6db4cb406869296f88579b5c21a9db2c72a4 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:35:11 +0200 Subject: [PATCH 10/32] improve ump docs --- runtime/parachains/src/ump.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs index c0a8af36f8a8..0b1dfb3b6679 100644 --- a/runtime/parachains/src/ump.rs +++ b/runtime/parachains/src/ump.rs @@ -42,15 +42,16 @@ pub use pallet::*; /// It is possible that by the time the message is sank the origin parachain was offboarded. It is /// up to the implementer to check that if it cares. pub trait UmpSink { - /// Process an incoming upward message and return the amount of weight it consumed, or `None` if - /// it did not begin processing a message since it would otherwise exceed `max_weight`. + /// Process an incoming upward message and return the amount of weight it consumed, or + /// `Err(message_id, required_weight)` if it did not begin processing a message since it would + /// otherwise exceed `max_weight`. /// /// See the trait docs for more details. fn process_upward_message(origin: ParaId, msg: &[u8], max_weight: Weight) -> Result; } /// An implementation of a sink that just swallows the message without consuming any weight. Returns -/// `Some(0)` indicating that no messages existed for it to process. +/// `Ok(0)` indicating that no messages existed for it to process. impl UmpSink for () { fn process_upward_message(_: ParaId, _: &[u8], _: Weight) -> Result { Ok(0) From f0e3ee3f304447834425e70e585c9de66c67468c Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:47:34 +0200 Subject: [PATCH 11/32] make xcm-simulator XCM sending asynchronous --- xcm/xcm-simulator/example/src/lib.rs | 8 ++ xcm/xcm-simulator/src/lib.rs | 103 +++++++++++--- xcm/xcm-test-utils/Cargo.toml | 45 ++++++ xcm/xcm-test-utils/src/kusama_tests.rs | 100 +++++++++++++ xcm/xcm-test-utils/src/lib.rs | 24 ++++ xcm/xcm-test-utils/src/tests.rs | 187 +++++++++++++++++++++++++ 6 files changed, 448 insertions(+), 19 deletions(-) create mode 100644 xcm/xcm-test-utils/Cargo.toml create mode 100644 xcm/xcm-test-utils/src/kusama_tests.rs create mode 100644 xcm/xcm-test-utils/src/lib.rs create mode 100644 xcm/xcm-test-utils/src/tests.rs diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index f318409bf187..0e7bdb402030 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -128,6 +128,8 @@ mod tests { )); }); + RelayChainXcmRouter::process_messages().unwrap(); + ParaA::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -155,6 +157,8 @@ mod tests { )); }); + ParachainXcmRouter::::process_messages().unwrap(); + Relay::execute_with(|| { use relay_chain::{Event, System}; assert!(System::events() @@ -182,6 +186,8 @@ mod tests { )); }); + ParachainXcmRouter::::process_messages().unwrap(); + ParaB::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -204,6 +210,8 @@ mod tests { )); }); + RelayChainXcmRouter::process_messages().unwrap(); + ParaA::execute_with(|| { // free execution, full amount received assert_eq!( diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 096d62b61695..ba24dc08b483 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -16,7 +16,7 @@ //! Test kit to simulate cross-chain message passing and XCM execution -pub use codec::Encode; +pub use codec::{Decode, Encode}; pub use paste; pub use frame_support::{traits::Get, weights::Weight}; @@ -168,6 +168,18 @@ macro_rules! __impl_ext { }; } +use sp_std::collections::vec_deque::VecDeque; + +thread_local! { + pub static PARA_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); +} + +thread_local! { + pub static RELAY_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); +} + #[macro_export] macro_rules! decl_test_network { ( @@ -190,27 +202,57 @@ macro_rules! decl_test_network { /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); + impl> ParachainXcmRouter { + fn process_messages() -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; + + while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $crate::X1($crate::Parent) => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); + let r = <$relay_chain>::process_upward_message( + para_id, &encoded[..], + $crate::Weight::max_value(), + ); + if let Err((id, required)) = r { + return Err($crate::XcmError::WeightLimitReached(required)); + } + }, + $( + $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); + let messages = vec![(para_id, 1, &encoded[..])]; + let _weight = <$parachain>::handle_xcmp_messages( + messages.into_iter(), + $crate::Weight::max_value(), + ); + }, + )* + _ => { + return Err($crate::XcmError::CannotReachDestination(destination, message)); + } + } + } + + Ok(()) + } + } + impl> $crate::SendXcm for ParachainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; - match destination { + match destination.clone() { $crate::X1($crate::Parent) => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let _ = <$relay_chain>::process_upward_message( - T::get(), &encoded[..], - $crate::Weight::max_value(), - ); + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); Ok(()) }, $( $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); - let messages = vec![(T::get(), 1, &encoded[..])]; - let _ = <$parachain>::handle_xcmp_messages( - messages.into_iter(), - $crate::Weight::max_value(), - ); + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); Ok(()) }, )* @@ -221,18 +263,41 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; + + impl RelayChainXcmRouter { + fn process_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; + + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $( + $crate::X1($crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + } + } + + Ok(()) + } + } + impl $crate::SendXcm for RelayChainXcmRouter { fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { use $crate::DmpMessageHandlerT; - match destination { + match destination.clone() { $( $crate::X1($crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - let messages = vec![(1, encoded)]; - let _ = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); + $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((destination, message))); Ok(()) }, )* diff --git a/xcm/xcm-test-utils/Cargo.toml b/xcm/xcm-test-utils/Cargo.toml new file mode 100644 index 000000000000..bfa6c5237dde --- /dev/null +++ b/xcm/xcm-test-utils/Cargo.toml @@ -0,0 +1,45 @@ +[package] +authors = ["Parity Technologies "] +edition = "2018" +name = "xcm-test-utils" +description = "XCM testing tools" +version = "0.1.0" + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-xcm = { path = "../pallet-xcm", default-features = false } + +polkadot-parachain = { path = "../../parachain" } +polkadot-primitives = { path = "../../primitives" } +polkadot-runtime-parachains = { path = "../../runtime/parachains" } + +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +xcm = { path = "..", default-features = false } +xcm-builder = { path = "../xcm-builder", default-features = false } +xcm-executor = { path = "../xcm-executor", default-features = false } + +log = "0.4" + +[dev-dependencies] +kusama-runtime = { path = "../../runtime/kusama" } + +[features] +default = ["std"] +std = [ + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] \ No newline at end of file diff --git a/xcm/xcm-test-utils/src/kusama_tests.rs b/xcm/xcm-test-utils/src/kusama_tests.rs new file mode 100644 index 000000000000..e2310bd70748 --- /dev/null +++ b/xcm/xcm-test-utils/src/kusama_tests.rs @@ -0,0 +1,100 @@ + +use kusama::XcmConfig; +use xcm_executor::XcmExecutor; +use MultiLocation::*; +use xcm::v0::ExecuteXcm; +use xcm::opaque::v0::Outcome; +use xcm::opaque::v0::prelude::*; +use sp_std::prelude::*; +use polkadot_primitives::v1::AccountId; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; + +use kusama_runtime as kusama; + +pub const ALICE: AccountId = AccountId::new([0u8; 32]); +pub const PARA_ID: u32 = 2000; +pub const INITIAL_BALANCE: u128 = 100_000_000_000; + +pub fn kusama_ext() -> sp_io::TestExternalities { + use kusama::{Runtime, System}; + + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + let parachain_acc: AccountId = ParaId::from(PARA_ID).into_account(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, INITIAL_BALANCE), + (parachain_acc, INITIAL_BALANCE) + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + // use polkadot_primitives::v1::{MAX_CODE_SIZE, MAX_POV_SIZE}; + // // default parachains host configuration from polkadot's `chain_spec.rs` + // kusama::ParachainsConfigurationConfig { + // config: polkadot_runtime_parachains::configuration::HostConfiguration { + // validation_upgrade_frequency: 1u32, + // validation_upgrade_delay: 1, + // code_retention_period: 1200, + // max_code_size: MAX_CODE_SIZE, + // max_pov_size: MAX_POV_SIZE, + // max_head_data_size: 32 * 1024, + // group_rotation_frequency: 20, + // chain_availability_period: 4, + // thread_availability_period: 4, + // max_upward_queue_count: 8, + // max_upward_queue_size: 1024 * 1024, + // max_downward_message_size: 1024, + // // this is approximatelly 4ms. + // // + // // Same as `4 * frame_support::weights::WEIGHT_PER_MILLIS`. We don't bother with + // // an import since that's a made up number and should be replaced with a constant + // // obtained by benchmarking anyway. + // ump_service_total_weight: 4 * 1_000_000_000, + // max_upward_message_size: 1024 * 1024, + // max_upward_message_num_per_candidate: 5, + // hrmp_open_request_ttl: 5, + // hrmp_sender_deposit: 0, + // hrmp_recipient_deposit: 0, + // hrmp_channel_max_capacity: 8, + // hrmp_channel_max_total_size: 8 * 1024, + // hrmp_max_parachain_inbound_channels: 4, + // hrmp_max_parathread_inbound_channels: 4, + // hrmp_channel_max_message_size: 1024 * 1024, + // hrmp_max_parachain_outbound_channels: 4, + // hrmp_max_parathread_outbound_channels: 4, + // hrmp_max_message_num_per_candidate: 5, + // dispute_period: 6, + // no_show_slots: 2, + // n_delay_tranches: 25, + // needed_approvals: 2, + // relay_vrf_modulo_samples: 2, + // zeroth_delay_tranche_width: 0, + // ..Default::default() + // }, + // }.assimilate_storage(&mut t).unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +#[test] +fn kusama_executor_works() { + use xcm::v0::Xcm; + + kusama_ext().execute_with(|| { + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID).into(), Xcm::WithdrawAsset { + assets: vec![ConcreteFungible { id: Null, amount: 10_000_000_000 }], + effects: vec![ + Order::BuyExecution { fees: All, weight: 0, debt: 2_000_000_000, halt_on_error: false, xcm: vec![] } + ], + }, 2_000_000_000); + assert_eq!(r, Outcome::Complete(2_000_000_000)); + }) +} diff --git a/xcm/xcm-test-utils/src/lib.rs b/xcm/xcm-test-utils/src/lib.rs new file mode 100644 index 000000000000..131fc7f301ce --- /dev/null +++ b/xcm/xcm-test-utils/src/lib.rs @@ -0,0 +1,24 @@ +use xcm::opaque::v0::opaque::Xcm; +use xcm::opaque::v0::prelude::*; +use sp_std::cell::RefCell; +use sp_std::prelude::*; + +#[cfg(test)] +mod kusama_tests; + +// #[cfg(test)] +// mod tests; + +thread_local! { + pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); +} +pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} +pub struct MockXcmSender; +impl SendXcm for MockXcmSender { + fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { + SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); + Ok(()) + } +} diff --git a/xcm/xcm-test-utils/src/tests.rs b/xcm/xcm-test-utils/src/tests.rs new file mode 100644 index 000000000000..929ebdc8dbcc --- /dev/null +++ b/xcm/xcm-test-utils/src/tests.rs @@ -0,0 +1,187 @@ +// use super::*; +use frame_support::{construct_runtime, parameter_types, traits::{All, AllowAll}, weights::Weight}; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; + +use polkadot_parachain::primitives::Id as ParaId; +use polkadot_runtime_parachains::{configuration, origin, shared, ump}; +use xcm::v0::{MultiLocation, NetworkId}; +use xcm::opaque::v0::Outcome; +use xcm::opaque::v0::MultiAsset; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, + ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, + FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, +}; +use xcm_executor::XcmExecutor; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +impl shared::Config for Runtime {} + +impl configuration::Config for Runtime {} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::Null; + pub const KusamaNetwork: NetworkId = NetworkId::Kusama; + pub const AnyNetwork: NetworkId = NetworkId::Any; + pub Ancestry: MultiLocation = MultiLocation::Null; + pub UnitWeightCost: Weight = 1_000; +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, +); + +pub type LocalAssetTransactor = + XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = 1_000; + pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); +} + +pub type Barrier = AllowUnpaidExecutionFrom>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = MockXcmSender; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = LocalOriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmRouter = MockXcmSender; + // Anyone can execute XCM messages locally... + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecuteFilter = (); + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = All<(MultiLocation, Vec)>; + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +parameter_types! { + pub const FirstMessageFactorPercent: u64 = 100; +} + +impl ump::Config for Runtime { + type Event = Event; + type UmpSink = ump::XcmSink, Runtime>; + type FirstMessageFactorPercent = FirstMessageFactorPercent; +} + +impl origin::Config for Runtime {} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + ParasOrigin: origin::{Pallet, Origin}, + ParasUmp: ump::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + } +); + +use crate::MockXcmSender; +use crate::sent_xcm; +use MultiLocation::*; +use xcm::v0::SendXcm; +use xcm::v0::ExecuteXcm; + +#[test] +fn it_works() { + use xcm::opaque::v0::opaque::Xcm; + assert!(MockXcmSender::send_xcm(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + }).is_ok()); + assert_eq!(sent_xcm(), vec![(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + })]); +} + +#[test] +fn executor_works() { + use xcm::v0::Xcm; + let r = XcmExecutor::::execute_xcm(Null, Xcm::WithdrawAsset { + assets: vec![], effects: vec![] + }, 1000); + assert_eq!(r, Outcome::Complete(1000)); +} From 52bc4f04eb058e49f331d70149887f7b6ed62448 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 13:50:01 +0200 Subject: [PATCH 12/32] remove xcm-test-utils --- xcm/xcm-test-utils/Cargo.toml | 45 ------ xcm/xcm-test-utils/src/kusama_tests.rs | 100 ------------- xcm/xcm-test-utils/src/lib.rs | 24 ---- xcm/xcm-test-utils/src/tests.rs | 187 ------------------------- 4 files changed, 356 deletions(-) delete mode 100644 xcm/xcm-test-utils/Cargo.toml delete mode 100644 xcm/xcm-test-utils/src/kusama_tests.rs delete mode 100644 xcm/xcm-test-utils/src/lib.rs delete mode 100644 xcm/xcm-test-utils/src/tests.rs diff --git a/xcm/xcm-test-utils/Cargo.toml b/xcm/xcm-test-utils/Cargo.toml deleted file mode 100644 index bfa6c5237dde..000000000000 --- a/xcm/xcm-test-utils/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -authors = ["Parity Technologies "] -edition = "2018" -name = "xcm-test-utils" -description = "XCM testing tools" -version = "0.1.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-xcm = { path = "../pallet-xcm", default-features = false } - -polkadot-parachain = { path = "../../parachain" } -polkadot-primitives = { path = "../../primitives" } -polkadot-runtime-parachains = { path = "../../runtime/parachains" } - -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } - -xcm = { path = "..", default-features = false } -xcm-builder = { path = "../xcm-builder", default-features = false } -xcm-executor = { path = "../xcm-executor", default-features = false } - -log = "0.4" - -[dev-dependencies] -kusama-runtime = { path = "../../runtime/kusama" } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "frame-system/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm/std", - "xcm-builder/std", - "xcm-executor/std", -] \ No newline at end of file diff --git a/xcm/xcm-test-utils/src/kusama_tests.rs b/xcm/xcm-test-utils/src/kusama_tests.rs deleted file mode 100644 index e2310bd70748..000000000000 --- a/xcm/xcm-test-utils/src/kusama_tests.rs +++ /dev/null @@ -1,100 +0,0 @@ - -use kusama::XcmConfig; -use xcm_executor::XcmExecutor; -use MultiLocation::*; -use xcm::v0::ExecuteXcm; -use xcm::opaque::v0::Outcome; -use xcm::opaque::v0::prelude::*; -use sp_std::prelude::*; -use polkadot_primitives::v1::AccountId; -use polkadot_parachain::primitives::Id as ParaId; -use sp_runtime::traits::AccountIdConversion; - -use kusama_runtime as kusama; - -pub const ALICE: AccountId = AccountId::new([0u8; 32]); -pub const PARA_ID: u32 = 2000; -pub const INITIAL_BALANCE: u128 = 100_000_000_000; - -pub fn kusama_ext() -> sp_io::TestExternalities { - use kusama::{Runtime, System}; - - let mut t = frame_system::GenesisConfig::default() - .build_storage::() - .unwrap(); - - let parachain_acc: AccountId = ParaId::from(PARA_ID).into_account(); - - pallet_balances::GenesisConfig:: { - balances: vec![ - (ALICE, INITIAL_BALANCE), - (parachain_acc, INITIAL_BALANCE) - ], - } - .assimilate_storage(&mut t) - .unwrap(); - - // use polkadot_primitives::v1::{MAX_CODE_SIZE, MAX_POV_SIZE}; - // // default parachains host configuration from polkadot's `chain_spec.rs` - // kusama::ParachainsConfigurationConfig { - // config: polkadot_runtime_parachains::configuration::HostConfiguration { - // validation_upgrade_frequency: 1u32, - // validation_upgrade_delay: 1, - // code_retention_period: 1200, - // max_code_size: MAX_CODE_SIZE, - // max_pov_size: MAX_POV_SIZE, - // max_head_data_size: 32 * 1024, - // group_rotation_frequency: 20, - // chain_availability_period: 4, - // thread_availability_period: 4, - // max_upward_queue_count: 8, - // max_upward_queue_size: 1024 * 1024, - // max_downward_message_size: 1024, - // // this is approximatelly 4ms. - // // - // // Same as `4 * frame_support::weights::WEIGHT_PER_MILLIS`. We don't bother with - // // an import since that's a made up number and should be replaced with a constant - // // obtained by benchmarking anyway. - // ump_service_total_weight: 4 * 1_000_000_000, - // max_upward_message_size: 1024 * 1024, - // max_upward_message_num_per_candidate: 5, - // hrmp_open_request_ttl: 5, - // hrmp_sender_deposit: 0, - // hrmp_recipient_deposit: 0, - // hrmp_channel_max_capacity: 8, - // hrmp_channel_max_total_size: 8 * 1024, - // hrmp_max_parachain_inbound_channels: 4, - // hrmp_max_parathread_inbound_channels: 4, - // hrmp_channel_max_message_size: 1024 * 1024, - // hrmp_max_parachain_outbound_channels: 4, - // hrmp_max_parathread_outbound_channels: 4, - // hrmp_max_message_num_per_candidate: 5, - // dispute_period: 6, - // no_show_slots: 2, - // n_delay_tranches: 25, - // needed_approvals: 2, - // relay_vrf_modulo_samples: 2, - // zeroth_delay_tranche_width: 0, - // ..Default::default() - // }, - // }.assimilate_storage(&mut t).unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext -} - -#[test] -fn kusama_executor_works() { - use xcm::v0::Xcm; - - kusama_ext().execute_with(|| { - let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID).into(), Xcm::WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount: 10_000_000_000 }], - effects: vec![ - Order::BuyExecution { fees: All, weight: 0, debt: 2_000_000_000, halt_on_error: false, xcm: vec![] } - ], - }, 2_000_000_000); - assert_eq!(r, Outcome::Complete(2_000_000_000)); - }) -} diff --git a/xcm/xcm-test-utils/src/lib.rs b/xcm/xcm-test-utils/src/lib.rs deleted file mode 100644 index 131fc7f301ce..000000000000 --- a/xcm/xcm-test-utils/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -use xcm::opaque::v0::opaque::Xcm; -use xcm::opaque::v0::prelude::*; -use sp_std::cell::RefCell; -use sp_std::prelude::*; - -#[cfg(test)] -mod kusama_tests; - -// #[cfg(test)] -// mod tests; - -thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); -} -pub fn sent_xcm() -> Vec<(MultiLocation, Xcm)> { - SENT_XCM.with(|q| (*q.borrow()).clone()) -} -pub struct MockXcmSender; -impl SendXcm for MockXcmSender { - fn send_xcm(dest: MultiLocation, msg: Xcm) -> XcmResult { - SENT_XCM.with(|q| q.borrow_mut().push((dest, msg))); - Ok(()) - } -} diff --git a/xcm/xcm-test-utils/src/tests.rs b/xcm/xcm-test-utils/src/tests.rs deleted file mode 100644 index 929ebdc8dbcc..000000000000 --- a/xcm/xcm-test-utils/src/tests.rs +++ /dev/null @@ -1,187 +0,0 @@ -// use super::*; -use frame_support::{construct_runtime, parameter_types, traits::{All, AllowAll}, weights::Weight}; -use sp_core::H256; -use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; - -use polkadot_parachain::primitives::Id as ParaId; -use polkadot_runtime_parachains::{configuration, origin, shared, ump}; -use xcm::v0::{MultiLocation, NetworkId}; -use xcm::opaque::v0::Outcome; -use xcm::opaque::v0::MultiAsset; -use xcm_builder::{ - AccountId32Aliases, AllowUnpaidExecutionFrom, ChildParachainAsNative, ChildParachainConvertsVia, - ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfConcreteFungible, - FixedWeightBounds, IsConcrete, LocationInverter, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, -}; -use xcm_executor::XcmExecutor; - -pub type AccountId = AccountId32; -pub type Balance = u128; - -parameter_types! { - pub const BlockHashCount: u64 = 250; -} - -impl frame_system::Config for Runtime { - type Origin = Origin; - type Call = Call; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type Event = Event; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = AllowAll; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); -} - -parameter_types! { - pub ExistentialDeposit: Balance = 1; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type Balance = Balance; - type Event = Event; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; -} - -impl shared::Config for Runtime {} - -impl configuration::Config for Runtime {} - -parameter_types! { - pub const KsmLocation: MultiLocation = MultiLocation::Null; - pub const KusamaNetwork: NetworkId = NetworkId::Kusama; - pub const AnyNetwork: NetworkId = NetworkId::Any; - pub Ancestry: MultiLocation = MultiLocation::Null; - pub UnitWeightCost: Weight = 1_000; -} - -pub type SovereignAccountOf = ( - ChildParachainConvertsVia, - AccountId32Aliases, -); - -pub type LocalAssetTransactor = - XcmCurrencyAdapter, SovereignAccountOf, AccountId, ()>; - -type LocalOriginConverter = ( - SovereignSignedViaLocation, - ChildParachainAsNative, - SignedAccountId32AsNative, - ChildSystemParachainAsSuperuser, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = 1_000; - pub KsmPerSecond: (MultiLocation, u128) = (KsmLocation::get(), 1); -} - -pub type Barrier = AllowUnpaidExecutionFrom>; - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type Call = Call; - type XcmSender = MockXcmSender; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = (); - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = FixedRateOfConcreteFungible; - type ResponseHandler = (); -} - -pub type LocalOriginToLocation = SignedToAccountId32; - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmRouter = MockXcmSender; - // Anyone can execute XCM messages locally... - type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmExecuteFilter = (); - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = All<(MultiLocation, Vec)>; - type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; - type Weigher = FixedWeightBounds; -} - -parameter_types! { - pub const FirstMessageFactorPercent: u64 = 100; -} - -impl ump::Config for Runtime { - type Event = Event; - type UmpSink = ump::XcmSink, Runtime>; - type FirstMessageFactorPercent = FirstMessageFactorPercent; -} - -impl origin::Config for Runtime {} - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: frame_system::{Pallet, Call, Storage, Config, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - ParasOrigin: origin::{Pallet, Origin}, - ParasUmp: ump::{Pallet, Call, Storage, Event}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, - } -); - -use crate::MockXcmSender; -use crate::sent_xcm; -use MultiLocation::*; -use xcm::v0::SendXcm; -use xcm::v0::ExecuteXcm; - -#[test] -fn it_works() { - use xcm::opaque::v0::opaque::Xcm; - assert!(MockXcmSender::send_xcm(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - }).is_ok()); - assert_eq!(sent_xcm(), vec![(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - })]); -} - -#[test] -fn executor_works() { - use xcm::v0::Xcm; - let r = XcmExecutor::::execute_xcm(Null, Xcm::WithdrawAsset { - assets: vec![], effects: vec![] - }, 1000); - assert_eq!(r, Outcome::Complete(1000)); -} From bfa99132beaf86f5b717e1f6778ded37865ec078 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 16:23:27 +0200 Subject: [PATCH 13/32] Update xcm/xcm-simulator/src/lib.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- xcm/xcm-simulator/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index ba24dc08b483..2a708cd01473 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -173,9 +173,6 @@ use sp_std::collections::vec_deque::VecDeque; thread_local! { pub static PARA_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); -} - -thread_local! { pub static RELAY_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); } From 86f7d510be6a40513aa86e2d17ee67f475aa8ef1 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 18:21:06 +0200 Subject: [PATCH 14/32] use automatic dispatch of message queues to check query response functionality as success message --- xcm/xcm-simulator/example/src/lib.rs | 12 +-- xcm/xcm-simulator/src/lib.rs | 115 +++++++++++++++------------ 2 files changed, 67 insertions(+), 60 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 322215bc4047..d39d0a9ee523 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -138,8 +138,6 @@ mod tests { )); }); - RelayChainXcmRouter::process_messages().unwrap(); - ParaA::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -167,8 +165,6 @@ mod tests { )); }); - ParachainXcmRouter::::process_messages().unwrap(); - Relay::execute_with(|| { use relay_chain::{Event, System}; assert!(System::events() @@ -196,8 +192,6 @@ mod tests { )); }); - ParachainXcmRouter::::process_messages().unwrap(); - ParaB::execute_with(|| { use parachain::{Event, System}; assert!(System::events() @@ -226,8 +220,6 @@ mod tests { assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); }); - RelayChainXcmRouter::process_messages().unwrap(); - ParaA::execute_with(|| { use xcm::opaque::v0::NetworkId; use xcm_simulator::{BuyExecution, DepositAsset}; @@ -355,7 +347,7 @@ mod tests { assets: vec![All], dest: Parachain(2).into(), // valid destination }, - Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + Order::QueryHolding { query_id, dest: Parachain(1).into(), assets: vec![All] }, ], }; // Send withdraw and deposit with query holding @@ -377,7 +369,7 @@ mod tests { }); // Check that QueryResponse message was received - ParaB::execute_with(|| { + ParaA::execute_with(|| { use xcm::opaque::v0::Response::Assets; assert_eq!( parachain::MsgQueue::received_dmp(), diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 2a708cd01473..66cd704d1ffc 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -36,8 +36,16 @@ pub use xcm::{v0::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; pub trait TestExt { + /// Initialize the test environment. fn new_ext() -> sp_io::TestExternalities; + /// Resets the state of the test environment. fn reset_ext(); + /// Execute some code in the context of the test externalities, with manual + /// message processing which requires calling `process_messages()`. + fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R; + /// Execute some code in the context of the test externalities, with + /// automatic processing of messages. + /// Messages are dispatched once the passed closure completes. fn execute_with(execute: impl FnOnce() -> R) -> R; } @@ -161,9 +169,15 @@ macro_rules! __impl_ext { $ext_name.with(|v| *v.borrow_mut() = $new_ext); } - fn execute_with(execute: impl FnOnce() -> R) -> R { + fn execute_without_auto_dispatch(execute: impl FnOnce() -> R) -> R { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } + + fn execute_with(execute: impl FnOnce() -> R) -> R { + let result = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); + process_messages().expect("message processing failure"); + result + } } }; } @@ -196,44 +210,47 @@ macro_rules! decl_test_network { } } + fn process_messages() -> $crate::XcmResult { + process_relay_messages()?; + process_para_messages() + } + /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); - impl> ParachainXcmRouter { - fn process_messages() -> $crate::XcmResult { - use $crate::{UmpSink, XcmpMessageHandlerT}; + fn process_para_messages() -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; - while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( - |b| b.borrow_mut().pop_front()) { - match destination { - $crate::X1($crate::Parent) => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let r = <$relay_chain>::process_upward_message( - para_id, &encoded[..], + while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $crate::X1($crate::Parent) => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); + let r = <$relay_chain>::process_upward_message( + para_id, &encoded[..], + $crate::Weight::max_value(), + ); + if let Err((id, required)) = r { + return Err($crate::XcmError::WeightLimitReached(required)); + } + }, + $( + $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); + let messages = vec![(para_id, 1, &encoded[..])]; + let _weight = <$parachain>::handle_xcmp_messages( + messages.into_iter(), $crate::Weight::max_value(), ); - if let Err((id, required)) = r { - return Err($crate::XcmError::WeightLimitReached(required)); - } }, - $( - $crate::X2($crate::Parent, $crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); - let messages = vec![(para_id, 1, &encoded[..])]; - let _weight = <$parachain>::handle_xcmp_messages( - messages.into_iter(), - $crate::Weight::max_value(), - ); - }, - )* - _ => { - return Err($crate::XcmError::CannotReachDestination(destination, message)); - } + )* + _ => { + return Err($crate::XcmError::CannotReachDestination(destination, message)); } } - - Ok(()) } + + Ok(()) } impl> $crate::SendXcm for ParachainXcmRouter { @@ -261,29 +278,27 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; - impl RelayChainXcmRouter { - fn process_messages() -> $crate::XcmResult { - use $crate::DmpMessageHandlerT; + fn process_relay_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; - while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( - |b| b.borrow_mut().pop_front()) { - match destination { - $( - $crate::X1($crate::Parachain(id)) if id == $para_id => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - // NOTE: RelayChainBlockNumber is hard-coded to 1 - let messages = vec![(1, encoded)]; - let _weight = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); - }, - )* - _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), - } + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination { + $( + $crate::X1($crate::Parachain(id)) if id == $para_id => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), } - - Ok(()) } + + Ok(()) } impl $crate::SendXcm for RelayChainXcmRouter { @@ -303,4 +318,4 @@ macro_rules! decl_test_network { } } }; -} +} \ No newline at end of file From d2b81f96488a97e76e14a94ded86aae2a063a20c Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 18:47:20 +0200 Subject: [PATCH 15/32] clean --- xcm/xcm-simulator/example/src/lib.rs | 97 ++++++++++++++++------------ 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index d39d0a9ee523..11e84d87cf30 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -109,13 +109,13 @@ mod tests { MultiAsset::*, MultiLocation::*, NetworkId, Order, OriginKind, + Response::Assets, Xcm::*, }; use xcm_simulator::TestExt; // Helper function for forming buy execution message fn buy_execution(debt: Weight) -> Order { - use xcm::opaque::v0::prelude::*; Order::BuyExecution { fees: All, weight: 0, debt, halt_on_error: false, xcm: vec![] } } @@ -208,35 +208,33 @@ mod tests { fn reserve_transfer_assets() { MockNet::reset(); + let withdraw_amount = 123; + let max_weight_for_execution = 10; + Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), X1(Parachain(1)), X1(Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }), - vec![ConcreteFungible { id: Null, amount: 123 }], - 123, + vec![ConcreteFungible { id: Null, amount: withdraw_amount }], + max_weight_for_execution, )); let para_account_a = ParaId::from(1).into_account(); - assert_eq!(parachain::Balances::free_balance(¶_account_a), INITIAL_BALANCE + 123); + assert_eq!( + parachain::Balances::free_balance(¶_account_a), + INITIAL_BALANCE + withdraw_amount + ); }); ParaA::execute_with(|| { - use xcm::opaque::v0::NetworkId; - use xcm_simulator::{BuyExecution, DepositAsset}; - // check message received + // Check message received let expected_message = ( X1(Parent), ReserveAssetDeposit { - assets: vec![ConcreteFungible { id: X1(Parent), amount: 123 }], + assets: vec![ConcreteFungible { id: X1(Parent), amount: withdraw_amount }], effects: vec![ - BuyExecution { - fees: All, - weight: 0, - debt: 123, - halt_on_error: false, - xcm: vec![], - }, - DepositAsset { + buy_execution(max_weight_for_execution), + Order::DepositAsset { assets: vec![All], dest: X1(Junction::AccountId32 { network: NetworkId::Any, @@ -247,29 +245,31 @@ mod tests { }, ); assert_eq!(parachain::MsgQueue::received_dmp(), vec![expected_message]); - // check message execution with full amount received + // Check message execution with full amount received assert_eq!( pallet_balances::Pallet::::free_balance(&ALICE), - INITIAL_BALANCE + 123 + INITIAL_BALANCE + withdraw_amount ); }); } /// Scenario: - /// A parachain transfers funds on the relaychain to another parachain's account. + /// 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; + let mut amount_received = 0; + let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: Parachain(2).into() }, ], }; @@ -277,15 +277,16 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_received += send_amount; + Relay::execute_with(|| { - let amount = 10; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount + INITIAL_BALANCE - send_amount ); let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); }); } @@ -298,16 +299,20 @@ mod tests { fn query_holding() { MockNet::reset(); + let send_amount = 10; + let mut amount_spent = 0; + let mut amount_received = 0; + let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + let query_id_set = 1234; + // First send a message which fails on the relay chain ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); let query_id = 1234; let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: OnlyChild.into(), // invalid destination @@ -320,14 +325,15 @@ mod tests { assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_spent += send_amount; + // Check that no transfer was executed and no response message was sent Relay::execute_with(|| { - let amount = 10; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - amount + INITIAL_BALANCE - amount_spent ); // but deposit did not execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); @@ -336,44 +342,49 @@ mod tests { // Now send a message which fully succeeds on the relay chain ParaA::execute_with(|| { - let amount = 10; - let weight = 3 * relay_chain::BaseXcmWeight::get(); - let query_id = 1234; let message = WithdrawAsset { - assets: vec![ConcreteFungible { id: Null, amount }], + assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ - buy_execution(weight), + buy_execution(weight_for_execution), Order::DepositAsset { assets: vec![All], dest: Parachain(2).into(), // valid destination }, - Order::QueryHolding { query_id, dest: Parachain(1).into(), assets: vec![All] }, + Order::QueryHolding { + query_id: query_id_set, + dest: Parachain(1).into(), + assets: vec![All], + }, ], }; // Send withdraw and deposit with query holding assert_ok!(ParachainPalletXcm::send_xcm(Null, X1(Parent), message.clone(),)); }); + amount_spent += send_amount; + amount_received += send_amount; + // Check that transfer was executed Relay::execute_with(|| { - let spent = 20; let para_account_a: relay_chain::AccountId = ParaId::from(1).into_account(); // withdraw did execute assert_eq!( relay_chain::Balances::free_balance(para_account_a), - INITIAL_BALANCE - spent + INITIAL_BALANCE - amount_spent ); // and deposit did execute let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - assert_eq!(relay_chain::Balances::free_balance(para_account_b), 10); + assert_eq!(relay_chain::Balances::free_balance(para_account_b), amount_received); }); // Check that QueryResponse message was received ParaA::execute_with(|| { - use xcm::opaque::v0::Response::Assets; assert_eq!( parachain::MsgQueue::received_dmp(), - vec![(X1(Parent), QueryResponse { query_id: 1234, response: Assets(vec![]) })] + vec![( + X1(Parent), + QueryResponse { query_id: query_id_set, response: Assets(vec![]) } + )] ); }); } From 287cd76e78cc5fa025c87cba8089ebfb4b93ee71 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:48:45 -0400 Subject: [PATCH 16/32] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 66cd704d1ffc..919eaf8f1309 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -278,6 +278,7 @@ macro_rules! decl_test_network { /// XCM router for relay chain. pub struct RelayChainXcmRouter; + /// Process all messages originating from the relay chain. fn process_relay_messages() -> $crate::XcmResult { use $crate::DmpMessageHandlerT; @@ -318,4 +319,4 @@ macro_rules! decl_test_network { } } }; -} \ No newline at end of file +} From 830593487fb307f214107ff413f2f4371cedb6c4 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:48:52 -0400 Subject: [PATCH 17/32] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 919eaf8f1309..6771f52f6119 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -218,6 +218,7 @@ macro_rules! decl_test_network { /// XCM router for parachain. pub struct ParachainXcmRouter($crate::PhantomData); + /// Process all messages originating from parachains. fn process_para_messages() -> $crate::XcmResult { use $crate::{UmpSink, XcmpMessageHandlerT}; From 0444d45372f0358808dcc921334ddb5bf1799635 Mon Sep 17 00:00:00 2001 From: Amar Singh Date: Mon, 2 Aug 2021 12:49:07 -0400 Subject: [PATCH 18/32] Update xcm/xcm-simulator/src/lib.rs Co-authored-by: Alexander Popiak --- xcm/xcm-simulator/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 6771f52f6119..f89430879508 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -210,6 +210,9 @@ macro_rules! decl_test_network { } } + /// Process any XCMs in the message buses. + /// + /// Called automatically by `TestExt::execute_with`. fn process_messages() -> $crate::XcmResult { process_relay_messages()?; process_para_messages() From 8e60c1164418f928ef2a6a3ac4713e84baa98eaa Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:17:48 +0200 Subject: [PATCH 19/32] spellcheck nit --- xcm/xcm-simulator/example/src/parachain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 854bf1906eeb..3f03367d4406 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -193,7 +193,7 @@ pub mod mock_msg_queue { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { // XCMP - /// Some XCM was executed ok. + /// Some XCM was executed OK. Success(Option), /// Some XCM failed. Fail(Option, XcmError), From daed1fdc4d5deba432b3bf591068f843eb9061eb Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:40:06 +0200 Subject: [PATCH 20/32] clean --- xcm/xcm-simulator/example/src/lib.rs | 8 +++++--- xcm/xcm-simulator/src/lib.rs | 11 ++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 11e84d87cf30..638e82b1f2c2 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -307,8 +307,6 @@ mod tests { // First send a message which fails on the relay chain ParaA::execute_with(|| { - //let para_account_b: relay_chain::AccountId = ParaId::from(2).into_account(); - let query_id = 1234; let message = WithdrawAsset { assets: vec![ConcreteFungible { id: Null, amount: send_amount }], effects: vec![ @@ -318,7 +316,11 @@ mod tests { dest: OnlyChild.into(), // invalid destination }, // is not triggered because the deposit fails - Order::QueryHolding { query_id, dest: Parachain(2).into(), assets: vec![All] }, + Order::QueryHolding { + query_id: query_id_set, + dest: Parachain(2).into(), + assets: vec![All], + }, ], }; // Send withdraw and deposit with query holding diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index f89430879508..bb907a088daf 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -20,9 +20,6 @@ pub use codec::{Decode, Encode}; pub use paste; pub use frame_support::{traits::Get, weights::Weight}; -pub use sp_io::TestExternalities; -pub use sp_std::{cell::RefCell, marker::PhantomData}; - pub use polkadot_core_primitives::BlockNumber as RelayBlockNumber; pub use polkadot_parachain::primitives::{ DmpMessageHandler as DmpMessageHandlerT, Id as ParaId, XcmpMessageFormat, @@ -32,6 +29,8 @@ pub use polkadot_runtime_parachains::{ dmp, ump::{self, MessageId, UmpSink, XcmSink}, }; +pub use sp_io::TestExternalities; +pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData}; pub use xcm::{v0::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; @@ -92,8 +91,8 @@ macro_rules! decl_test_relay_chain { Self::execute_with(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( - origin, msg, max_weight, - ) + origin, msg, max_weight, + ) }) } } @@ -182,8 +181,6 @@ macro_rules! __impl_ext { }; } -use sp_std::collections::vec_deque::VecDeque; - thread_local! { pub static PARA_MESSAGE_BUS: RefCell)>> = RefCell::new(VecDeque::new()); From 68c725f4a45036979654788cacf5db3480a3ea62 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Mon, 2 Aug 2021 19:42:37 +0200 Subject: [PATCH 21/32] fix fmt in macro --- xcm/xcm-simulator/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index bb907a088daf..2956904f94ef 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -91,8 +91,8 @@ macro_rules! decl_test_relay_chain { Self::execute_with(|| { $crate::ump::XcmSink::<$crate::XcmExecutor<$xcm_config>, $runtime>::process_upward_message( - origin, msg, max_weight, - ) + origin, msg, max_weight, + ) }) } } From 25772457b2913fbea4a1fce406b27a5214884370 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 2 Aug 2021 22:21:34 +0200 Subject: [PATCH 22/32] add moonriver like runtime and reserve based transfer scenario --- .../statemine-like-example/src/lib.rs | 132 ++++- .../src/moonriver_like.rs | 452 ++++++++++++++++++ .../src/statemine_like.rs | 85 +++- 3 files changed, 658 insertions(+), 11 deletions(-) create mode 100644 xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs index 318f13c0729c..8e8463f92156 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/lib.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -16,18 +16,22 @@ mod karura_like; mod kusama_like; +mod moonriver_like; mod statemine_like; use sp_runtime::AccountId32; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; -use polkadot_parachain::primitives::Id as ParaId; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; use sp_runtime::traits::AccountIdConversion; +use xcm_builder::SiblingParachainConvertsVia; +use xcm_executor::traits::Convert; pub const ALICE: AccountId32 = AccountId32::new([42u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; -pub const KARURA_ID: u32 = 2000; pub const STATEMINE_ID: u32 = 1000; +pub const KARURA_ID: u32 = 2000; +pub const MOONRIVER_ID: u32 = 3000; decl_test_parachain! { pub struct KaruraLike { @@ -46,6 +50,15 @@ decl_test_relay_chain! { } } +decl_test_parachain! { + pub struct MoonriverLike { + Runtime = moonriver_like::Runtime, + XcmpMessageHandler = moonriver_like::MsgQueue, + DmpMessageHandler = moonriver_like::MsgQueue, + new_ext = moonriver_like_ext(MOONRIVER_ID), + } +} + decl_test_parachain! { pub struct StatemineLike { Runtime = statemine_like::Runtime, @@ -61,6 +74,7 @@ decl_test_network! { parachains = vec![ (STATEMINE_ID, StatemineLike), (KARURA_ID, KaruraLike), + (MOONRIVER_ID, MoonriverLike), ], } } @@ -91,12 +105,14 @@ pub fn kusama_like_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); let karura_acc: AccountId = ParaId::from(KARURA_ID).into_account(); + let moonriver_acc: AccountId = ParaId::from(MOONRIVER_ID).into_account(); let statemine_acc: AccountId = ParaId::from(STATEMINE_ID).into_account(); let balances = vec![ (ALICE, INITIAL_BALANCE), (statemine_acc, INITIAL_BALANCE), (karura_acc, INITIAL_BALANCE), + (moonriver_acc, INITIAL_BALANCE), ]; pallet_balances::GenesisConfig:: { @@ -110,6 +126,27 @@ pub fn kusama_like_ext() -> sp_io::TestExternalities { ext } +pub fn moonriver_like_ext(para_id: u32) -> sp_io::TestExternalities { + use moonriver_like::{MsgQueue, Runtime, System}; + + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + let balances = vec![ + (ALICE, INITIAL_BALANCE) + ]; + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + MsgQueue::set_para_id(para_id.into()); + }); + ext +} + pub fn statemine_like_ext(para_id: u32) -> sp_io::TestExternalities { use statemine_like::{MsgQueue, Runtime, System}; @@ -161,6 +198,11 @@ mod tests { } } + fn statemine_acc_for(para_id: u32) -> statemine_like::AccountId { + SiblingParachainConvertsVia::::convert( + X2(Parent, Parachain(para_id))).unwrap() + } + /// Scenario: /// A parachain wants to move KSM from Kusama to Statemine. /// It withdraws funds and then teleports them to the destination. @@ -199,12 +241,86 @@ mod tests { }); StatemineLike::execute_with(|| { - use polkadot_parachain::primitives::Sibling; - use xcm_builder::SiblingParachainConvertsVia; - use xcm_executor::traits::Convert; - let karura_acc = SiblingParachainConvertsVia::::convert( - X2(Parent, Parachain(KARURA_ID))).unwrap(); - assert_eq!(statemine_like::Balances::free_balance(karura_acc), amount); + assert_eq!(statemine_like::Balances::free_balance(statemine_acc_for(KARURA_ID)), amount); + }); + } + + /// Scenario: + /// A chain wants to move funds to another parachain in an asset where Statemine acts as the reserve. + #[test] + fn reserve_based_transfer_works() { + use statemine_like::Assets as StatemineAssets; + use statemine_like::Origin as StatemineOrigin; + use moonriver_like::Assets as MoonriverAssets; + use moonriver_like::Origin as MoonriverOrigin; + use frame_support::traits::Currency; + + let ed = statemine_like::ExistentialDeposit::get(); + let amount = 10 * ed; + let ksm_amount = 1 * kusama_like::UNITS; + + let asset_id = 1; + // setup asset ownwership: + // Karura owns both native and asset tokens in its sovereign account on Statemine. + StatemineLike::execute_with(|| { + assert_ok!(StatemineAssets::force_create( + StatemineOrigin::root(), asset_id, ALICE, true /* is_sufficient */, ed)); + + assert_ok!(StatemineAssets::mint(StatemineOrigin::signed(ALICE), asset_id, statemine_acc_for(KARURA_ID), 2 * amount)); + + assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(KARURA_ID)), 2 * amount); + assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(MOONRIVER_ID)), 0); + + statemine_like::Balances::make_free_balance_be(&statemine_acc_for(KARURA_ID), ksm_amount); + }); + + MoonriverLike::execute_with(|| { + assert_ok!(MoonriverAssets::force_create( + MoonriverOrigin::root(), asset_id, ALICE, true /* is_sufficient */, ed)); + + assert_ok!(MoonriverAssets::mint(MoonriverOrigin::signed(ALICE), asset_id, ALICE, 2 * amount)); + }); + + KaruraLike::execute_with(|| { + let weight = 3 * statemine_like::UnitWeightCost::get(); + let message = Xcm::WithdrawAsset { + assets: vec![ConcreteFungible { id: Parent.into(), amount: ksm_amount }], + effects: vec![ + Order::BuyExecution { + fees: All, + weight, + debt: weight, + halt_on_error: false, + xcm: vec![Xcm::TransferReserveAsset { + assets: vec![ConcreteFungible { id: GeneralIndex{ id: asset_id.into() }.into(), amount }.into()], + dest: X2(Parent, Parachain(MOONRIVER_ID)), + effects: vec![Order::DepositAsset { + assets: vec![All], + dest: Null, + }], + }], + }, + ] + }; + assert_ok!(KaruraPalletXcm::send_xcm( + Null, + X2(Parent, Parachain(STATEMINE_ID)), + message.clone(), + )); + }); + + StatemineLike::execute_with(|| { + assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(KARURA_ID)), amount); + assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(MOONRIVER_ID)), amount); + }); + + MoonriverLike::execute_with(|| { + let self_account: moonriver_like::AccountId = moonriver_like::NullIsDefault::convert( + MultiLocation::Null).unwrap(); + assert_eq!( + MoonriverAssets::balance(asset_id, self_account), + amount + ); }); } } diff --git a/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs b/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs new file mode 100644 index 000000000000..bc016a4dbe4b --- /dev/null +++ b/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs @@ -0,0 +1,452 @@ +// Copyright 2021 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 . + +// Copyright 2021 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 . + +//! Parachain runtime mock. + +use codec::{Decode, Encode}; +use frame_support::{ + construct_runtime, parameter_types, + traits::{All, AllowAll}, + weights::{constants::WEIGHT_PER_SECOND, Weight}, +}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{Hash, IdentityLookup}, + AccountId32, +}; +use sp_std::{convert::TryFrom, prelude::*}; + +use pallet_xcm::XcmPassthrough; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain::primitives::{ + DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, +}; +use xcm::{ + v0::{ + Error as XcmError, ExecuteXcm, + Junction::{Parachain, Parent}, + MultiAsset, + MultiLocation::{self, X1}, + NetworkId, Outcome, Xcm, + }, + VersionedXcm, +}; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, CurrencyAdapter as XcmCurrencyAdapter, + EnsureXcmOrigin, FixedRateOfConcreteFungible, FixedWeightBounds, IsConcrete, LocationInverter, + NativeAsset, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; +use xcm_executor::{Config, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +// Copied from Statemine +pub const EXISTENTIAL_DEPOSIT: Balance = 0; +pub const UNITS: Balance = 1_000_000_000_000; +pub const CENTS: Balance = UNITS / 30_000; +pub const MILLICENTS: Balance = CENTS / 1_000; + +pub const fn deposit(items: u32, bytes: u32) -> Balance { + // map to 1/10 of what the kusama relay chain charges (v9020) + (items as Balance * 2_000 * CENTS + (bytes as Balance) * 100 * MILLICENTS) / 10 +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +impl frame_system::Config for Runtime { + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = AllowAll; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type Balance = Balance; + type Event = Event; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const AssetDeposit: Balance = UNITS; // 1 UNIT deposit to create asset + pub const ApprovalDeposit: Balance = EXISTENTIAL_DEPOSIT; + pub const AssetsStringLimit: u32 = 50; + /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) + // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 + pub const MetadataDepositBase: Balance = deposit(1, 68); + pub const MetadataDepositPerByte: Balance = deposit(0, 1); +} + +use frame_system::EnsureRoot; +pub type AssetsForceOrigin = EnsureRoot; + +pub type AssetId = u32; + +impl pallet_assets::Config for Runtime { + type Event = Event; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4; + pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4; +} + +parameter_types! { + pub const KsmLocation: MultiLocation = MultiLocation::X1(Parent); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); + pub const StatemineLocation: MultiLocation = MultiLocation::X2(Parent, Parachain(super::STATEMINE_ID)); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); +} + +use sp_std::borrow::Borrow; +use xcm_executor::traits::Convert; +/// An empty `MultiLocation::Null` is mapped to the default for `AccountId` (all zeroes). +pub struct NullIsDefault(PhantomData); +impl< + AccountId: Default + Eq + Clone, +> Convert for NullIsDefault { + fn convert_ref(location: impl Borrow) -> Result { + if let &MultiLocation::Null = location.borrow() { + Ok(AccountId::default()) + } else { + Err(()) + } + } + + fn reverse_ref(who: impl Borrow) -> Result { + if who.borrow() == &AccountId::default() { + Ok(MultiLocation::Null) + } else { + Err(()) + } + } +} + +pub type LocationToAccountId = ( + NullIsDefault, + SiblingParachainConvertsVia, + AccountId32Aliases, +); + +pub type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation, + SignedAccountId32AsNative, + XcmPassthrough, +); + +parameter_types! { + pub const UnitWeightCost: Weight = 1; + pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1); +} + +use frame_support::traits::{Contains, fungibles}; +use sp_runtime::traits::Zero; +use sp_std::marker::PhantomData; +use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId, AsPrefixedGeneralIndex}; +use xcm_executor::traits::JustTry; + +pub struct CheckAsset(PhantomData); +impl Contains<>::AssetId> for CheckAsset +where + A: fungibles::Inspect +{ + fn contains(id: &>::AssetId) -> bool { + !A::total_issuance(*id).is_zero() + } +} + +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteAssetId, JustTry>, + ), + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only allow teleports of known assets. + CheckAsset, + CheckingAccount, +>; + +pub type LocalAssetTransactor = ( + XcmCurrencyAdapter, LocationToAccountId, AccountId, CheckingAccount>, + FungiblesTransactor, +); + +pub type XcmRouter = super::ParachainXcmRouter; +pub type Barrier = AllowUnpaidExecutionFrom>; + +use xcm_executor::traits::FilterAssetLocation; +pub struct StatemineAsset; +impl FilterAssetLocation for StatemineAsset { + fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool { + let statemine = MultiLocation::X2(Parent, Parachain(super::STATEMINE_ID)); + origin == &statemine + && matches!(asset, MultiAsset::ConcreteFungible { ref id, .. } + if id.match_and_split(&statemine).is_some() + ) + } +} + +pub struct XcmConfig; +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToCallOrigin; + type IsReserve = (NativeAsset, StatemineAsset); + type IsTeleporter = (); + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfConcreteFungible; + type ResponseHandler = (); +} + +#[frame_support::pallet] +pub mod mock_msg_queue { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + type XcmExecutor: ExecuteXcm; + } + + #[pallet::call] + impl Pallet {} + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::storage] + #[pallet::getter(fn parachain_id)] + pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + + impl Get for Pallet { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + // XCMP + /// Some XCM was executed ok. + Success(Option), + /// Some XCM failed. + Fail(Option, XcmError), + /// Bad XCM version used. + BadVersion(Option), + /// Bad XCM format used. + BadFormat(Option), + + // DMP + /// Downward message is invalid XCM. + InvalidFormat(MessageId), + /// Downward message is unsupported version of XCM. + UnsupportedVersion(MessageId), + /// Downward message executed with the given outcome. + ExecutedDownward(MessageId, Outcome), + } + + impl Pallet { + pub fn set_para_id(para_id: ParaId) { + ParachainId::::put(para_id); + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm, + max_weight: Weight, + ) -> Result { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let (result, event) = match Xcm::::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::execute_xcm(location.into(), xcm, max_weight) { + Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), + Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), + // As far as the caller is concerned, this was dispatched without error, so + // we just report the weight used. + Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl XcmpMessageHandler for Pallet { + fn handle_xcmp_messages<'a, I: Iterator>( + iter: I, + max_weight: Weight, + ) -> Weight { + for (sender, sent_at, data) in iter { + let mut data_ref = data; + let _ = XcmpMessageFormat::decode(&mut data_ref) + .expect("Simulator encodes with versioned xcm format; qed"); + + let mut remaining_fragments = &data_ref[..]; + while !remaining_fragments.is_empty() { + if let Ok(xcm) = VersionedXcm::::decode(&mut remaining_fragments) { + let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight); + } else { + debug_assert!(false, "Invalid incoming XCMP message data"); + } + } + } + max_weight + } + } + + impl DmpMessageHandler for Pallet { + fn handle_dmp_messages( + iter: impl Iterator)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let id = sp_io::hashing::blake2_256(&data[..]); + let maybe_msg = + VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from); + match maybe_msg { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(Err(())) => { + Self::deposit_event(Event::UnsupportedVersion(id)); + }, + Ok(Ok(x)) => { + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + } + } + limit + } + } +} + +impl mock_msg_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = All<(MultiLocation, Xcm)>; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = (); + type XcmReserveTransferFilter = All<(MultiLocation, Vec)>; + type Weigher = FixedWeightBounds; +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Storage, Config, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, + PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, + } +); diff --git a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs index e0b17db98cfa..ca9dfc45fa5d 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs @@ -56,6 +56,17 @@ use xcm_executor::{Config, XcmExecutor}; pub type AccountId = AccountId32; pub type Balance = u128; +// copied from Statemine +pub const EXISTENTIAL_DEPOSIT: Balance = CENTS / 10; +pub const UNITS: Balance = 1_000_000_000_000; +pub const CENTS: Balance = UNITS / 30_000; +pub const MILLICENTS: Balance = CENTS / 1_000; + +pub const fn deposit(items: u32, bytes: u32) -> Balance { + // map to 1/10 of what the kusama relay chain charges (v9020) + (items as Balance * 2_000 * CENTS + (bytes as Balance) * 100 * MILLICENTS) / 10 +} + parameter_types! { pub const BlockHashCount: u64 = 250; } @@ -87,7 +98,7 @@ impl frame_system::Config for Runtime { } parameter_types! { - pub ExistentialDeposit: Balance = 1; + pub ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; pub const MaxLocks: u32 = 50; pub const MaxReserves: u32 = 50; } @@ -104,6 +115,37 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; } +parameter_types! { + pub const AssetDeposit: Balance = UNITS; // 1 UNIT deposit to create asset + pub const ApprovalDeposit: Balance = EXISTENTIAL_DEPOSIT; + pub const AssetsStringLimit: u32 = 50; + /// Key = 32 bytes, Value = 36 bytes (32+1+1+1+1) + // https://github.com/paritytech/substrate/blob/069917b/frame/assets/src/lib.rs#L257L271 + pub const MetadataDepositBase: Balance = deposit(1, 68); + pub const MetadataDepositPerByte: Balance = deposit(0, 1); +} + +use frame_system::EnsureRoot; +pub type AssetsForceOrigin = EnsureRoot; + +pub type AssetId = u32; + +impl pallet_assets::Config for Runtime { + type Event = Event; + type Balance = Balance; + type AssetId = AssetId; + type Currency = Balances; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); +} + parameter_types! { pub const ReservedXcmpWeight: Weight = WEIGHT_PER_SECOND / 4; pub const ReservedDmpWeight: Weight = WEIGHT_PER_SECOND / 4; @@ -113,6 +155,8 @@ parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::X1(Parent); pub const RelayNetwork: NetworkId = NetworkId::Kusama; pub Ancestry: MultiLocation = Parachain(MsgQueue::parachain_id().into()).into(); + pub const Local: MultiLocation = MultiLocation::Null; + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } pub type LocationToAccountId = ( @@ -132,8 +176,42 @@ parameter_types! { pub KsmPerSecond: (MultiLocation, u128) = (X1(Parent), 1); } -pub type LocalAssetTransactor = - XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>; +use frame_support::traits::{Contains, fungibles}; +use sp_runtime::traits::Zero; +use sp_std::marker::PhantomData; +use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId, AsPrefixedGeneralIndex}; +use xcm_executor::traits::JustTry; + +pub struct CheckAsset(PhantomData); +impl Contains<>::AssetId> for CheckAsset +where + A: fungibles::Inspect +{ + fn contains(id: &>::AssetId) -> bool { + !A::total_issuance(*id).is_zero() + } +} + +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + Assets, + // Use this currency when it is a fungible asset matching the given location or name: + ( + ConvertedConcreteAssetId, JustTry>, + ), + // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID: + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly): + AccountId, + // We only allow teleports of known assets. + CheckAsset, + CheckingAccount, +>; + +pub type LocalAssetTransactor = ( + XcmCurrencyAdapter, LocationToAccountId, AccountId, CheckingAccount>, + FungiblesTransactor, +); pub type XcmRouter = super::ParachainXcmRouter; pub type Barrier = AllowUnpaidExecutionFrom>; @@ -315,6 +393,7 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Storage, Config, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, MsgQueue: mock_msg_queue::{Pallet, Storage, Event}, PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin}, } From fa4bac12d05d12a25eae7130b407fe9de8185d08 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 3 Aug 2021 10:52:26 +0200 Subject: [PATCH 23/32] address review comments --- xcm/xcm-simulator/src/lib.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 2956904f94ef..0525b9ab1a7d 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -174,7 +174,11 @@ macro_rules! __impl_ext { fn execute_with(execute: impl FnOnce() -> R) -> R { let result = $ext_name.with(|v| v.borrow_mut().execute_with(execute)); - process_messages().expect("message processing failure"); + while exists_messages_in_any_bus() { + if let Err(xcm_error) = process_messages() { + panic!("Message processing failure: {:?}", xcm_error); + } + } result } } @@ -200,13 +204,26 @@ macro_rules! decl_test_network { impl $name { pub fn reset() { - use $crate::TestExt; - + use $crate::{TestExt, VecDeque}; + // Reset relay chain message bus + $crate::RELAY_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); + // Reset parachain message bus + $crate::PARA_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); + // Reset relay chain state <$relay_chain>::reset_ext(); + // Reset parachain state $( <$parachain>::reset_ext(); )* } } + /// Check if any messages exist in either message bus + fn exists_messages_in_any_bus() -> bool { + use $crate::{RELAY_MESSAGE_BUS, PARA_MESSAGE_BUS}; + let no_relay_messages_left = RELAY_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + let no_parachain_messages_left = PARA_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + !(no_relay_messages_left && no_parachain_messages_left) + } + /// Process any XCMs in the message buses. /// /// Called automatically by `TestExt::execute_with`. From 7f19aef10e9699bd9e593bfd81b3b62aa9787741 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Tue, 3 Aug 2021 11:24:31 +0200 Subject: [PATCH 24/32] fix typo Co-authored-by: Amar Singh --- xcm/xcm-simulator/statemine-like-example/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs index 8e8463f92156..a861819f10af 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/lib.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -260,7 +260,7 @@ mod tests { let ksm_amount = 1 * kusama_like::UNITS; let asset_id = 1; - // setup asset ownwership: + // setup asset ownership: // Karura owns both native and asset tokens in its sovereign account on Statemine. StatemineLike::execute_with(|| { assert_ok!(StatemineAssets::force_create( From 7b34f7df6b6ae971f50b4a13d6af1ed68fdaf4c7 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Tue, 3 Aug 2021 11:26:39 +0200 Subject: [PATCH 25/32] move imports --- xcm/xcm-simulator/statemine-like-example/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs index 8e8463f92156..d7ce264556e3 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/lib.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -21,10 +21,8 @@ mod statemine_like; use sp_runtime::AccountId32; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::traits::AccountIdConversion; -use xcm_builder::SiblingParachainConvertsVia; -use xcm_executor::traits::Convert; pub const ALICE: AccountId32 = AccountId32::new([42u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; @@ -185,6 +183,9 @@ mod tests { }; use xcm::opaque::v0::prelude::*; use xcm_simulator::TestExt; + use polkadot_parachain::primitives::Sibling; + use xcm_builder::SiblingParachainConvertsVia; + use xcm_executor::traits::Convert; // Construct a `BuyExecution` order. fn buy_execution(debt: Weight) -> Order { From 719f151a84c12465c3e903147e9f05cbaace4ebc Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Tue, 3 Aug 2021 11:56:15 +0200 Subject: [PATCH 26/32] formatting --- xcm/xcm-simulator/statemine-like-example/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs index d7ce264556e3..0901ccb3539b 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/lib.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -267,7 +267,8 @@ mod tests { assert_ok!(StatemineAssets::force_create( StatemineOrigin::root(), asset_id, ALICE, true /* is_sufficient */, ed)); - assert_ok!(StatemineAssets::mint(StatemineOrigin::signed(ALICE), asset_id, statemine_acc_for(KARURA_ID), 2 * amount)); + assert_ok!(StatemineAssets::mint( + StatemineOrigin::signed(ALICE), asset_id, statemine_acc_for(KARURA_ID), 2 * amount)); assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(KARURA_ID)), 2 * amount); assert_eq!(StatemineAssets::balance(asset_id, statemine_acc_for(MOONRIVER_ID)), 0); From 2903c07edd1d7527221ab1fbf668e8307474a17c Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Tue, 3 Aug 2021 12:07:53 +0200 Subject: [PATCH 27/32] remove statemine checking account --- xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs index ca9dfc45fa5d..904e534c2f55 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs @@ -209,7 +209,7 @@ pub type FungiblesTransactor = FungiblesAdapter< >; pub type LocalAssetTransactor = ( - XcmCurrencyAdapter, LocationToAccountId, AccountId, CheckingAccount>, + XcmCurrencyAdapter, LocationToAccountId, AccountId, ()>, FungiblesTransactor, ); From e659ec7bc09d530fe8b816a746281f0a71456af1 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 10 Aug 2021 16:41:08 -0400 Subject: [PATCH 28/32] make xcm simulator async --- xcm/xcm-simulator/example/src/lib.rs | 134 +++++++++++++++++++-- xcm/xcm-simulator/example/src/parachain.rs | 9 +- xcm/xcm-simulator/src/lib.rs | 134 ++++++++++++++++++--- 3 files changed, 249 insertions(+), 28 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index 547c6f1858be..ba07c144e4c9 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -17,9 +17,12 @@ mod parachain; mod relay_chain; +use polkadot_parachain::primitives::Id as ParaId; +use sp_runtime::traits::AccountIdConversion; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain}; pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); +pub const INITIAL_BALANCE: u128 = 1_000_000_000; decl_test_parachain! { pub struct ParaA { @@ -57,7 +60,9 @@ decl_test_network! { } } -pub const INITIAL_BALANCE: u128 = 1_000_000_000; +pub fn para_account_id(id: u32) -> relay_chain::AccountId { + ParaId::from(id).into_account() +} pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { use parachain::{MsgQueue, Runtime, System}; @@ -81,7 +86,9 @@ pub fn relay_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(ALICE, INITIAL_BALANCE)] } + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_id(1), INITIAL_BALANCE)] + } .assimilate_storage(&mut t) .unwrap(); @@ -98,10 +105,22 @@ mod tests { use super::*; use codec::Encode; - use frame_support::assert_ok; + use frame_support::{assert_ok, weights::Weight}; use xcm::latest::prelude::*; use xcm_simulator::TestExt; + // Helper function for forming buy execution message + fn buy_execution(fees: impl Into, debt: Weight) -> Order { + Order::BuyExecution { + fees: fees.into(), + weight: 0, + debt, + halt_on_error: false, + orders: vec![], + instructions: vec![], + } + } + #[test] fn dmp() { MockNet::reset(); @@ -187,23 +206,124 @@ mod tests { fn reserve_transfer() { MockNet::reset(); + let withdraw_amount = 123; + let max_weight_for_execution = 3; + Relay::execute_with(|| { assert_ok!(RelayChainPalletXcm::reserve_transfer_assets( relay_chain::Origin::signed(ALICE), Box::new(X1(Parachain(1)).into()), Box::new(X1(AccountId32 { network: Any, id: ALICE.into() }).into()), - (Here, 123).into(), + (Here, withdraw_amount).into(), 0, - 3, + max_weight_for_execution, )); + assert_eq!( + parachain::Balances::free_balance(¶_account_id(1)), + INITIAL_BALANCE + withdraw_amount + ); }); ParaA::execute_with(|| { // free execution, full amount received assert_eq!( pallet_balances::Pallet::::free_balance(&ALICE), - INITIAL_BALANCE + 123 + INITIAL_BALANCE + withdraw_amount ); }); } -} + + /// 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; + let weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + + ParaA::execute_with(|| { + let message = WithdrawAsset { + assets: (Here, send_amount).into(), + effects: vec![ + buy_execution((Here, send_amount), weight_for_execution), + Order::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(2).into(), + }, + ], + }; + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Here.into(), Parent.into(), message.clone())); + }); + + Relay::execute_with(|| { + assert_eq!( + relay_chain::Balances::free_balance(para_account_id(1)), + INITIAL_BALANCE - send_amount + ); + assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), 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 weight_for_execution = 3 * relay_chain::BaseXcmWeight::get(); + let query_id_set = 1234; + + // Send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let message = WithdrawAsset { + assets: (Here, send_amount).into(), + effects: vec![ + buy_execution((Here, send_amount), weight_for_execution), + Order::DepositAsset { + assets: All.into(), + max_assets: 1, + beneficiary: Parachain(2).into(), + }, + Order::QueryHolding { + query_id: query_id_set, + dest: Parachain(1).into(), + assets: All.into(), + }, + ], + }; + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Here.into(), Parent.into(), message.clone(),)); + }); + + // Check that transfer was executed + Relay::execute_with(|| { + // Withdraw executed + assert_eq!( + relay_chain::Balances::free_balance(para_account_id(1)), + INITIAL_BALANCE - send_amount + ); + // Deposit executed + assert_eq!(relay_chain::Balances::free_balance(para_account_id(2)), send_amount); + }); + + // Check that QueryResponse message was received + ParaA::execute_with(|| { + assert_eq!( + parachain::MsgQueue::received_dmp(), + vec![( + Parent.into(), + QueryResponse { query_id: query_id_set, response: Response::Assets(MultiAssets::new()) } + )] + ); + }); + } +} diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 79c2f6e2947d..31e262e6ea10 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -166,6 +166,12 @@ pub mod mock_msg_queue { #[pallet::getter(fn parachain_id)] pub(super) type ParachainId = StorageValue<_, ParaId, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn received_dmp)] + /// A queue of received DMP messages + pub(super) type ReceivedDmp = + StorageValue<_, Vec<(MultiLocation, Xcm)>, ValueQuery>; + impl Get for Pallet { fn get() -> ParaId { Self::parachain_id() @@ -266,7 +272,8 @@ pub mod mock_msg_queue { Self::deposit_event(Event::UnsupportedVersion(id)); }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x, limit); + let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); + >::mutate(|y| y.push((Parent.into(), x))); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, } diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index aed9635cff7f..25cd15cc9e49 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -21,7 +21,7 @@ pub use paste; pub use frame_support::{traits::Get, weights::Weight}; pub use sp_io::TestExternalities; -pub use sp_std::{cell::RefCell, marker::PhantomData}; +pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData}; pub use polkadot_core_primitives::BlockNumber as RelayBlockNumber; pub use polkadot_parachain::primitives::{ @@ -36,9 +36,24 @@ pub use xcm::{latest::prelude::*, VersionedXcm}; pub use xcm_executor::XcmExecutor; pub trait TestExt { + /// Initialize the test environment. fn new_ext() -> sp_io::TestExternalities; + /// Resets the state of the test environment. fn reset_ext(); - fn execute_with(execute: impl FnOnce() -> R) -> R; + /// Execute code in the context of the test externalities, without automatic + /// message processing. All messages in the message buses can be processed + /// by calling `Self::dispatch_xcm_buses()`. + fn execute_without_dispatch(execute: impl FnOnce() -> R) -> R; + /// Process all messages in the message buses + fn dispatch_xcm_buses(); + /// Execute some code in the context of the test externalities, with + /// automatic message processing. + /// Messages are dispatched once the passed closure completes. + fn execute_with(execute: impl FnOnce() -> R) -> R { + let result = Self::execute_without_dispatch(execute); + Self::dispatch_xcm_buses(); + result + } } pub enum MessageKind { @@ -162,13 +177,31 @@ macro_rules! __impl_ext { $ext_name.with(|v| *v.borrow_mut() = $new_ext); } - fn execute_with(execute: impl FnOnce() -> R) -> R { + fn execute_without_dispatch(execute: impl FnOnce() -> R) -> R { $ext_name.with(|v| v.borrow_mut().execute_with(execute)) } + + fn dispatch_xcm_buses() { + while exists_messages_in_any_bus() { + if let Err(xcm_error) = process_relay_messages() { + panic!("Relay chain XCM execution failure: {:?}", xcm_error); + } + if let Err(xcm_error) = process_para_messages() { + panic!("Parachain XCM execution failure: {:?}", xcm_error); + } + } + } } }; } +thread_local! { + pub static PARA_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); + pub static RELAY_MESSAGE_BUS: RefCell)>> + = RefCell::new(VecDeque::new()); +} + #[macro_export] macro_rules! decl_test_network { ( @@ -181,37 +214,101 @@ macro_rules! decl_test_network { impl $name { pub fn reset() { - use $crate::TestExt; - + use $crate::{TestExt, VecDeque}; + // Reset relay chain message bus + $crate::RELAY_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); + // Reset parachain message bus + $crate::PARA_MESSAGE_BUS.with(|b| b.replace(VecDeque::new())); <$relay_chain>::reset_ext(); $( <$parachain>::reset_ext(); )* } } - /// XCM router for parachain. - pub struct ParachainXcmRouter($crate::PhantomData); + /// Check if any messages exist in either message bus + fn exists_messages_in_any_bus() -> bool { + use $crate::{RELAY_MESSAGE_BUS, PARA_MESSAGE_BUS}; + let no_relay_messages_left = RELAY_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + let no_parachain_messages_left = PARA_MESSAGE_BUS.with(|b| b.borrow().is_empty()); + !(no_relay_messages_left && no_parachain_messages_left) + } - impl> $crate::SendXcm for ParachainXcmRouter { - fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { - use $crate::{UmpSink, XcmpMessageHandlerT}; + /// Process all messages originating from parachains. + fn process_para_messages() -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; + while let Some((para_id, destination, message)) = $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { match destination.interior() { $crate::Junctions::Here if destination.parent_count() == 1 => { let encoded = $crate::encode_xcm(message, $crate::MessageKind::Ump); - let _ = <$relay_chain>::process_upward_message( - T::get(), &encoded[..], + let r = <$relay_chain>::process_upward_message( + para_id, &encoded[..], $crate::Weight::max_value(), ); - Ok(()) + if let Err((id, required)) = r { + return Err($crate::XcmError::WeightLimitReached(required)); + } }, $( $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 1 => { let encoded = $crate::encode_xcm(message, $crate::MessageKind::Xcmp); - let messages = vec![(T::get(), 1, &encoded[..])]; - let _ = <$parachain>::handle_xcmp_messages( + let messages = vec![(para_id, 1, &encoded[..])]; + let _weight = <$parachain>::handle_xcmp_messages( messages.into_iter(), $crate::Weight::max_value(), ); + }, + )* + _ => { + return Err($crate::XcmError::CannotReachDestination(destination, message)); + } + } + } + + Ok(()) + } + + /// Process all messages originating from the relay chain. + fn process_relay_messages() -> $crate::XcmResult { + use $crate::DmpMessageHandlerT; + + while let Some((destination, message)) = $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().pop_front()) { + match destination.interior() { + $( + $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 0 => { + let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); + // NOTE: RelayChainBlockNumber is hard-coded to 1 + let messages = vec![(1, encoded)]; + let _weight = <$parachain>::handle_dmp_messages( + messages.into_iter(), $crate::Weight::max_value(), + ); + }, + )* + _ => return Err($crate::XcmError::SendFailed("Only sends to children parachain.")), + } + } + + Ok(()) + } + + /// XCM router for parachain. + pub struct ParachainXcmRouter($crate::PhantomData); + + impl> $crate::SendXcm for ParachainXcmRouter { + fn send_xcm(destination: $crate::MultiLocation, message: $crate::Xcm<()>) -> $crate::XcmResult { + use $crate::{UmpSink, XcmpMessageHandlerT}; + + match destination.interior() { + $crate::Junctions::Here if destination.parent_count() == 1 => { + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); + Ok(()) + }, + $( + $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 1 => { + $crate::PARA_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((T::get(), destination, message))); Ok(()) }, )* @@ -229,11 +326,8 @@ macro_rules! decl_test_network { match destination.interior() { $( $crate::X1($crate::Parachain(id)) if *id == $para_id && destination.parent_count() == 0 => { - let encoded = $crate::encode_xcm(message, $crate::MessageKind::Dmp); - let messages = vec![(1, encoded)]; - let _ = <$parachain>::handle_dmp_messages( - messages.into_iter(), $crate::Weight::max_value(), - ); + $crate::RELAY_MESSAGE_BUS.with( + |b| b.borrow_mut().push_back((destination, message))); Ok(()) }, )* From e6229221670427370c389639e230d62675fb1e67 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 10 Aug 2021 16:49:44 -0400 Subject: [PATCH 29/32] fmt --- xcm/xcm-simulator/example/src/lib.rs | 15 +++++++++------ xcm/xcm-simulator/example/src/parachain.rs | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/xcm/xcm-simulator/example/src/lib.rs b/xcm/xcm-simulator/example/src/lib.rs index ba07c144e4c9..683905bcec41 100644 --- a/xcm/xcm-simulator/example/src/lib.rs +++ b/xcm/xcm-simulator/example/src/lib.rs @@ -87,10 +87,10 @@ pub fn relay_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, INITIAL_BALANCE), (para_account_id(1), INITIAL_BALANCE)] - } - .assimilate_storage(&mut t) - .unwrap(); + balances: vec![(ALICE, INITIAL_BALANCE), (para_account_id(1), INITIAL_BALANCE)], + } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); @@ -321,9 +321,12 @@ mod tests { parachain::MsgQueue::received_dmp(), vec![( Parent.into(), - QueryResponse { query_id: query_id_set, response: Response::Assets(MultiAssets::new()) } + QueryResponse { + query_id: query_id_set, + response: Response::Assets(MultiAssets::new()) + } )] ); }); } -} +} diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 31e262e6ea10..11d363643078 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -224,7 +224,7 @@ pub mod mock_msg_queue { // we just report the weight used. Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), } - }, + } Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), }; Self::deposit_event(event); @@ -267,15 +267,15 @@ pub mod mock_msg_queue { match maybe_msg { Err(_) => { Self::deposit_event(Event::InvalidFormat(id)); - }, + } Ok(Err(())) => { Self::deposit_event(Event::UnsupportedVersion(id)); - }, + } Ok(Ok(x)) => { let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); >::mutate(|y| y.push((Parent.into(), x))); Self::deposit_event(Event::ExecutedDownward(id, outcome)); - }, + } } } limit From 03c4c7c93fbfdf7085bfa9a6faefd6f599880425 Mon Sep 17 00:00:00 2001 From: 4meta5 Date: Tue, 10 Aug 2021 16:55:25 -0400 Subject: [PATCH 30/32] nightly fmt --- xcm/xcm-simulator/example/src/parachain.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index 11d363643078..31e262e6ea10 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -224,7 +224,7 @@ pub mod mock_msg_queue { // we just report the weight used. Outcome::Incomplete(w, e) => (Ok(w), Event::Fail(Some(hash), e)), } - } + }, Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), }; Self::deposit_event(event); @@ -267,15 +267,15 @@ pub mod mock_msg_queue { match maybe_msg { Err(_) => { Self::deposit_event(Event::InvalidFormat(id)); - } + }, Ok(Err(())) => { Self::deposit_event(Event::UnsupportedVersion(id)); - } + }, Ok(Ok(x)) => { let outcome = T::XcmExecutor::execute_xcm(Parent.into(), x.clone(), limit); >::mutate(|y| y.push((Parent.into(), x))); Self::deposit_event(Event::ExecutedDownward(id, outcome)); - } + }, } } limit From a2f524bf0a86cc0896d62e368b943d9274411b2b Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Mon, 16 Aug 2021 19:08:41 +0200 Subject: [PATCH 31/32] add StatemineLike as reserve to KaruraLike --- xcm/xcm-simulator/statemine-like-example/src/karura_like.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs index 69882fc2c2b5..ac3c78d9e375 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs @@ -153,7 +153,7 @@ impl Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = NativeAsset; + type IsReserve = (NativeAsset, StatemineAsset); type IsTeleporter = (); type LocationInverter = LocationInverter; type Barrier = Barrier; From eedf757b4c4330c3b596c71aac1fe93893785e34 Mon Sep 17 00:00:00 2001 From: Alexander Popiak Date: Fri, 10 Sep 2021 12:15:15 +0200 Subject: [PATCH 32/32] update xcm simulator statemine example to XCM v2 --- .../statemine-like-example/src/karura_like.rs | 14 ++++-- .../statemine-like-example/src/kusama_like.rs | 16 +++++-- .../statemine-like-example/src/lib.rs | 44 +++++++++---------- .../src/moonriver_like.rs | 14 ++++-- .../src/statemine_like.rs | 14 ++++-- 5 files changed, 67 insertions(+), 35 deletions(-) diff --git a/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs index ddef1e67a587..9b039f59eb84 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/karura_like.rs @@ -139,6 +139,7 @@ pub type XcmOriginToCallOrigin = ( parameter_types! { pub const UnitWeightCost: Weight = 1; pub KsmPerSecond: (XcmAssetId, u128) = (Concrete(Parent.into()), 1); + pub const MaxInstructions: u32 = 100; } pub type LocalAssetTransactor = @@ -169,9 +170,12 @@ impl Config for XcmConfig { type IsTeleporter = (); type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; - type ResponseHandler = (); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; } #[frame_support::pallet] @@ -322,8 +326,12 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs b/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs index 1e40ab00574d..a6b065f1d755 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/kusama_like.rs @@ -117,6 +117,7 @@ type LocalOriginConverter = ( parameter_types! { pub const BaseXcmWeight: Weight = 1_000_000_000; pub KsmPerSecond: (AssetId, u128) = (Concrete(KsmLocation::get()), 1); + pub const MaxInstructions: u32 = 100; } parameter_types! { @@ -145,9 +146,12 @@ impl xcm_executor::Config for XcmConfig { type IsTeleporter = TrustedTeleporters; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; - type ResponseHandler = (); + type ResponseHandler = XcmPallet; + type AssetTrap = XcmPallet; + type AssetClaims = XcmPallet; + type SubscriptionService = XcmPallet; } pub type LocalOriginToLocation = SignedToAccountId32; @@ -163,8 +167,12 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } parameter_types! { @@ -192,6 +200,6 @@ construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, ParasOrigin: origin::{Pallet, Origin}, ParasUmp: ump::{Pallet, Call, Storage, Event}, - XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event}, + XcmPallet: pallet_xcm::{Pallet, Call, Storage, Event, Origin}, } ); diff --git a/xcm/xcm-simulator/statemine-like-example/src/lib.rs b/xcm/xcm-simulator/statemine-like-example/src/lib.rs index ec8cb60e5299..b70688871c63 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/lib.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/lib.rs @@ -175,7 +175,7 @@ mod tests { use super::*; use frame_support::{assert_ok, weights::Weight}; - use xcm::latest::prelude::*; + use xcm::v1::prelude::*; use xcm_simulator::TestExt; use polkadot_parachain::primitives::Sibling; use xcm_builder::SiblingParachainConvertsVia; @@ -224,10 +224,12 @@ mod tests { }, ], }; + use std::convert::TryFrom; + let xcm = xcm::v2::Xcm::try_from(message.clone()).expect("xcm conversion should not fail"); assert_ok!(KaruraPalletXcm::send_xcm( Here, MultiLocation::parent(), - message.clone(), + xcm, )); }); @@ -280,30 +282,28 @@ mod tests { KaruraLike::execute_with(|| { let weight = 3 * statemine_like::UnitWeightCost::get(); - let message = Xcm::WithdrawAsset { - assets: vec![(MultiLocation::parent(), ksm_amount).into()].into(), - effects: vec![ - Order::BuyExecution { + use xcm::v2::Xcm as XcmV2; + use xcm::v2::Instruction; + let message = XcmV2(vec![ + Instruction::WithdrawAsset(vec![(MultiLocation::parent(), ksm_amount).into()].into()), + Instruction::BuyExecution { fees: (MultiLocation::parent(), ksm_amount).into(), - weight, - debt: weight, - halt_on_error: false, - instructions: vec![Xcm::TransferReserveAsset { - assets: vec![(GeneralIndex(asset_id.into()), amount).into()].into(), - dest: MultiLocation::new(1, X1(Parachain(MOONRIVER_ID))), - effects: vec![Order::DepositAsset { - assets: All.into(), - max_assets: 2, - beneficiary: Here.into(), - }], - }], - }, - ] - }; + weight_limit: Some(weight).into() + }, + Instruction::TransferReserveAsset { + assets: vec![(GeneralIndex(asset_id.into()), amount).into()].into(), + dest: MultiLocation::new(1, X1(Parachain(MOONRIVER_ID))), + xcm: XcmV2(vec![Instruction::DepositAsset { + assets: All.into(), + max_assets: 2, + beneficiary: Here.into(), + }]), + } + ]); assert_ok!(KaruraPalletXcm::send_xcm( Here, MultiLocation::new(1, X1(Parachain(STATEMINE_ID))), - message.clone(), + message, )); }); diff --git a/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs b/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs index decd1b6e83a8..6c10d4826a55 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/moonriver_like.rs @@ -207,6 +207,7 @@ pub type XcmOriginToCallOrigin = ( parameter_types! { pub const UnitWeightCost: Weight = 1; pub KsmPerSecond: (XcmAssetId, u128) = (Concrete(Parent.into()), 1); + pub const MaxInstructions: u32 = 100; } use frame_support::traits::{Contains, fungibles}; @@ -271,9 +272,12 @@ impl Config for XcmConfig { type IsTeleporter = (); type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; - type ResponseHandler = (); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; } #[frame_support::pallet] @@ -424,8 +428,12 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs index 5745aea45f93..6dff75ac62bd 100644 --- a/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs +++ b/xcm/xcm-simulator/statemine-like-example/src/statemine_like.rs @@ -167,6 +167,7 @@ pub type XcmOriginToCallOrigin = ( parameter_types! { pub const UnitWeightCost: Weight = 1; pub KsmPerSecond: (XcmAssetId, u128) = (Concrete(Parent.into()), 1); + pub const MaxInstructions: u32 = 100; } use frame_support::traits::{Contains, fungibles}; @@ -219,9 +220,12 @@ impl Config for XcmConfig { type IsTeleporter = NativeAsset; type LocationInverter = LocationInverter; type Barrier = Barrier; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type Trader = FixedRateOfFungible; - type ResponseHandler = (); + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; } #[frame_support::pallet] @@ -372,8 +376,12 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; + type Weigher = FixedWeightBounds; type LocationInverter = LocationInverter; + type Origin = Origin; + type Call = Call; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;