From 4875ea11aeef4f3fc7d724940e5ffb703830619b Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi <shawntabrizi@gmail.com> Date: Mon, 29 Apr 2024 17:22:23 -0400 Subject: [PATCH] Refactor XCM Simulator Example (#4220) This PR does a "developer experience" refactor of the XCM Simulator Example. I was looking for existing code / documentation where developers could better learn about working with and configuring XCM. The XCM Simulator was a natural starting point due to the fact that it can emulate end to end XCM scenarios, without needing to spawn multiple real chains. However, the XCM Simulator Example was just 3 giant files with a ton of configurations, runtime, pallets, and tests mashed together. This PR breaks down the XCM Simulator Example in a way that I believe is more approachable by a new developer who is looking to navigate the various components of the end to end example, and modify it themselves. The basic structure is: - xcm simulator example - lib (tries to only use the xcm simulator macros) - tests - relay-chain - mod (basic runtime that developers should be familiar with) - xcm-config - mod (contains the `XcmConfig` type - various files for each custom configuration - parachain - mock_msg_queue (custom pallet for simulator example) - mod (basic runtime that developers should be familiar with) - xcm-config - mod (contains the `XcmConfig` type - various files for each custom configuration I would like to add more documentation to this too, but I think this is a first step to be accepted which will affect how documentation is added to the example --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- polkadot/xcm/xcm-simulator/example/src/lib.rs | 529 +----------------- .../xcm-simulator/example/src/parachain.rs | 470 ---------------- .../example/src/parachain/mock_msg_queue.rs | 185 ++++++ .../example/src/parachain/mod.rs | 182 ++++++ .../parachain/xcm_config/asset_transactor.rs | 39 ++ .../src/parachain/xcm_config/barrier.rs | 20 + .../src/parachain/xcm_config/constants.rs | 30 + .../xcm_config/location_converter.rs | 25 + .../example/src/parachain/xcm_config/mod.rs | 63 +++ .../parachain/xcm_config/origin_converter.rs | 29 + .../src/parachain/xcm_config/reserve.rs | 21 + .../src/parachain/xcm_config/teleporter.rs | 27 + .../src/parachain/xcm_config/weigher.rs | 27 + .../{relay_chain.rs => relay_chain/mod.rs} | 145 +---- .../xcm_config/asset_transactor.rs | 38 ++ .../src/relay_chain/xcm_config/barrier.rs | 20 + .../src/relay_chain/xcm_config/constants.rs | 31 + .../xcm_config/location_converter.rs | 25 + .../example/src/relay_chain/xcm_config/mod.rs | 62 ++ .../xcm_config/origin_converter.rs | 34 ++ .../src/relay_chain/xcm_config/weigher.rs | 27 + .../xcm/xcm-simulator/example/src/tests.rs | 513 +++++++++++++++++ prdoc/pr_4220.prdoc | 11 + 23 files changed, 1435 insertions(+), 1118 deletions(-) delete mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/asset_transactor.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/barrier.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/location_converter.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/origin_converter.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/reserve.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/teleporter.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/weigher.rs rename polkadot/xcm/xcm-simulator/example/src/{relay_chain.rs => relay_chain/mod.rs} (53%) create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/asset_transactor.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/barrier.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/constants.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/location_converter.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/origin_converter.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/weigher.rs create mode 100644 polkadot/xcm/xcm-simulator/example/src/tests.rs create mode 100644 prdoc/pr_4220.prdoc diff --git a/polkadot/xcm/xcm-simulator/example/src/lib.rs b/polkadot/xcm/xcm-simulator/example/src/lib.rs index 56e204bf571..6fb9a69770e 100644 --- a/polkadot/xcm/xcm-simulator/example/src/lib.rs +++ b/polkadot/xcm/xcm-simulator/example/src/lib.rs @@ -17,13 +17,16 @@ mod parachain; mod relay_chain; +#[cfg(test)] +mod tests; + use sp_runtime::BuildStorage; use sp_tracing; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; use xcm_simulator::{decl_test_network, decl_test_parachain, decl_test_relay_chain, TestExt}; -pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([0u8; 32]); +pub const ALICE: sp_runtime::AccountId32 = sp_runtime::AccountId32::new([1u8; 32]); pub const INITIAL_BALANCE: u128 = 1_000_000_000; decl_test_parachain! { @@ -68,27 +71,27 @@ decl_test_network! { pub fn parent_account_id() -> parachain::AccountId { let location = (Parent,); - parachain::LocationToAccountId::convert_location(&location.into()).unwrap() + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() } pub fn child_account_id(para: u32) -> relay_chain::AccountId { let location = (Parachain(para),); - relay_chain::LocationToAccountId::convert_location(&location.into()).unwrap() + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() } pub fn child_account_account_id(para: u32, who: sp_runtime::AccountId32) -> relay_chain::AccountId { let location = (Parachain(para), AccountId32 { network: None, id: who.into() }); - relay_chain::LocationToAccountId::convert_location(&location.into()).unwrap() + relay_chain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() } pub fn sibling_account_account_id(para: u32, who: sp_runtime::AccountId32) -> parachain::AccountId { let location = (Parent, Parachain(para), AccountId32 { network: None, id: who.into() }); - parachain::LocationToAccountId::convert_location(&location.into()).unwrap() + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() } pub fn parent_account_account_id(who: sp_runtime::AccountId32) -> parachain::AccountId { let location = (Parent, AccountId32 { network: None, id: who.into() }); - parachain::LocationToAccountId::convert_location(&location.into()).unwrap() + parachain::location_converter::LocationConverter::convert_location(&location.into()).unwrap() } pub fn para_ext(para_id: u32) -> sp_io::TestExternalities { @@ -137,517 +140,3 @@ pub fn relay_ext() -> sp_io::TestExternalities { pub type RelayChainPalletXcm = pallet_xcm::Pallet<relay_chain::Runtime>; pub type ParachainPalletXcm = pallet_xcm::Pallet<parachain::Runtime>; - -#[cfg(test)] -mod tests { - use super::*; - - use codec::Encode; - use frame_support::{assert_ok, weights::Weight}; - use xcm::latest::QueryResponseInfo; - use xcm_simulator::TestExt; - - // Helper function for forming buy execution message - fn buy_execution<C>(fees: impl Into<Asset>) -> Instruction<C> { - BuyExecution { fees: fees.into(), weight_limit: Unlimited } - } - - #[test] - fn remote_account_ids_work() { - child_account_account_id(1, ALICE); - sibling_account_account_id(1, ALICE); - parent_account_account_id(ALICE); - } - - #[test] - fn dmp() { - MockNet::reset(); - - let remark = parachain::RuntimeCall::System( - frame_system::Call::<parachain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, - ); - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::send_xcm( - Here, - Parachain(1), - Xcm(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), - call: remark.encode().into(), - }]), - )); - }); - - ParaA::execute_with(|| { - use parachain::{RuntimeEvent, System}; - assert!(System::events().iter().any(|r| matches!( - r.event, - RuntimeEvent::System(frame_system::Event::Remarked { .. }) - ))); - }); - } - - #[test] - fn ump() { - MockNet::reset(); - - let remark = relay_chain::RuntimeCall::System( - frame_system::Call::<relay_chain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, - ); - ParaA::execute_with(|| { - assert_ok!(ParachainPalletXcm::send_xcm( - Here, - Parent, - Xcm(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), - call: remark.encode().into(), - }]), - )); - }); - - Relay::execute_with(|| { - use relay_chain::{RuntimeEvent, System}; - assert!(System::events().iter().any(|r| matches!( - r.event, - RuntimeEvent::System(frame_system::Event::Remarked { .. }) - ))); - }); - } - - #[test] - fn xcmp() { - MockNet::reset(); - - let remark = parachain::RuntimeCall::System( - frame_system::Call::<parachain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, - ); - ParaA::execute_with(|| { - assert_ok!(ParachainPalletXcm::send_xcm( - Here, - (Parent, Parachain(2)), - Xcm(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), - call: remark.encode().into(), - }]), - )); - }); - - ParaB::execute_with(|| { - use parachain::{RuntimeEvent, System}; - assert!(System::events().iter().any(|r| matches!( - r.event, - RuntimeEvent::System(frame_system::Event::Remarked { .. }) - ))); - }); - } - - #[test] - fn reserve_transfer() { - MockNet::reset(); - - let withdraw_amount = 123; - - Relay::execute_with(|| { - assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets( - relay_chain::RuntimeOrigin::signed(ALICE), - Box::new(Parachain(1).into()), - Box::new(AccountId32 { network: None, id: ALICE.into() }.into()), - Box::new((Here, withdraw_amount).into()), - 0, - Unlimited, - )); - assert_eq!( - relay_chain::Balances::free_balance(&child_account_id(1)), - INITIAL_BALANCE + withdraw_amount - ); - }); - - ParaA::execute_with(|| { - // free execution, full amount received - assert_eq!( - pallet_balances::Pallet::<parachain::Runtime>::free_balance(&ALICE), - INITIAL_BALANCE + withdraw_amount - ); - }); - } - - #[test] - fn remote_locking_and_unlocking() { - MockNet::reset(); - - let locked_amount = 100; - - ParaB::execute_with(|| { - let message = Xcm(vec![LockAsset { - asset: (Here, locked_amount).into(), - unlocker: Parachain(1).into(), - }]); - assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); - }); - - Relay::execute_with(|| { - use pallet_balances::{BalanceLock, Reasons}; - assert_eq!( - relay_chain::Balances::locks(&child_account_id(2)), - vec![BalanceLock { - id: *b"py/xcmlk", - amount: locked_amount, - reasons: Reasons::All - }] - ); - }); - - ParaA::execute_with(|| { - assert_eq!( - parachain::MsgQueue::received_dmp(), - vec![Xcm(vec![NoteUnlockable { - owner: (Parent, Parachain(2)).into(), - asset: (Parent, locked_amount).into() - }])] - ); - }); - - ParaB::execute_with(|| { - // Request unlocking part of the funds on the relay chain - let message = Xcm(vec![RequestUnlock { - asset: (Parent, locked_amount - 50).into(), - locker: Parent.into(), - }]); - assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(1)), message)); - }); - - Relay::execute_with(|| { - use pallet_balances::{BalanceLock, Reasons}; - // Lock is reduced - assert_eq!( - relay_chain::Balances::locks(&child_account_id(2)), - vec![BalanceLock { - id: *b"py/xcmlk", - amount: locked_amount - 50, - reasons: Reasons::All - }] - ); - }); - } - - /// Scenario: - /// A parachain transfers an NFT resident on the relay chain to another parachain account. - /// - /// Asserts that the parachain accounts are updated as expected. - #[test] - fn withdraw_and_deposit_nft() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_eq!(relay_chain::Uniques::owner(1, 42), Some(child_account_id(1))); - }); - - ParaA::execute_with(|| { - let message = Xcm(vec![TransferAsset { - assets: (GeneralIndex(1), 42u32).into(), - beneficiary: Parachain(2).into(), - }]); - // Send withdraw and deposit - assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message)); - }); - - Relay::execute_with(|| { - assert_eq!(relay_chain::Uniques::owner(1, 42), Some(child_account_id(2))); - }); - } - - /// Scenario: - /// The relay-chain teleports an NFT to a parachain. - /// - /// Asserts that the parachain accounts are updated as expected. - #[test] - fn teleport_nft() { - MockNet::reset(); - - Relay::execute_with(|| { - // Mint the NFT (1, 69) and give it to our "parachain#1 alias". - assert_ok!(relay_chain::Uniques::mint( - relay_chain::RuntimeOrigin::signed(ALICE), - 1, - 69, - child_account_account_id(1, ALICE), - )); - // The parachain#1 alias of Alice is what must hold it on the Relay-chain for it to be - // withdrawable by Alice on the parachain. - assert_eq!( - relay_chain::Uniques::owner(1, 69), - Some(child_account_account_id(1, ALICE)) - ); - }); - ParaA::execute_with(|| { - assert_ok!(parachain::ForeignUniques::force_create( - parachain::RuntimeOrigin::root(), - (Parent, GeneralIndex(1)).into(), - ALICE, - false, - )); - assert_eq!( - parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()), - None, - ); - assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0); - - // IRL Alice would probably just execute this locally on the Relay-chain, but we can't - // easily do that here since we only send between chains. - let message = Xcm(vec![ - WithdrawAsset((GeneralIndex(1), 69u32).into()), - InitiateTeleport { - assets: AllCounted(1).into(), - dest: Parachain(1).into(), - xcm: Xcm(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), - }]), - }, - ]); - // Send teleport - let alice = AccountId32 { id: ALICE.into(), network: None }; - assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); - }); - ParaA::execute_with(|| { - assert_eq!( - parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()), - Some(ALICE), - ); - assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000); - }); - Relay::execute_with(|| { - assert_eq!(relay_chain::Uniques::owner(1, 69), None); - }); - } - - /// Scenario: - /// The relay-chain transfers an NFT into a parachain's sovereign account, who then mints a - /// trustless-backed-derived locally. - /// - /// Asserts that the parachain accounts are updated as expected. - #[test] - fn reserve_asset_transfer_nft() { - sp_tracing::init_for_tests(); - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(relay_chain::Uniques::force_create( - relay_chain::RuntimeOrigin::root(), - 2, - ALICE, - false - )); - assert_ok!(relay_chain::Uniques::mint( - relay_chain::RuntimeOrigin::signed(ALICE), - 2, - 69, - child_account_account_id(1, ALICE) - )); - assert_eq!( - relay_chain::Uniques::owner(2, 69), - Some(child_account_account_id(1, ALICE)) - ); - }); - ParaA::execute_with(|| { - assert_ok!(parachain::ForeignUniques::force_create( - parachain::RuntimeOrigin::root(), - (Parent, GeneralIndex(2)).into(), - ALICE, - false, - )); - assert_eq!( - parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()), - None, - ); - assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0); - - let message = Xcm(vec![ - WithdrawAsset((GeneralIndex(2), 69u32).into()), - DepositReserveAsset { - assets: AllCounted(1).into(), - dest: Parachain(1).into(), - xcm: Xcm(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), - }]), - }, - ]); - // Send transfer - let alice = AccountId32 { id: ALICE.into(), network: None }; - assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); - }); - ParaA::execute_with(|| { - log::debug!(target: "xcm-executor", "Hello"); - assert_eq!( - parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()), - Some(ALICE), - ); - assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000); - }); - - Relay::execute_with(|| { - assert_eq!(relay_chain::Uniques::owner(2, 69), Some(child_account_id(1))); - }); - } - - /// Scenario: - /// The relay-chain creates an asset class on a parachain and then Alice transfers her NFT into - /// that parachain's sovereign account, who then mints a trustless-backed-derivative locally. - /// - /// Asserts that the parachain accounts are updated as expected. - #[test] - fn reserve_asset_class_create_and_reserve_transfer() { - MockNet::reset(); - - Relay::execute_with(|| { - assert_ok!(relay_chain::Uniques::force_create( - relay_chain::RuntimeOrigin::root(), - 2, - ALICE, - false - )); - assert_ok!(relay_chain::Uniques::mint( - relay_chain::RuntimeOrigin::signed(ALICE), - 2, - 69, - child_account_account_id(1, ALICE) - )); - assert_eq!( - relay_chain::Uniques::owner(2, 69), - Some(child_account_account_id(1, ALICE)) - ); - - let message = Xcm(vec![Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(1_000_000_000, 1024 * 1024), - call: parachain::RuntimeCall::from( - pallet_uniques::Call::<parachain::Runtime>::create { - collection: (Parent, 2u64).into(), - admin: parent_account_id(), - }, - ) - .encode() - .into(), - }]); - // Send creation. - assert_ok!(RelayChainPalletXcm::send_xcm(Here, Parachain(1), message)); - }); - ParaA::execute_with(|| { - // Then transfer - let message = Xcm(vec![ - WithdrawAsset((GeneralIndex(2), 69u32).into()), - DepositReserveAsset { - assets: AllCounted(1).into(), - dest: Parachain(1).into(), - xcm: Xcm(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), - }]), - }, - ]); - let alice = AccountId32 { id: ALICE.into(), network: None }; - assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); - }); - ParaA::execute_with(|| { - assert_eq!(parachain::Balances::reserved_balance(&parent_account_id()), 1000); - assert_eq!( - parachain::ForeignUniques::collection_owner((Parent, 2u64).into()), - Some(parent_account_id()) - ); - }); - } - - /// Scenario: - /// A parachain transfers funds on the relay chain to another parachain account. - /// - /// Asserts that the parachain accounts are updated as expected. - #[test] - fn withdraw_and_deposit() { - MockNet::reset(); - - let send_amount = 10; - - ParaA::execute_with(|| { - let message = Xcm(vec![ - WithdrawAsset((Here, send_amount).into()), - buy_execution((Here, send_amount)), - DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, - ]); - // Send withdraw and deposit - assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); - }); - - Relay::execute_with(|| { - assert_eq!( - relay_chain::Balances::free_balance(child_account_id(1)), - INITIAL_BALANCE - send_amount - ); - assert_eq!( - relay_chain::Balances::free_balance(child_account_id(2)), - INITIAL_BALANCE + send_amount - ); - }); - } - - /// Scenario: - /// A parachain wants to be notified that a transfer worked correctly. - /// It sends a `QueryHolding` after the deposit to get notified on success. - /// - /// Asserts that the balances are updated correctly and the expected XCM is sent. - #[test] - fn query_holding() { - MockNet::reset(); - - let send_amount = 10; - let query_id_set = 1234; - - // Send a message which fully succeeds on the relay chain - ParaA::execute_with(|| { - let message = Xcm(vec![ - WithdrawAsset((Here, send_amount).into()), - buy_execution((Here, send_amount)), - DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, - ReportHolding { - response_info: QueryResponseInfo { - destination: Parachain(1).into(), - query_id: query_id_set, - max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), - }, - assets: All.into(), - }, - ]); - // Send withdraw and deposit with query holding - assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); - }); - - // Check that transfer was executed - Relay::execute_with(|| { - // Withdraw executed - assert_eq!( - relay_chain::Balances::free_balance(child_account_id(1)), - INITIAL_BALANCE - send_amount - ); - // Deposit executed - assert_eq!( - relay_chain::Balances::free_balance(child_account_id(2)), - INITIAL_BALANCE + send_amount - ); - }); - - // Check that QueryResponse message was received - ParaA::execute_with(|| { - assert_eq!( - parachain::MsgQueue::received_dmp(), - vec![Xcm(vec![QueryResponse { - query_id: query_id_set, - response: Response::Assets(Assets::new()), - max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), - querier: Some(Here.into()), - }])], - ); - }); - } -} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain.rs b/polkadot/xcm/xcm-simulator/example/src/parachain.rs deleted file mode 100644 index 41e62596392..00000000000 --- a/polkadot/xcm/xcm-simulator/example/src/parachain.rs +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (C) 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 <http://www.gnu.org/licenses/>. - -//! Parachain runtime mock. - -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, EverythingBut, Nothing}, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, -}; - -use frame_system::EnsureRoot; -use sp_core::{ConstU32, H256}; -use sp_runtime::{ - traits::{Get, Hash, IdentityLookup}, - AccountId32, -}; -use sp_std::prelude::*; - -use pallet_xcm::XcmPassthrough; -use polkadot_core_primitives::BlockNumber as RelayBlockNumber; -use polkadot_parachain_primitives::primitives::{ - DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler, -}; -use xcm::{latest::prelude::*, VersionedXcm}; -use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, - EnsureDecodableXcm, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, NoChecking, - NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, -}; -use xcm_executor::{ - traits::{ConvertLocation, JustTry}, - Config, XcmExecutor, -}; - -pub type SovereignAccountOf = ( - SiblingParachainConvertsVia<Sibling, AccountId>, - AccountId32Aliases<RelayNetwork, AccountId>, - ParentIsPreset<AccountId>, -); - -pub type AccountId = AccountId32; -pub type Balance = u128; - -parameter_types! { - pub const BlockHashCount: u64 = 250; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup<Self::AccountId>; - type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData<Balance>; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = Everything; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -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 RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; -} - -#[cfg(feature = "runtime-benchmarks")] -pub struct UniquesHelper; -#[cfg(feature = "runtime-benchmarks")] -impl pallet_uniques::BenchmarkHelper<Location, AssetInstance> for UniquesHelper { - fn collection(i: u16) -> Location { - GeneralIndex(i as u128).into() - } - fn item(i: u16) -> AssetInstance { - AssetInstance::Index(i as u128) - } -} - -impl pallet_uniques::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type CollectionId = Location; - type ItemId = AssetInstance; - type Currency = Balances; - type CreateOrigin = ForeignCreators; - type ForceOrigin = frame_system::EnsureRoot<AccountId>; - type CollectionDeposit = frame_support::traits::ConstU128<1_000>; - type ItemDeposit = frame_support::traits::ConstU128<1_000>; - type MetadataDepositBase = frame_support::traits::ConstU128<1_000>; - type AttributeDepositBase = frame_support::traits::ConstU128<1_000>; - type DepositPerByte = frame_support::traits::ConstU128<1>; - type StringLimit = ConstU32<64>; - type KeyLimit = ConstU32<64>; - type ValueLimit = ConstU32<128>; - type Locker = (); - type WeightInfo = (); - #[cfg(feature = "runtime-benchmarks")] - type Helper = UniquesHelper; -} - -// `EnsureOriginWithArg` impl for `CreateOrigin` which allows only XCM origins -// which are locations containing the class location. -pub struct ForeignCreators; -impl EnsureOriginWithArg<RuntimeOrigin, Location> for ForeignCreators { - type Success = AccountId; - - fn try_origin( - o: RuntimeOrigin, - a: &Location, - ) -> sp_std::result::Result<Self::Success, RuntimeOrigin> { - let origin_location = pallet_xcm::EnsureXcm::<Everything>::try_origin(o.clone())?; - if !a.starts_with(&origin_location) { - return Err(o) - } - SovereignAccountOf::convert_location(&origin_location).ok_or(o) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(a: &Location) -> Result<RuntimeOrigin, ()> { - Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) - } -} - -parameter_types! { - pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); - pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); -} - -parameter_types! { - pub const KsmLocation: Location = Location::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); -} - -pub type LocationToAccountId = ( - ParentIsPreset<AccountId>, - SiblingParachainConvertsVia<Sibling, AccountId>, - AccountId32Aliases<RelayNetwork, AccountId>, - Account32Hash<(), AccountId>, -); - -pub type XcmOriginToCallOrigin = ( - SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>, - SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>, - XcmPassthrough<RuntimeOrigin>, -); - -parameter_types! { - pub const UnitWeightCost: Weight = Weight::from_parts(1, 1); - pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; - pub ForeignPrefix: Location = (Parent,).into(); -} - -pub type LocalAssetTransactor = ( - FungibleAdapter<Balances, IsConcrete<KsmLocation>, LocationToAccountId, AccountId, ()>, - NonFungiblesAdapter< - ForeignUniques, - ConvertedConcreteId<Location, AssetInstance, JustTry, JustTry>, - SovereignAccountOf, - AccountId, - NoChecking, - (), - >, -); - -pub type XcmRouter = EnsureDecodableXcm<super::ParachainXcmRouter<MsgQueue>>; -pub type Barrier = AllowUnpaidExecutionFrom<Everything>; - -parameter_types! { - pub NftCollectionOne: AssetFilter - = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); - pub NftCollectionOneForRelay: (AssetFilter, Location) - = (NftCollectionOne::get(), (Parent,).into()); -} -pub type TrustedTeleporters = xcm_builder::Case<NftCollectionOneForRelay>; -pub type TrustedReserves = EverythingBut<xcm_builder::Case<NftCollectionOneForRelay>>; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = (NativeAsset, TrustedReserves); - type IsTeleporter = TrustedTeleporters; - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>; - type Trader = FixedRateOfFungible<KsmPerSecondPerByte, ()>; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = PolkadotXcm; - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = (); - type FeeManager = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} - -#[frame_support::pallet] -pub mod mock_msg_queue { - use super::*; - use frame_support::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; - type XcmExecutor: ExecuteXcm<Self::RuntimeCall>; - } - - #[pallet::call] - impl<T: Config> Pallet<T> {} - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet<T>(_); - - #[pallet::storage] - #[pallet::getter(fn parachain_id)] - pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>; - - #[pallet::storage] - #[pallet::getter(fn received_dmp)] - /// A queue of received DMP messages - pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>; - - impl<T: Config> Get<ParaId> for Pallet<T> { - fn get() -> ParaId { - Self::parachain_id() - } - } - - pub type MessageId = [u8; 32]; - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event<T: Config> { - // XCMP - /// Some XCM was executed OK. - Success(Option<T::Hash>), - /// Some XCM failed. - Fail(Option<T::Hash>, XcmError), - /// Bad XCM version used. - BadVersion(Option<T::Hash>), - /// Bad XCM format used. - BadFormat(Option<T::Hash>), - - // 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<T: Config> Pallet<T> { - pub fn set_para_id(para_id: ParaId) { - ParachainId::<T>::put(para_id); - } - - fn handle_xcmp_message( - sender: ParaId, - _sent_at: RelayBlockNumber, - xcm: VersionedXcm<T::RuntimeCall>, - max_weight: Weight, - ) -> Result<Weight, XcmError> { - let hash = Encode::using_encoded(&xcm, T::Hashing::hash); - let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); - let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) { - Ok(xcm) => { - let location = (Parent, Parachain(sender.into())); - match T::XcmExecutor::prepare_and_execute( - location, - xcm, - &mut message_hash, - max_weight, - Weight::zero(), - ) { - Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), - Outcome::Complete { used } => (Ok(used), 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 { used, error } => - (Ok(used), Event::Fail(Some(hash), error)), - } - }, - Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), - }; - Self::deposit_event(event); - result - } - } - - impl<T: Config> XcmpMessageHandler for Pallet<T> { - fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>( - 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::<T::RuntimeCall>::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<T: Config> DmpMessageHandler for Pallet<T> { - fn handle_dmp_messages( - iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>, - limit: Weight, - ) -> Weight { - for (_i, (_sent_at, data)) in iter.enumerate() { - let mut id = sp_io::hashing::blake2_256(&data[..]); - let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]); - match maybe_versioned { - Err(_) => { - Self::deposit_event(Event::InvalidFormat(id)); - }, - Ok(versioned) => match Xcm::try_from(versioned) { - Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), - Ok(x) => { - let outcome = T::XcmExecutor::prepare_and_execute( - Parent, - x.clone(), - &mut id, - limit, - Weight::zero(), - ); - <ReceivedDmp<T>>::append(x); - Self::deposit_event(Event::ExecutedDownward(id, outcome)); - }, - }, - } - } - limit - } - } -} - -impl mock_msg_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = XcmExecutor<XcmConfig>; -} - -pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>; - -pub struct TrustedLockerCase<T>(PhantomData<T>); -impl<T: Get<(Location, AssetFilter)>> ContainsPair<Location, Asset> for TrustedLockerCase<T> { - fn contains(origin: &Location, asset: &Asset) -> bool { - let (o, a) = T::get(); - a.matches(asset) && &o == origin - } -} - -parameter_types! { - pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); -} - -pub type TrustedLockers = TrustedLockerCase<RelayTokenForRelay>; - -impl pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; - type XcmExecuteFilter = Everything; - type XcmExecutor = XcmExecutor<XcmConfig>; - type XcmTeleportFilter = Nothing; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>; - type UniversalLocation = UniversalLocation; - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - type Currency = Balances; - type CurrencyMatcher = (); - type TrustedLockers = TrustedLockers; - type SovereignAccountOf = LocationToAccountId; - type MaxLockers = ConstU32<8>; - type MaxRemoteLockConsumers = ConstU32<0>; - type RemoteLockConsumerIdentifier = (); - type WeightInfo = pallet_xcm::TestWeightInfo; - type AdminOrigin = EnsureRoot<AccountId>; -} - -type Block = frame_system::mocking::MockBlock<Runtime>; - -construct_runtime!( - pub enum Runtime - { - System: frame_system, - Balances: pallet_balances, - MsgQueue: mock_msg_queue, - PolkadotXcm: pallet_xcm, - ForeignUniques: pallet_uniques, - } -); diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs new file mode 100644 index 00000000000..17cde921f3e --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/mock_msg_queue.rs @@ -0,0 +1,185 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +pub use pallet::*; +use polkadot_core_primitives::BlockNumber as RelayBlockNumber; +use polkadot_parachain_primitives::primitives::{ + DmpMessageHandler, Id as ParaId, XcmpMessageFormat, XcmpMessageHandler, +}; +use sp_runtime::traits::{Get, Hash}; +use xcm::{latest::prelude::*, VersionedXcm}; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; + type XcmExecutor: ExecuteXcm<Self::RuntimeCall>; + } + + #[pallet::call] + impl<T: Config> Pallet<T> {} + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet<T>(_); + + #[pallet::storage] + pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>; + + #[pallet::storage] + /// A queue of received DMP messages + pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>; + + impl<T: Config> Get<ParaId> for Pallet<T> { + fn get() -> ParaId { + Self::parachain_id() + } + } + + pub type MessageId = [u8; 32]; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event<T: Config> { + // XCMP + /// Some XCM was executed OK. + Success(Option<T::Hash>), + /// Some XCM failed. + Fail(Option<T::Hash>, XcmError), + /// Bad XCM version used. + BadVersion(Option<T::Hash>), + /// Bad XCM format used. + BadFormat(Option<T::Hash>), + + // 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<T: Config> Pallet<T> { + /// Get the Parachain Id. + pub fn parachain_id() -> ParaId { + ParachainId::<T>::get() + } + + /// Set the Parachain Id. + pub fn set_para_id(para_id: ParaId) { + ParachainId::<T>::put(para_id); + } + + /// Get the queue of receieved DMP messages. + pub fn received_dmp() -> Vec<Xcm<T::RuntimeCall>> { + ReceivedDmp::<T>::get() + } + + fn handle_xcmp_message( + sender: ParaId, + _sent_at: RelayBlockNumber, + xcm: VersionedXcm<T::RuntimeCall>, + max_weight: Weight, + ) -> Result<Weight, XcmError> { + let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); + let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) { + Ok(xcm) => { + let location = (Parent, Parachain(sender.into())); + match T::XcmExecutor::prepare_and_execute( + location, + xcm, + &mut message_hash, + max_weight, + Weight::zero(), + ) { + Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)), + Outcome::Complete { used } => (Ok(used), 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 { used, error } => + (Ok(used), Event::Fail(Some(hash), error)), + } + }, + Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))), + }; + Self::deposit_event(event); + result + } + } + + impl<T: Config> XcmpMessageHandler for Pallet<T> { + fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>( + 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::<T::RuntimeCall>::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<T: Config> DmpMessageHandler for Pallet<T> { + fn handle_dmp_messages( + iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>, + limit: Weight, + ) -> Weight { + for (_i, (_sent_at, data)) in iter.enumerate() { + let mut id = sp_io::hashing::blake2_256(&data[..]); + let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]); + match maybe_versioned { + Err(_) => { + Self::deposit_event(Event::InvalidFormat(id)); + }, + Ok(versioned) => match Xcm::try_from(versioned) { + Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), + Ok(x) => { + let outcome = T::XcmExecutor::prepare_and_execute( + Parent, + x.clone(), + &mut id, + limit, + Weight::zero(), + ); + <ReceivedDmp<T>>::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, + }, + } + } + limit + } + } +} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs new file mode 100644 index 00000000000..8021f955165 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/mod.rs @@ -0,0 +1,182 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +//! Parachain runtime mock. + +mod mock_msg_queue; +mod xcm_config; +pub use xcm_config::*; + +use core::marker::PhantomData; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ConstU128, ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, +}; +use frame_system::EnsureRoot; +use sp_core::ConstU32; +use sp_runtime::{ + traits::{Get, IdentityLookup}, + AccountId32, +}; +use sp_std::prelude::*; +use xcm::latest::prelude::*; +use xcm_builder::{EnsureXcmOrigin, SignedToAccountId32}; +use xcm_executor::{traits::ConvertLocation, XcmExecutor}; + +pub type AccountId = AccountId32; +pub type Balance = u128; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountId = AccountId; + type Lookup = IdentityLookup<Self::AccountId>; + type Block = Block; + type AccountData = pallet_balances::AccountData<Balance>; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct UniquesHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_uniques::BenchmarkHelper<Location, AssetInstance> for UniquesHelper { + fn collection(i: u16) -> Location { + GeneralIndex(i as u128).into() + } + fn item(i: u16) -> AssetInstance { + AssetInstance::Index(i as u128) + } +} + +impl pallet_uniques::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CollectionId = Location; + type ItemId = AssetInstance; + type Currency = Balances; + type CreateOrigin = ForeignCreators; + type ForceOrigin = frame_system::EnsureRoot<AccountId>; + type CollectionDeposit = frame_support::traits::ConstU128<1_000>; + type ItemDeposit = frame_support::traits::ConstU128<1_000>; + type MetadataDepositBase = frame_support::traits::ConstU128<1_000>; + type AttributeDepositBase = frame_support::traits::ConstU128<1_000>; + type DepositPerByte = frame_support::traits::ConstU128<1>; + type StringLimit = ConstU32<64>; + type KeyLimit = ConstU32<64>; + type ValueLimit = ConstU32<128>; + type Locker = (); + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type Helper = UniquesHelper; +} + +// `EnsureOriginWithArg` impl for `CreateOrigin` which allows only XCM origins +// which are locations containing the class location. +pub struct ForeignCreators; +impl EnsureOriginWithArg<RuntimeOrigin, Location> for ForeignCreators { + type Success = AccountId; + + fn try_origin( + o: RuntimeOrigin, + a: &Location, + ) -> sp_std::result::Result<Self::Success, RuntimeOrigin> { + let origin_location = pallet_xcm::EnsureXcm::<Everything>::try_origin(o.clone())?; + if !a.starts_with(&origin_location) { + return Err(o); + } + xcm_config::location_converter::LocationConverter::convert_location(&origin_location) + .ok_or(o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &Location) -> Result<RuntimeOrigin, ()> { + Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) + } +} + +parameter_types! { + pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); + pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0); +} + +impl mock_msg_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor<XcmConfig>; +} + +pub type LocalOriginToLocation = + SignedToAccountId32<RuntimeOrigin, AccountId, constants::RelayNetwork>; + +pub struct TrustedLockerCase<T>(PhantomData<T>); +impl<T: Get<(Location, AssetFilter)>> ContainsPair<Location, Asset> for TrustedLockerCase<T> { + fn contains(origin: &Location, asset: &Asset) -> bool { + let (o, a) = T::get(); + a.matches(asset) && &o == origin + } +} + +parameter_types! { + pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible })); +} + +pub type TrustedLockers = TrustedLockerCase<RelayTokenForRelay>; + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>; + type XcmExecuteFilter = Everything; + type XcmExecutor = XcmExecutor<XcmConfig>; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = weigher::Weigher; + type UniversalLocation = constants::UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type Currency = Balances; + type CurrencyMatcher = (); + type TrustedLockers = TrustedLockers; + type SovereignAccountOf = location_converter::LocationConverter; + type MaxLockers = ConstU32<8>; + type MaxRemoteLockConsumers = ConstU32<0>; + type RemoteLockConsumerIdentifier = (); + type WeightInfo = pallet_xcm::TestWeightInfo; + type AdminOrigin = EnsureRoot<AccountId>; +} + +type Block = frame_system::mocking::MockBlock<Runtime>; + +construct_runtime!( + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + MsgQueue: mock_msg_queue, + PolkadotXcm: pallet_xcm, + ForeignUniques: pallet_uniques, + } +); diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/asset_transactor.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/asset_transactor.rs new file mode 100644 index 00000000000..25cffcf8cef --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/asset_transactor.rs @@ -0,0 +1,39 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::{ + constants::KsmLocation, location_converter::LocationConverter, AccountId, Balances, + ForeignUniques, +}; +use xcm::latest::prelude::*; +use xcm_builder::{ + ConvertedConcreteId, FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter, +}; +use xcm_executor::traits::JustTry; + +type LocalAssetTransactor = ( + FungibleAdapter<Balances, IsConcrete<KsmLocation>, LocationConverter, AccountId, ()>, + NonFungiblesAdapter< + ForeignUniques, + ConvertedConcreteId<Location, AssetInstance, JustTry, JustTry>, + LocationConverter, + AccountId, + NoChecking, + (), + >, +); + +pub type AssetTransactor = LocalAssetTransactor; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/barrier.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/barrier.rs new file mode 100644 index 00000000000..1c7aa2c6d32 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/barrier.rs @@ -0,0 +1,20 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom<Everything>; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs new file mode 100644 index 00000000000..f6d0174def8 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/constants.rs @@ -0,0 +1,30 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::MsgQueue; +use frame_support::parameter_types; +use xcm::latest::prelude::*; + +parameter_types! { + pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const KsmLocation: Location = Location::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into(); +} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/location_converter.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/location_converter.rs new file mode 100644 index 00000000000..5a54414dd13 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/location_converter.rs @@ -0,0 +1,25 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::{constants::RelayNetwork, AccountId}; +use xcm_builder::{AccountId32Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +type LocationToAccountId = ( + HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>, + AccountId32Aliases<RelayNetwork, AccountId>, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs new file mode 100644 index 00000000000..0ba02aab9bf --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/mod.rs @@ -0,0 +1,63 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod reserve; +pub mod teleporter; +pub mod weigher; + +use crate::parachain::{MsgQueue, PolkadotXcm, RuntimeCall}; +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm<crate::ParachainXcmRouter<MsgQueue>>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = origin_converter::OriginConverter; + type IsReserve = reserve::TrustedReserves; + type IsTeleporter = teleporter::TrustedTeleporters; + type UniversalLocation = constants::UniversalLocation; + type Barrier = barrier::Barrier; + type Weigher = weigher::Weigher; + type Trader = FixedRateOfFungible<constants::KsmPerSecondPerByte, ()>; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = PolkadotXcm; + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); +} diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/origin_converter.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/origin_converter.rs new file mode 100644 index 00000000000..5a60f0e6001 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/origin_converter.rs @@ -0,0 +1,29 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; +use pallet_xcm::XcmPassthrough; +use xcm_builder::{SignedAccountId32AsNative, SovereignSignedViaLocation}; + +type XcmOriginToCallOrigin = ( + SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>, + SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>, + XcmPassthrough<RuntimeOrigin>, +); + +pub type OriginConverter = XcmOriginToCallOrigin; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/reserve.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/reserve.rs new file mode 100644 index 00000000000..8763a2f37cc --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/reserve.rs @@ -0,0 +1,21 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::teleporter::TrustedTeleporters; +use frame_support::traits::EverythingBut; +use xcm_builder::NativeAsset; + +pub type TrustedReserves = (NativeAsset, EverythingBut<TrustedTeleporters>); diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/teleporter.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/teleporter.rs new file mode 100644 index 00000000000..41cb7a5eb2d --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/teleporter.rs @@ -0,0 +1,27 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use frame_support::parameter_types; +use xcm::latest::prelude::*; + +parameter_types! { + pub NftCollectionOne: AssetFilter + = Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) }); + pub NftCollectionOneForRelay: (AssetFilter, Location) + = (NftCollectionOne::get(), (Parent,).into()); +} + +pub type TrustedTeleporters = xcm_builder::Case<NftCollectionOneForRelay>; diff --git a/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/weigher.rs b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/weigher.rs new file mode 100644 index 00000000000..4bdc98ea3b0 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/parachain/xcm_config/weigher.rs @@ -0,0 +1,27 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::parachain::RuntimeCall; +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +parameter_types! { + pub const UnitWeightCost: Weight = Weight::from_parts(1, 1); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs similarity index 53% rename from polkadot/xcm/xcm-simulator/example/src/relay_chain.rs rename to polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs index b41df3cfa2b..f698eba41d4 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/mod.rs @@ -16,31 +16,29 @@ //! Relay chain runtime mock. +mod xcm_config; +pub use xcm_config::*; + use frame_support::{ construct_runtime, derive_impl, parameter_types, - traits::{AsEnsureOriginWithArg, Everything, Nothing, ProcessMessage, ProcessMessageError}, + traits::{ + AsEnsureOriginWithArg, ConstU128, Everything, Nothing, ProcessMessage, ProcessMessageError, + }, weights::{Weight, WeightMeter}, }; use frame_system::EnsureRoot; -use sp_core::{ConstU32, H256}; +use sp_core::ConstU32; use sp_runtime::{traits::IdentityLookup, AccountId32}; -use polkadot_parachain_primitives::primitives::Id as ParaId; use polkadot_runtime_parachains::{ configuration, inclusion::{AggregateMessageOrigin, UmpQueueId}, origin, shared, }; use xcm::latest::prelude::*; -use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, - ConvertedConcreteId, EnsureDecodableXcm, FixedRateOfFungible, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NoChecking, NonFungiblesAdapter, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, -}; -use xcm_executor::{traits::JustTry, Config, XcmExecutor}; +use xcm_builder::{IsConcrete, SignedToAccountId32}; +use xcm_executor::XcmExecutor; pub type AccountId = AccountId32; pub type Balance = u128; @@ -51,51 +49,17 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type RuntimeCall = RuntimeCall; - type Nonce = u64; - type Hash = H256; - type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup<Self::AccountId>; type Block = Block; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; - type BlockWeights = (); - type BlockLength = (); - type Version = (); - type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData<Balance>; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = Everything; - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -parameter_types! { - pub ExistentialDeposit: Balance = 1; - pub const MaxLocks: u32 = 50; - pub const MaxReserves: u32 = 50; } +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; + type ExistentialDeposit = ConstU128<1>; type AccountStore = System; - type WeightInfo = (); - type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; - type RuntimeHoldReason = RuntimeHoldReason; - type RuntimeFreezeReason = RuntimeFreezeReason; - type FreezeIdentifier = (); - type MaxFreezes = ConstU32<0>; } impl pallet_uniques::Config for Runtime { @@ -127,83 +91,8 @@ impl configuration::Config for Runtime { type WeightInfo = configuration::TestWeightInfo; } -parameter_types! { - pub const TokenLocation: Location = Here.into_location(); - pub RelayNetwork: NetworkId = ByGenesis([0; 32]); - pub const AnyNetwork: Option<NetworkId> = None; - pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); - pub UnitWeightCost: u64 = 1_000; -} - -pub type LocationToAccountId = ( - ChildParachainConvertsVia<ParaId, AccountId>, - AccountId32Aliases<RelayNetwork, AccountId>, - Account32Hash<(), AccountId>, -); - -pub type LocalAssetTransactor = ( - FungibleAdapter<Balances, IsConcrete<TokenLocation>, LocationToAccountId, AccountId, ()>, - NonFungiblesAdapter< - Uniques, - ConvertedConcreteId<u32, u32, AsPrefixedGeneralIndex<(), u32, JustTry>, JustTry>, - LocationToAccountId, - AccountId, - NoChecking, - (), - >, -); - -type LocalOriginConverter = ( - SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>, - ChildParachainAsNative<origin::Origin, RuntimeOrigin>, - SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>, - ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>, -); - -parameter_types! { - pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); - pub TokensPerSecondPerByte: (AssetId, u128, u128) = - (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; -} - -pub type XcmRouter = EnsureDecodableXcm<super::RelayChainXcmRouter>; -pub type Barrier = AllowUnpaidExecutionFrom<Everything>; - -pub struct XcmConfig; -impl Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = LocalOriginConverter; - type IsReserve = (); - type IsTeleporter = (); - type UniversalLocation = UniversalLocation; - type Barrier = Barrier; - type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>; - type Trader = FixedRateOfFungible<TokensPerSecondPerByte, ()>; - type ResponseHandler = (); - type AssetTrap = (); - type AssetLocker = XcmPallet; - type AssetExchanger = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = (); - type FeeManager = (); - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); -} - -pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>; +pub type LocalOriginToLocation = + SignedToAccountId32<RuntimeOrigin, AccountId, constants::RelayNetwork>; impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; @@ -215,16 +104,16 @@ impl pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor<XcmConfig>; type XcmTeleportFilter = Everything; type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>; - type UniversalLocation = UniversalLocation; + type Weigher = weigher::Weigher; + type UniversalLocation = constants::UniversalLocation; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; type Currency = Balances; - type CurrencyMatcher = IsConcrete<TokenLocation>; + type CurrencyMatcher = IsConcrete<constants::TokenLocation>; type TrustedLockers = (); - type SovereignAccountOf = LocationToAccountId; + type SovereignAccountOf = location_converter::LocationConverter; type MaxLockers = ConstU32<8>; type MaxRemoteLockConsumers = ConstU32<0>; type RemoteLockConsumerIdentifier = (); diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/asset_transactor.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/asset_transactor.rs new file mode 100644 index 00000000000..c212569d481 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/asset_transactor.rs @@ -0,0 +1,38 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::relay_chain::{ + constants::TokenLocation, location_converter::LocationConverter, AccountId, Balances, Uniques, +}; +use xcm_builder::{ + AsPrefixedGeneralIndex, ConvertedConcreteId, FungibleAdapter, IsConcrete, NoChecking, + NonFungiblesAdapter, +}; +use xcm_executor::traits::JustTry; + +type LocalAssetTransactor = ( + FungibleAdapter<Balances, IsConcrete<TokenLocation>, LocationConverter, AccountId, ()>, + NonFungiblesAdapter< + Uniques, + ConvertedConcreteId<u32, u32, AsPrefixedGeneralIndex<(), u32, JustTry>, JustTry>, + LocationConverter, + AccountId, + NoChecking, + (), + >, +); + +pub type AssetTransactor = LocalAssetTransactor; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/barrier.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/barrier.rs new file mode 100644 index 00000000000..1c7aa2c6d32 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/barrier.rs @@ -0,0 +1,20 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use frame_support::traits::Everything; +use xcm_builder::AllowUnpaidExecutionFrom; + +pub type Barrier = AllowUnpaidExecutionFrom<Everything>; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/constants.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/constants.rs new file mode 100644 index 00000000000..f590c42990d --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/constants.rs @@ -0,0 +1,31 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use frame_support::parameter_types; +use xcm::latest::prelude::*; + +parameter_types! { + pub TokensPerSecondPerByte: (AssetId, u128, u128) = + (AssetId(TokenLocation::get()), 1_000_000_000_000, 1024 * 1024); + pub const MaxAssetsIntoHolding: u32 = 64; +} + +parameter_types! { + pub const TokenLocation: Location = Here.into_location(); + pub RelayNetwork: NetworkId = ByGenesis([0; 32]); + pub UniversalLocation: InteriorLocation = RelayNetwork::get().into(); + pub UnitWeightCost: u64 = 1_000; +} diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/location_converter.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/location_converter.rs new file mode 100644 index 00000000000..0f5f4e43dc9 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/location_converter.rs @@ -0,0 +1,25 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::relay_chain::{constants::RelayNetwork, AccountId}; +use xcm_builder::{AccountId32Aliases, DescribeAllTerminal, DescribeFamily, HashedDescription}; + +type LocationToAccountId = ( + HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>, + AccountId32Aliases<RelayNetwork, AccountId>, +); + +pub type LocationConverter = LocationToAccountId; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs new file mode 100644 index 00000000000..a7a8bae5156 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs @@ -0,0 +1,62 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +pub mod asset_transactor; +pub mod barrier; +pub mod constants; +pub mod location_converter; +pub mod origin_converter; +pub mod weigher; + +use crate::relay_chain::{RuntimeCall, XcmPallet}; +use frame_support::traits::{Everything, Nothing}; +use xcm_builder::{EnsureDecodableXcm, FixedRateOfFungible, FrameTransactionalProcessor}; +use xcm_executor::Config; + +// Generated from `decl_test_network!` +pub type XcmRouter = EnsureDecodableXcm<crate::RelayChainXcmRouter>; + +pub struct XcmConfig; +impl Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = asset_transactor::AssetTransactor; + type OriginConverter = origin_converter::OriginConverter; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = constants::UniversalLocation; + type Barrier = barrier::Barrier; + type Weigher = weigher::Weigher; + type Trader = FixedRateOfFungible<constants::TokensPerSecondPerByte, ()>; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = XcmPallet; + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type FeeManager = (); + type MaxAssetsIntoHolding = constants::MaxAssetsIntoHolding; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); +} diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/origin_converter.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/origin_converter.rs new file mode 100644 index 00000000000..3c79912a926 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/origin_converter.rs @@ -0,0 +1,34 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::relay_chain::{ + constants::RelayNetwork, location_converter::LocationConverter, RuntimeOrigin, +}; +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::origin; +use xcm_builder::{ + ChildParachainAsNative, ChildSystemParachainAsSuperuser, SignedAccountId32AsNative, + SovereignSignedViaLocation, +}; + +type LocalOriginConverter = ( + SovereignSignedViaLocation<LocationConverter, RuntimeOrigin>, + ChildParachainAsNative<origin::Origin, RuntimeOrigin>, + SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>, + ChildSystemParachainAsSuperuser<ParaId, RuntimeOrigin>, +); + +pub type OriginConverter = LocalOriginConverter; diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/weigher.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/weigher.rs new file mode 100644 index 00000000000..5c02565f460 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/weigher.rs @@ -0,0 +1,27 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::relay_chain::RuntimeCall; +use frame_support::parameter_types; +use xcm::latest::prelude::*; +use xcm_builder::FixedWeightBounds; + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); + pub const MaxInstructions: u32 = 100; +} + +pub type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>; diff --git a/polkadot/xcm/xcm-simulator/example/src/tests.rs b/polkadot/xcm/xcm-simulator/example/src/tests.rs new file mode 100644 index 00000000000..6486a849af3 --- /dev/null +++ b/polkadot/xcm/xcm-simulator/example/src/tests.rs @@ -0,0 +1,513 @@ +// Copyright (C) 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 <http://www.gnu.org/licenses/>. + +use crate::*; + +use codec::Encode; +use frame_support::{assert_ok, weights::Weight}; +use xcm::latest::QueryResponseInfo; +use xcm_simulator::TestExt; + +// Helper function for forming buy execution message +fn buy_execution<C>(fees: impl Into<Asset>) -> Instruction<C> { + BuyExecution { fees: fees.into(), weight_limit: Unlimited } +} + +#[test] +fn remote_account_ids_work() { + child_account_account_id(1, ALICE); + sibling_account_account_id(1, ALICE); + parent_account_account_id(ALICE); +} + +#[test] +fn dmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::<parachain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, + ); + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::send_xcm( + Here, + Parachain(1), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaA::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn ump() { + MockNet::reset(); + + let remark = relay_chain::RuntimeCall::System( + frame_system::Call::<relay_chain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + Parent, + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + Relay::execute_with(|| { + use relay_chain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn xcmp() { + MockNet::reset(); + + let remark = parachain::RuntimeCall::System( + frame_system::Call::<parachain::Runtime>::remark_with_event { remark: vec![1, 2, 3] }, + ); + ParaA::execute_with(|| { + assert_ok!(ParachainPalletXcm::send_xcm( + Here, + (Parent, Parachain(2)), + Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(INITIAL_BALANCE as u64, 1024 * 1024), + call: remark.encode().into(), + }]), + )); + }); + + ParaB::execute_with(|| { + use parachain::{RuntimeEvent, System}; + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::System(frame_system::Event::Remarked { .. }) + ))); + }); +} + +#[test] +fn reserve_transfer() { + MockNet::reset(); + + let withdraw_amount = 123; + + Relay::execute_with(|| { + assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets( + relay_chain::RuntimeOrigin::signed(ALICE), + Box::new(Parachain(1).into()), + Box::new(AccountId32 { network: None, id: ALICE.into() }.into()), + Box::new((Here, withdraw_amount).into()), + 0, + Unlimited, + )); + assert_eq!( + relay_chain::Balances::free_balance(&child_account_id(1)), + INITIAL_BALANCE + withdraw_amount + ); + }); + + ParaA::execute_with(|| { + // free execution, full amount received + assert_eq!( + pallet_balances::Pallet::<parachain::Runtime>::free_balance(&ALICE), + INITIAL_BALANCE + withdraw_amount + ); + }); +} + +#[test] +fn remote_locking_and_unlocking() { + MockNet::reset(); + + let locked_amount = 100; + + ParaB::execute_with(|| { + let message = Xcm(vec![LockAsset { + asset: (Here, locked_amount).into(), + unlocker: Parachain(1).into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + assert_eq!( + relay_chain::Balances::locks(&child_account_id(2)), + vec![BalanceLock { id: *b"py/xcmlk", amount: locked_amount, reasons: Reasons::All }] + ); + }); + + ParaA::execute_with(|| { + assert_eq!( + parachain::MsgQueue::received_dmp(), + vec![Xcm(vec![NoteUnlockable { + owner: (Parent, Parachain(2)).into(), + asset: (Parent, locked_amount).into() + }])] + ); + }); + + ParaB::execute_with(|| { + // Request unlocking part of the funds on the relay chain + let message = Xcm(vec![RequestUnlock { + asset: (Parent, locked_amount - 50).into(), + locker: Parent.into(), + }]); + assert_ok!(ParachainPalletXcm::send_xcm(Here, (Parent, Parachain(1)), message)); + }); + + Relay::execute_with(|| { + use pallet_balances::{BalanceLock, Reasons}; + // Lock is reduced + assert_eq!( + relay_chain::Balances::locks(&child_account_id(2)), + vec![BalanceLock { + id: *b"py/xcmlk", + amount: locked_amount - 50, + reasons: Reasons::All + }] + ); + }); +} + +/// Scenario: +/// A parachain transfers an NFT resident on the relay chain to another parachain account. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn withdraw_and_deposit_nft() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_eq!(relay_chain::Uniques::owner(1, 42), Some(child_account_id(1))); + }); + + ParaA::execute_with(|| { + let message = Xcm(vec![TransferAsset { + assets: (GeneralIndex(1), 42u32).into(), + beneficiary: Parachain(2).into(), + }]); + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message)); + }); + + Relay::execute_with(|| { + assert_eq!(relay_chain::Uniques::owner(1, 42), Some(child_account_id(2))); + }); +} + +/// Scenario: +/// The relay-chain teleports an NFT to a parachain. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn teleport_nft() { + MockNet::reset(); + + Relay::execute_with(|| { + // Mint the NFT (1, 69) and give it to our "parachain#1 alias". + assert_ok!(relay_chain::Uniques::mint( + relay_chain::RuntimeOrigin::signed(ALICE), + 1, + 69, + child_account_account_id(1, ALICE), + )); + // The parachain#1 alias of Alice is what must hold it on the Relay-chain for it to be + // withdrawable by Alice on the parachain. + assert_eq!(relay_chain::Uniques::owner(1, 69), Some(child_account_account_id(1, ALICE))); + }); + ParaA::execute_with(|| { + assert_ok!(parachain::ForeignUniques::force_create( + parachain::RuntimeOrigin::root(), + (Parent, GeneralIndex(1)).into(), + ALICE, + false, + )); + assert_eq!( + parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()), + None, + ); + assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0); + + // IRL Alice would probably just execute this locally on the Relay-chain, but we can't + // easily do that here since we only send between chains. + let message = Xcm(vec![ + WithdrawAsset((GeneralIndex(1), 69u32).into()), + InitiateTeleport { + assets: AllCounted(1).into(), + dest: Parachain(1).into(), + xcm: Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), + }]), + }, + ]); + // Send teleport + let alice = AccountId32 { id: ALICE.into(), network: None }; + assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); + }); + ParaA::execute_with(|| { + assert_eq!( + parachain::ForeignUniques::owner((Parent, GeneralIndex(1)).into(), 69u32.into()), + Some(ALICE), + ); + assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000); + }); + Relay::execute_with(|| { + assert_eq!(relay_chain::Uniques::owner(1, 69), None); + }); +} + +/// Scenario: +/// The relay-chain transfers an NFT into a parachain's sovereign account, who then mints a +/// trustless-backed-derived locally. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn reserve_asset_transfer_nft() { + sp_tracing::init_for_tests(); + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(relay_chain::Uniques::force_create( + relay_chain::RuntimeOrigin::root(), + 2, + ALICE, + false + )); + assert_ok!(relay_chain::Uniques::mint( + relay_chain::RuntimeOrigin::signed(ALICE), + 2, + 69, + child_account_account_id(1, ALICE) + )); + assert_eq!(relay_chain::Uniques::owner(2, 69), Some(child_account_account_id(1, ALICE))); + }); + ParaA::execute_with(|| { + assert_ok!(parachain::ForeignUniques::force_create( + parachain::RuntimeOrigin::root(), + (Parent, GeneralIndex(2)).into(), + ALICE, + false, + )); + assert_eq!( + parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()), + None, + ); + assert_eq!(parachain::Balances::reserved_balance(&ALICE), 0); + + let message = Xcm(vec![ + WithdrawAsset((GeneralIndex(2), 69u32).into()), + DepositReserveAsset { + assets: AllCounted(1).into(), + dest: Parachain(1).into(), + xcm: Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), + }]), + }, + ]); + // Send transfer + let alice = AccountId32 { id: ALICE.into(), network: None }; + assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); + }); + ParaA::execute_with(|| { + log::debug!(target: "xcm-executor", "Hello"); + assert_eq!( + parachain::ForeignUniques::owner((Parent, GeneralIndex(2)).into(), 69u32.into()), + Some(ALICE), + ); + assert_eq!(parachain::Balances::reserved_balance(&ALICE), 1000); + }); + + Relay::execute_with(|| { + assert_eq!(relay_chain::Uniques::owner(2, 69), Some(child_account_id(1))); + }); +} + +/// Scenario: +/// The relay-chain creates an asset class on a parachain and then Alice transfers her NFT into +/// that parachain's sovereign account, who then mints a trustless-backed-derivative locally. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn reserve_asset_class_create_and_reserve_transfer() { + MockNet::reset(); + + Relay::execute_with(|| { + assert_ok!(relay_chain::Uniques::force_create( + relay_chain::RuntimeOrigin::root(), + 2, + ALICE, + false + )); + assert_ok!(relay_chain::Uniques::mint( + relay_chain::RuntimeOrigin::signed(ALICE), + 2, + 69, + child_account_account_id(1, ALICE) + )); + assert_eq!(relay_chain::Uniques::owner(2, 69), Some(child_account_account_id(1, ALICE))); + + let message = Xcm(vec![Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(1_000_000_000, 1024 * 1024), + call: parachain::RuntimeCall::from( + pallet_uniques::Call::<parachain::Runtime>::create { + collection: (Parent, 2u64).into(), + admin: parent_account_id(), + }, + ) + .encode() + .into(), + }]); + // Send creation. + assert_ok!(RelayChainPalletXcm::send_xcm(Here, Parachain(1), message)); + }); + ParaA::execute_with(|| { + // Then transfer + let message = Xcm(vec![ + WithdrawAsset((GeneralIndex(2), 69u32).into()), + DepositReserveAsset { + assets: AllCounted(1).into(), + dest: Parachain(1).into(), + xcm: Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: (AccountId32 { id: ALICE.into(), network: None },).into(), + }]), + }, + ]); + let alice = AccountId32 { id: ALICE.into(), network: None }; + assert_ok!(ParachainPalletXcm::send_xcm(alice, Parent, message)); + }); + ParaA::execute_with(|| { + assert_eq!(parachain::Balances::reserved_balance(&parent_account_id()), 1000); + assert_eq!( + parachain::ForeignUniques::collection_owner((Parent, 2u64).into()), + Some(parent_account_id()) + ); + }); +} + +/// Scenario: +/// A parachain transfers funds on the relay chain to another parachain account. +/// +/// Asserts that the parachain accounts are updated as expected. +#[test] +fn withdraw_and_deposit() { + MockNet::reset(); + + let send_amount = 10; + + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ]); + // Send withdraw and deposit + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone())); + }); + + Relay::execute_with(|| { + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); +} + +/// Scenario: +/// A parachain wants to be notified that a transfer worked correctly. +/// It sends a `QueryHolding` after the deposit to get notified on success. +/// +/// Asserts that the balances are updated correctly and the expected XCM is sent. +#[test] +fn query_holding() { + MockNet::reset(); + + let send_amount = 10; + let query_id_set = 1234; + + // Send a message which fully succeeds on the relay chain + ParaA::execute_with(|| { + let message = Xcm(vec![ + WithdrawAsset((Here, send_amount).into()), + buy_execution((Here, send_amount)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: Parachain(2).into() }, + ReportHolding { + response_info: QueryResponseInfo { + destination: Parachain(1).into(), + query_id: query_id_set, + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + }, + assets: All.into(), + }, + ]); + // Send withdraw and deposit with query holding + assert_ok!(ParachainPalletXcm::send_xcm(Here, Parent, message.clone(),)); + }); + + // Check that transfer was executed + Relay::execute_with(|| { + // Withdraw executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(1)), + INITIAL_BALANCE - send_amount + ); + // Deposit executed + assert_eq!( + relay_chain::Balances::free_balance(child_account_id(2)), + INITIAL_BALANCE + send_amount + ); + }); + + // Check that QueryResponse message was received + ParaA::execute_with(|| { + assert_eq!( + parachain::MsgQueue::received_dmp(), + vec![Xcm(vec![QueryResponse { + query_id: query_id_set, + response: Response::Assets(Assets::new()), + max_weight: Weight::from_parts(1_000_000_000, 1024 * 1024), + querier: Some(Here.into()), + }])], + ); + }); +} diff --git a/prdoc/pr_4220.prdoc b/prdoc/pr_4220.prdoc new file mode 100644 index 00000000000..d5688ab325c --- /dev/null +++ b/prdoc/pr_4220.prdoc @@ -0,0 +1,11 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Refactor XCM Simulator Example + +doc: + - audience: Runtime Dev + description: | + This PR refactors the XCM Simulator Example to improve developer experience when trying to read and understand the example. 3 monolithic files have been broken down into their respective components across various modules. No major logical changes were made. + +crates: [ ] -- GitLab