From ccd187e463ce1fda95e5d5525e3ffb1afd27ad2d Mon Sep 17 00:00:00 2001 From: bogdanS98 Date: Fri, 3 Nov 2023 14:45:08 +0200 Subject: [PATCH] Fix sibling runtime, integration tests and update xcm configs for all runtimes with the correct multilocations --- runtime/integration-tests/Cargo.toml | 9 + .../integration-tests/src/amplitude_tests.rs | 29 +- runtime/integration-tests/src/lib.rs | 2 + runtime/integration-tests/src/mock.rs | 10 +- .../integration-tests/src/pendulum_tests.rs | 24 +- runtime/integration-tests/src/sibling.rs | 794 +++++++++--------- runtime/integration-tests/src/test_macros.rs | 103 ++- 7 files changed, 522 insertions(+), 449 deletions(-) diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index 28d03507e..92081583e 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -9,6 +9,11 @@ version = "0.1.0" codec = { package = "parity-scale-codec", version = "3.0.0" } scale-info = { version = "2.1.2", features = ["derive"] } serde = { version = "1.0.144", features = ["derive"] } +log = { version = "0.4.17", default-features = false } +smallvec = "1.9.0" + +# Spacewalk libraries +spacewalk-primitives = { git = "https://github.com/pendulum-chain/spacewalk", default-features = false, rev = "58983d2695c309665c9c017a022436aaee088f3d"} frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40" } @@ -39,6 +44,7 @@ cumulus-pallet-dmp-queue = {git = "https://github.com/paritytech/cumulus", branc cumulus-pallet-xcmp-queue = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} cumulus-pallet-xcm = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} cumulus-primitives-core = {git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40"} +cumulus-primitives-utility = {git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.40"} parachain-info = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40" } statemint-runtime = { git = "https://github.com/paritytech/cumulus", branch = "polkadot-v0.9.40" } @@ -50,6 +56,9 @@ orml-traits = {git = "https://github.com/open-web3-stack/open-runtime-module-lib orml-tokens = {git = "https://github.com/open-web3-stack/open-runtime-module-library.git", branch = "polkadot-v0.9.40" } orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch = "polkadot-v0.9.40" } +# Local +runtime-common = {path = "../common", default-features = false} + pendulum-runtime = { path = "../pendulum" } amplitude-runtime = {path = "../amplitude" } diff --git a/runtime/integration-tests/src/amplitude_tests.rs b/runtime/integration-tests/src/amplitude_tests.rs index 45248ae2e..8e165bd5d 100644 --- a/runtime/integration-tests/src/amplitude_tests.rs +++ b/runtime/integration-tests/src/amplitude_tests.rs @@ -1,13 +1,14 @@ use crate::{ mock::{kusama_relay_ext, para_ext, ParachainType, USDT_ASSET_ID}, + sibling, test_macros::{ parachain1_transfer_asset_to_parachain2, parachain1_transfer_asset_to_parachain2_and_back, parachain1_transfer_incorrect_asset_to_parachain2_should_fail, transfer_10_relay_token_from_parachain_to_relay_chain, transfer_20_relay_token_from_relay_chain_to_parachain, - transfer_native_token_from_parachain1_to_parachain2, + transfer_native_token_from_parachain1_to_parachain2_and_back, }, - AMPLITUDE_ID, KUSAMA_ASSETHUB_ID, + AMPLITUDE_ID, KUSAMA_ASSETHUB_ID, SIBLING_ID, }; use frame_support::assert_ok; @@ -35,6 +36,16 @@ decl_test_parachain! { } } +decl_test_parachain! { + pub struct SiblingParachain { + Runtime = sibling::Runtime, + RuntimeOrigin = sibling::RuntimeOrigin, + XcmpMessageHandler = sibling::XcmpQueue, + DmpMessageHandler = sibling::DmpQueue, + new_ext = para_ext(ParachainType::Sibling), + } +} + decl_test_parachain! { pub struct AssetHubParachain { Runtime = kusama_asset_hub_runtime::Runtime, @@ -51,6 +62,7 @@ decl_test_network! { parachains = vec![ (1000, AssetHubParachain), (2124, AmplitudeParachain), + (9999, SiblingParachain), ], } } @@ -117,3 +129,16 @@ fn assethub_transfer_asset_to_amplitude_and_back() { network_id ); } + +#[test] +fn transfer_native_token_to_sibling_parachain_and_back() { + transfer_native_token_from_parachain1_to_parachain2_and_back!( + KusamaMockNet, + amplitude_runtime, + AmplitudeParachain, + sibling, + SiblingParachain, + AMPLITUDE_ID, + SIBLING_ID + ); +} diff --git a/runtime/integration-tests/src/lib.rs b/runtime/integration-tests/src/lib.rs index e9f3ca5bd..e015a378b 100644 --- a/runtime/integration-tests/src/lib.rs +++ b/runtime/integration-tests/src/lib.rs @@ -18,3 +18,5 @@ pub const POLKADOT_ASSETHUB_ID: u32 = 1000; pub const AMPLITUDE_ID: u32 = 2124; pub const KUSAMA_ASSETHUB_ID: u32 = 1000; + +pub const SIBLING_ID: u32 = 9999; diff --git a/runtime/integration-tests/src/mock.rs b/runtime/integration-tests/src/mock.rs index 2860597ce..6ac7f863a 100644 --- a/runtime/integration-tests/src/mock.rs +++ b/runtime/integration-tests/src/mock.rs @@ -1,4 +1,6 @@ -use crate::{AMPLITUDE_ID, KUSAMA_ASSETHUB_ID, PENDULUM_ID, POLKADOT_ASSETHUB_ID}; +use crate::{ + sibling, AMPLITUDE_ID, KUSAMA_ASSETHUB_ID, PENDULUM_ID, POLKADOT_ASSETHUB_ID, SIBLING_ID, +}; use frame_support::traits::GenesisBuild; use pendulum_runtime::CurrencyId; use polkadot_core_primitives::{AccountId, Balance, BlockNumber}; @@ -74,6 +76,8 @@ macro_rules! build_parachain_with_orml { orml_tokens::GenesisConfig::<$runtime> { balances: vec![ (AccountId::from(BOB), CurrencyId::XCM(0), units($orml_balance)), + (AccountId::from(ALICE), CurrencyId::XCM(0), units($orml_balance)), + (AccountId::from(BOB), CurrencyId::Native, units($orml_balance)), (AccountId::from(ALICE), CurrencyId::Native, units($orml_balance)), ], } @@ -199,7 +203,7 @@ impl ExtBuilderParachain { ParachainType::PolkadotAssetHub => POLKADOT_ASSETHUB_ID, ParachainType::KusamaAssetHub => KUSAMA_ASSETHUB_ID, ParachainType::Pendulum => PENDULUM_ID, - ParachainType::Sibling => PENDULUM_ID + 1, + ParachainType::Sibling => SIBLING_ID, ParachainType::Amplitude => AMPLITUDE_ID, } } @@ -249,7 +253,7 @@ impl Builder for ExtBuilderParachain { ) }, ParachainType::Sibling => { - use pendulum_runtime::{Runtime, System}; + use sibling::{Runtime, System}; build_parachain_with_orml!( self, Runtime, diff --git a/runtime/integration-tests/src/pendulum_tests.rs b/runtime/integration-tests/src/pendulum_tests.rs index d6d54442c..c312bad4b 100644 --- a/runtime/integration-tests/src/pendulum_tests.rs +++ b/runtime/integration-tests/src/pendulum_tests.rs @@ -1,13 +1,14 @@ use crate::{ mock::{para_ext, polkadot_relay_ext, ParachainType, USDT_ASSET_ID}, + sibling, test_macros::{ parachain1_transfer_asset_to_parachain2, parachain1_transfer_asset_to_parachain2_and_back, parachain1_transfer_incorrect_asset_to_parachain2_should_fail, transfer_10_relay_token_from_parachain_to_relay_chain, transfer_20_relay_token_from_relay_chain_to_parachain, - transfer_native_token_from_parachain1_to_parachain2, + transfer_native_token_from_parachain1_to_parachain2_and_back, }, - PENDULUM_ID, POLKADOT_ASSETHUB_ID, + PENDULUM_ID, POLKADOT_ASSETHUB_ID, SIBLING_ID, }; use frame_support::assert_ok; @@ -37,10 +38,10 @@ decl_test_parachain! { decl_test_parachain! { pub struct SiblingParachain { - Runtime = pendulum_runtime::Runtime, - RuntimeOrigin = pendulum_runtime::RuntimeOrigin, - XcmpMessageHandler = pendulum_runtime::XcmpQueue, - DmpMessageHandler = pendulum_runtime::DmpQueue, + Runtime = sibling::Runtime, + RuntimeOrigin = sibling::RuntimeOrigin, + XcmpMessageHandler = sibling::XcmpQueue, + DmpMessageHandler = sibling::DmpQueue, new_ext = para_ext(ParachainType::Sibling), } } @@ -61,7 +62,7 @@ decl_test_network! { parachains = vec![ (1000, AssetHubParachain), (2094, PendulumParachain), - (2095, SiblingParachain), + (9999, SiblingParachain), ], } } @@ -130,15 +131,12 @@ fn assethub_transfer_asset_to_pendulum_and_back() { } #[test] -fn transfer_pendulum_native_token_to_pendulum() { - // ID for Pendulum's second instance - pub const SIBLING_ID: u32 = PENDULUM_ID + 1; - - transfer_native_token_from_parachain1_to_parachain2!( +fn transfer_native_token_to_sibling_parachain_and_back() { + transfer_native_token_from_parachain1_to_parachain2_and_back!( PolkadotMockNet, pendulum_runtime, PendulumParachain, - pendulum_runtime, + sibling, SiblingParachain, PENDULUM_ID, SIBLING_ID diff --git a/runtime/integration-tests/src/sibling.rs b/runtime/integration-tests/src/sibling.rs index 9b630a481..cf523f6d4 100644 --- a/runtime/integration-tests/src/sibling.rs +++ b/runtime/integration-tests/src/sibling.rs @@ -1,400 +1,372 @@ // Based on https://github.com/open-web3-stack/open-runtime-module-library/blob/83a76c2bf66c0b1236c31077e6fb24bb760a3535/xtokens/src/mock/para.rs -// This is temporary until successful compiling -#![allow(warnings)] #![cfg(test)] -use codec::{Decode, Encode, MaxEncodedLen}; -use cumulus_primitives_core::GetChannelInfo; +use core::marker::PhantomData; use frame_support::{ - match_types, parameter_types, - traits::{ConstU32, Everything, Get}, + log, match_types, parameter_types, + traits::{ConstU32, ContainsPair, Everything, Nothing}, }; -use orml_traits::parameter_type_with_key; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; +use frame_system::EnsureRoot; +use orml_traits::{ + location::{RelativeReserveProvider, Reserve}, + parameter_type_with_key, +}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use polkadot_runtime_common::MAXIMUM_BLOCK_WEIGHT; use sp_core::H256; -use sp_debug_derive::RuntimeDebug; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, Convert, IdentityLookup}, - AccountId32, BoundedVec, BuildStorage, + traits::{BlakeTwo256, Convert, IdentityLookup, Zero}, + AccountId32, +}; +use xcm::v3::prelude::*; +use xcm_emulator::{ + cumulus_pallet_parachain_system::{self, RelayNumberStrictlyIncreases}, + Weight, }; -use xcm::{v3::prelude::*, CreateMatcher, MatchXcm}; -use xcm_emulator::TestExt; use xcm_executor::{ - traits::{ShouldExecute, WeightTrader}, - Config, + traits::{JustTry, ShouldExecute, WeightTrader}, + Assets, XcmExecutor, }; -use pendulum_runtime::ParachainInfo; +use spacewalk_primitives::CurrencyId; +use xcm::latest::Weight as XCMWeight; +use xcm_builder::{ + AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, + FixedWeightBounds, FungiblesAdapter, NoChecking, ParentIsPreset, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, +}; pub type AccountId = AccountId32; -#[derive( - Encode, - Decode, - Eq, - PartialEq, - Copy, - Clone, - RuntimeDebug, - PartialOrd, - Ord, - MaxEncodedLen, - TypeInfo, -)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub enum CurrencyId { - /// Relay chain token. - R, - /// Parachain A token. - A, - /// Parachain A A1 token. - A1, - /// Parachain B token. - B, - /// Parachain B B1 token - B1, - /// Parachain B B2 token - B2, - /// Parachain C token - C, - /// Parachain D token - D, +parameter_types! { + pub const RelayLocation: MultiLocation = MultiLocation::parent(); + pub const RelayNetwork: NetworkId = NetworkId::Polkadot; + pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); + pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub UniversalLocation: InteriorMultiLocation = + X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); + } +/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// CurrencyIdConvert +/// This type implements conversions from our `CurrencyId` type into `MultiLocation` and vice-versa. +/// A currency locally is identified with a `CurrencyId` variant but in the network it is identified +/// in the form of a `MultiLocation`, in this case a pCfg (Para-Id, Currency-Id). pub struct CurrencyIdConvert; + +// Only supports native currency for now impl Convert> for CurrencyIdConvert { fn convert(id: CurrencyId) -> Option { match id { - CurrencyId::R => Some(Parent.into()), - CurrencyId::A => Some( - ( - Parent, - Parachain(1), - Junction::from(BoundedVec::try_from(b"A".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::A1 => Some( - ( - Parent, - Parachain(1), - Junction::from(BoundedVec::try_from(b"A1".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::B => Some( - ( - Parent, - Parachain(2), - Junction::from(BoundedVec::try_from(b"B".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::B1 => Some( - ( - Parent, - Parachain(2), - Junction::from(BoundedVec::try_from(b"B1".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::B2 => Some( - ( - Parent, - Parachain(2), - Junction::from(BoundedVec::try_from(b"B2".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::C => Some( - ( - Parent, - Parachain(3), - Junction::from(BoundedVec::try_from(b"C".to_vec()).unwrap()), - ) - .into(), - ), - CurrencyId::D => Some( - ( - Parent, - Parachain(4), - Junction::from(BoundedVec::try_from(b"D".to_vec()).unwrap()), - ) - .into(), - ), + CurrencyId::Native => Some(MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::parachain_id().into()), PalletInstance(10)), + )), + _ => None, } } } + impl Convert> for CurrencyIdConvert { - fn convert(l: MultiLocation) -> Option { - let mut a: Vec = "A".into(); - a.resize(32, 0); - let mut a1: Vec = "A1".into(); - a1.resize(32, 0); - let mut b: Vec = "B".into(); - b.resize(32, 0); - let mut b1: Vec = "B1".into(); - b1.resize(32, 0); - let mut b2: Vec = "B2".into(); - b2.resize(32, 0); - let mut c: Vec = "C".into(); - c.resize(32, 0); - let mut d: Vec = "D".into(); - d.resize(32, 0); - if l == MultiLocation::parent() { - return Some(CurrencyId::R) - } - match l { - MultiLocation { parents, interior } if parents == 1 => match interior { - X2(Parachain(1), GeneralKey { data, .. }) if data.to_vec() == a => - Some(CurrencyId::A), - X2(Parachain(1), GeneralKey { data, .. }) if data.to_vec() == a1 => - Some(CurrencyId::A1), - X2(Parachain(2), GeneralKey { data, .. }) if data.to_vec() == b => - Some(CurrencyId::B), - X2(Parachain(2), GeneralKey { data, .. }) if data.to_vec() == b1 => - Some(CurrencyId::B1), - X2(Parachain(2), GeneralKey { data, .. }) if data.to_vec() == b2 => - Some(CurrencyId::B2), - X2(Parachain(3), GeneralKey { data, .. }) if data.to_vec() == c => - Some(CurrencyId::C), - X2(Parachain(4), GeneralKey { data, .. }) if data.to_vec() == d => - Some(CurrencyId::D), - _ => None, - }, - MultiLocation { parents, interior } if parents == 0 => match interior { - X1(GeneralKey { data, .. }) if data.to_vec() == a => Some(CurrencyId::A), - X1(GeneralKey { data, .. }) if data.to_vec() == b => Some(CurrencyId::B), - X1(GeneralKey { data, .. }) if data.to_vec() == a1 => Some(CurrencyId::A1), - X1(GeneralKey { data, .. }) if data.to_vec() == b1 => Some(CurrencyId::B1), - X1(GeneralKey { data, .. }) if data.to_vec() == b2 => Some(CurrencyId::B2), - X1(GeneralKey { data, .. }) if data.to_vec() == c => Some(CurrencyId::C), - X1(GeneralKey { data, .. }) if data.to_vec() == d => Some(CurrencyId::D), - _ => None, - }, + fn convert(location: MultiLocation) -> Option { + match location { + // Just for testing purposes, parachain id is not verified so this can be used by all runtimes + MultiLocation { parents: 1, interior: X2(Parachain(_id), PalletInstance(10)) } => + Some(CurrencyId::Native), + MultiLocation { parents: 0, interior: X1(PalletInstance(10)) } => + Some(CurrencyId::Native), _ => None, } } } + impl Convert> for CurrencyIdConvert { fn convert(a: MultiAsset) -> Option { - if let MultiAsset { fun: Fungible(_), id: Concrete(id) } = a { + if let MultiAsset { id: AssetId::Concrete(id), fun: _ } = a { Self::convert(id) } else { - Option::None + None } } } -// decl_test_parachain! { -// pub struct SiblingParachain { -// Runtime = Runtime, -// RuntimeOrigin = RuntimeOrigin, -// XcmpMessageHandler = XcmpQueue, -// DmpMessageHandler = DmpQueue, -// new_ext = para_ext(ParachainType::Sibling), -// } -// } - -// I think we're not going to use this but our relay mock instead -// I'll just leave it here in case we need something from it -// decl_test_relay_chain! { -// pub struct Relay { -// Runtime = Runtime, -// RuntimeCall = relay::RuntimeCall, -// RuntimeEvent = relay::RuntimeEvent, -// //XcmConfig = relay::XcmConfig, -// //MessageQueue = relay::MessageQueue, -// System = relay::System, -// new_ext = relay_ext(), -// } -// } - -// decl_test_relay_chain! { -// pub struct PolkadotRelay { -// Runtime = polkadot_runtime::Runtime, -// XcmConfig = polkadot_runtime::xcm_config::XcmConfig, -// new_ext = polkadot_relay_ext(), -// } -// } - -// decl_test_network! { -// pub struct TestNet { -// relay_chain = PolkadotRelay, -// parachains = vec![ -// (1, SiblingParachain), -// ], -// } -// } - -// impl orml_tokens::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type Balance = Balance; -// type Amount = Amount; -// type CurrencyId = CurrencyId; -// type WeightInfo = (); -// type ExistentialDeposits = ExistentialDeposits; -// type CurrencyHooks = (); -// type MaxLocks = ConstU32<50>; -// type MaxReserves = ConstU32<50>; -// type ReserveIdentifier = [u8; 8]; -// type DustRemovalWhitelist = Everything; -// } - -// 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 parachain_info::Config for Runtime {} - -// parameter_types! { -// pub const RelayLocation: MultiLocation = MultiLocation::parent(); -// pub const RelayNetwork: NetworkId = NetworkId::Kusama; -// pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); -// pub UniversalLocation: InteriorMultiLocation = -// X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); -// } - -// pub type LocationToAccountId = ( -// ParentIsPreset, -// SiblingParachainConvertsVia, -// AccountId32Aliases, -// ); - -// pub type XcmOriginToCallOrigin = ( -// SovereignSignedViaLocation, -// RelayChainAsNative, -// SiblingParachainAsNative, -// SignedAccountId32AsNative, -// XcmPassthrough, -// ); - -// pub type LocalAssetTransactor = MultiCurrencyAdapter< -// Tokens, -// (), -// IsNativeConcrete, -// AccountId, -// LocationToAccountId, -// CurrencyId, -// CurrencyIdConvert, -// (), -// >; - -// pub type XcmRouter = ParachainXcmRouter; -// pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecution); - -// parameter_types! { -// pub const UnitWeightCost: Weight = Weight::from_parts(10, 10); -// pub const BaseXcmWeight: Weight = Weight::from_parts(100_000_000, 100_000_000); -// pub const MaxInstructions: u32 = 100; -// pub const MaxAssetsIntoHolding: u32 = 64; -// } - -// pub struct XcmConfig; -// impl Config for XcmConfig { -// type RuntimeCall = RuntimeCall; -// type XcmSender = XcmRouter; -// type AssetTransactor = LocalAssetTransactor; -// type OriginConverter = XcmOriginToCallOrigin; -// type IsReserve = MultiNativeAsset; -// type IsTeleporter = NativeAsset; -// type UniversalLocation = UniversalLocation; -// type Barrier = Barrier; -// type Weigher = FixedWeightBounds; -// type Trader = (); -// type ResponseHandler = (); -// type AssetTrap = PolkadotXcm; -// type AssetClaims = PolkadotXcm; -// type SubscriptionService = PolkadotXcm; -// type AssetLocker = PolkadotXcm; -// type AssetExchanger = (); -// type PalletInstancesInfo = (); -// type MaxAssetsIntoHolding = MaxAssetsIntoHolding; -// type FeeManager = (); -// type MessageExporter = (); -// type UniversalAliases = Nothing; -// type CallDispatcher = RuntimeCall; -// type SafeCallFilter = Everything; -// } - -// pub struct ChannelInfo; -// impl GetChannelInfo for ChannelInfo { -// fn get_channel_status(_id: ParaId) -> ChannelStatus { -// ChannelStatus::Ready(10, 10) -// } -// fn get_channel_max(_id: ParaId) -> Option { -// Some(usize::max_value()) -// } -// } - -// impl cumulus_pallet_xcmp_queue::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type XcmExecutor = XcmExecutor; -// type ChannelInfo = ChannelInfo; -// type VersionWrapper = (); -// type ExecuteOverweightOrigin = EnsureRoot; -// type ControllerOrigin = EnsureRoot; -// type ControllerOriginConverter = XcmOriginToCallOrigin; -// type WeightInfo = (); -// type PriceForSiblingDelivery = (); -// } - -// impl cumulus_pallet_dmp_queue::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type XcmExecutor = XcmExecutor; -// type ExecuteOverweightOrigin = EnsureRoot; -// } - -// impl cumulus_pallet_xcm::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type XcmExecutor = XcmExecutor; -// } - -//pub type LocalOriginToLocation = SignedToAccountId32; +/// Convert an incoming `MultiLocation` into a `CurrencyId` if possible. +/// Here we need to know the canonical representation of all the tokens we handle in order to +/// correctly convert their `MultiLocation` representation into our internal `CurrencyId` type. +impl xcm_executor::traits::Convert for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Result { + >>::convert(location.clone()) + .ok_or(location) + } +} + +/// A `FilterAssetLocation` implementation. Filters multi native assets whose +/// reserve is same with `origin`. +pub struct MultiNativeAsset(PhantomData); +impl ContainsPair for MultiNativeAsset +where + ReserveProvider: Reserve, +{ + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { + if let Some(ref reserve) = ReserveProvider::reserve(asset) { + if reserve == origin { + return true + } + } + false + } +} + +/// Means for transacting the fungibles assets of ths parachain. +pub type FungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation + Tokens, + // This means that this adapter should handle any token that `CurrencyIdConvert` can convert + // to `CurrencyId`, the `CurrencyId` type of `Tokens`, the fungibles implementation it uses. + ConvertedConcreteId, + // Convert an XCM MultiLocation into a local account id + LocationToAccountId, + // Our chain's account ID type (we can't get away without mentioning it explicitly) + AccountId, + // We dont allow teleports. + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognized. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognized. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. + pub UnitWeightCost: XCMWeight = XCMWeight::from_parts(1_000_000_000, 0); + pub const MaxInstructions: u32 = 100; + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); + pub const BaseXcmWeight: XCMWeight = XCMWeight::from_parts(150_000_000, 0); + pub const MaxAssetsForTransfer: usize = 2; +} + +match_types! { + pub type ParentOrParentsExecutivePlurality: impl Contains = { + MultiLocation { parents: 1, interior: Here } | + MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) } + }; +} + +//TODO: move DenyThenTry to polkadot's xcm module. +/// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else. +/// If it passes the Deny, and matches one of the Allow cases then it is let through. +pub struct DenyThenTry(PhantomData, PhantomData) +where + Deny: ShouldExecute, + Allow: ShouldExecute; + +impl ShouldExecute for DenyThenTry +where + Deny: ShouldExecute, + Allow: ShouldExecute, +{ + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + max_weight: XCMWeight, + weight_credit: &mut XCMWeight, + ) -> Result<(), ()> { + Deny::should_execute(origin, instructions, max_weight, weight_credit)?; + Allow::should_execute(origin, instructions, max_weight, weight_credit) + } +} + +// See issue #5233 +pub struct DenyReserveTransferToRelayChain; +impl ShouldExecute for DenyReserveTransferToRelayChain { + fn should_execute( + origin: &MultiLocation, + instructions: &mut [Instruction], + _max_weight: XCMWeight, + _weight_credit: &mut XCMWeight, + ) -> Result<(), ()> { + if instructions.iter().any(|inst| { + matches!( + inst, + InitiateReserveWithdraw { + reserve: MultiLocation { parents: 1, interior: Here }, + .. + } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } | + TransferReserveAsset { + dest: MultiLocation { parents: 1, interior: Here }, + .. + } + ) + }) { + return Err(()) // Deny + } + + // allow reserve transfers to arrive from relay chain + if matches!(origin, MultiLocation { parents: 1, interior: Here }) && + instructions.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. })) + { + log::warn!( + target: "xcm::barriers", + "Unexpected ReserveAssetDeposited from the relay chain", + ); + } + // Permit everything else + Ok(()) + } +} + +pub type Barrier = AllowUnpaidExecutionFrom; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = FungiblesTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + // Teleporting is disabled. + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = AllTokensAreCreatedEqualToWeight; + type ResponseHandler = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = ConstU32<8>; + type FeeManager = (); + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = SignedToAccountId32; + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type CurrencyMatcher = (); + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Everything; + // ^ Disable dispatchable execute on the XCM pallet. + // Needs to be `Everything` for local testing. + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Nothing; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; + // ^ Override for AdvertisedXcmVersion default + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + type TrustedLockers = (); + type SovereignAccountOf = LocationToAccountId; + type MaxLockers = ConstU32<8>; + type WeightInfo = pallet_xcm::TestWeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type ReachableDest = ReachableDest; +} #[cfg(feature = "runtime-benchmarks")] parameter_types! { pub ReachableDest: Option = Some(Parent.into()); } -// impl pallet_xcm::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type SendXcmOrigin = EnsureXcmOrigin; -// type XcmRouter = XcmRouter; -// type ExecuteXcmOrigin = EnsureXcmOrigin; -// type XcmExecuteFilter = Everything; -// type XcmExecutor = XcmExecutor; -// type XcmTeleportFilter = Nothing; -// type XcmReserveTransferFilter = Everything; -// type Weigher = FixedWeightBounds; -// 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 = (); -// type SovereignAccountOf = (); -// type MaxLockers = ConstU32<8>; -// type WeightInfo = pallet_xcm::TestWeightInfo; -// #[cfg(feature = "runtime-benchmarks")] -// type ReachableDest = ReachableDest; -// } +parameter_type_with_key! { + pub ParachainMinFee: |_location: MultiLocation| -> Option { + None + }; +} + +impl orml_xtokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = BaseXcmWeight; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; //TODO to support hrmp transfer beetween parachain adjust this parameter + type MultiLocationsFilter = Everything; + type ReserveProvider = RelativeReserveProvider; + type UniversalLocation = UniversalLocation; +} pub struct AccountIdToMultiLocation; impl Convert for AccountIdToMultiLocation { fn convert(account: AccountId) -> MultiLocation { - X1(Junction::AccountId32 { network: None, id: account.into() }).into() + MultiLocation { + parents: 0, + interior: X1(xcm::v3::Junction::AccountId32 { network: None, id: account.into() }), + } } } +impl cumulus_pallet_xcm::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; +} + +#[cfg(feature = "runtime-benchmarks")] parameter_types! { - pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); - pub const MaxAssetsForTransfer: usize = 3; + pub ReachableDest: Option = Some(Parent.into()); } match_types! { @@ -409,51 +381,28 @@ match_types! { }; } -parameter_type_with_key! { - pub ParachainMinFee: |location: MultiLocation| -> Option { - #[allow(clippy::match_ref_pats)] // false positive - match (location.parents, location.first_interior()) { - (1, Some(Parachain(3))) => Some(40), - _ => None, - } - }; -} - -// impl orml_xtokens::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type Balance = Balance; -// type CurrencyId = CurrencyId; -// type CurrencyIdConvert = CurrencyIdConvert; -// type AccountIdToMultiLocation = AccountIdToMultiLocation; -// type SelfLocation = SelfLocation; -// type MultiLocationsFilter = ParentOrParachains; -// type MinXcmFee = ParachainMinFee; -// type XcmExecutor = XcmExecutor; -// type Weigher = FixedWeightBounds; -// type BaseXcmWeight = BaseXcmWeight; -// type UniversalLocation = UniversalLocation; -// type MaxAssetsForTransfer = MaxAssetsForTransfer; -// type ReserveProvider = AbsoluteReserveProvider; -// } - -// impl orml_xcm::Config for Runtime { -// type RuntimeEvent = RuntimeEvent; -// type SovereignOrigin = EnsureRoot; -// } - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( - pub enum Test where + pub enum Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Storage, Config, Event}, Tokens: orml_tokens::{Pallet, Storage, Config, Event}, + XTokens: orml_xtokens::{Pallet, Storage, Call, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Event}, + PolkadotXcm: pallet_xcm, + ParachainSystem: cumulus_pallet_parachain_system::{ + Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned, + }, + ParachainInfo: parachain_info::{Pallet, Storage, Config}, + XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event}, + DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event}, + CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin}, } ); @@ -466,7 +415,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; } -impl frame_system::Config for Test { +impl frame_system::Config for Runtime { type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); @@ -480,7 +429,7 @@ impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type RuntimeEvent = TestEvent; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; @@ -489,12 +438,10 @@ impl frame_system::Config for Test { type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = SS58Prefix; - type OnSetCode = (); + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; type MaxConsumers = frame_support::traits::ConstU32<16>; } -pub type TestEvent = RuntimeEvent; - parameter_types! { pub const MaxLocks: u32 = 50; } @@ -519,7 +466,7 @@ impl type OnKilledTokenAccount = (); } -impl orml_tokens::Config for Test { +impl orml_tokens::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type Amount = Amount; @@ -538,7 +485,7 @@ parameter_types! { pub const MaxReserves: u32 = 50; } -impl pallet_balances::Config for Test { +impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; /// The type for recording an account's balance. type Balance = Balance; @@ -547,7 +494,76 @@ impl pallet_balances::Config for Test { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type WeightInfo = pallet_balances::weights::SubstrateWeight; + type WeightInfo = pallet_balances::weights::SubstrateWeight; type MaxReserves = MaxReserves; type ReserveIdentifier = (); } + +parameter_types! { + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type DmpMessageHandler = DmpQueue; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type PriceForSiblingDelivery = (); + type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = EnsureRoot; +} + +/// A trader who believes all tokens are created equal to "weight" of any chain, +/// which is not true, but good enough to mock the fee payment of XCM execution. +/// +/// This mock will always trade `n` amount of weight to `n` amount of tokens. +pub struct AllTokensAreCreatedEqualToWeight(MultiLocation); +impl WeightTrader for AllTokensAreCreatedEqualToWeight { + fn new() -> Self { + Self(MultiLocation::parent()) + } + + fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { + let asset_id = payment.fungible.iter().next().expect("Payment must be something; qed").0; + let required = + MultiAsset { id: asset_id.clone(), fun: Fungible(weight.ref_time() as u128) }; + + if let MultiAsset { fun: _, id: Concrete(ref id) } = &required { + self.0 = id.clone(); + } + + let unused = payment.checked_sub(required).map_err(|_| XcmError::TooExpensive)?; + Ok(unused) + } + + fn refund_weight(&mut self, weight: Weight) -> Option { + if weight.is_zero() { + None + } else { + Some((self.0.clone(), weight.ref_time() as u128).into()) + } + } +} diff --git a/runtime/integration-tests/src/test_macros.rs b/runtime/integration-tests/src/test_macros.rs index 15253a388..702d68874 100644 --- a/runtime/integration-tests/src/test_macros.rs +++ b/runtime/integration-tests/src/test_macros.rs @@ -461,7 +461,7 @@ macro_rules! parachain1_transfer_asset_to_parachain2_and_back { }}; } -macro_rules! transfer_native_token_from_parachain1_to_parachain2 { +macro_rules! transfer_native_token_from_parachain1_to_parachain2_and_back { ( $mocknet:ident, $parachain1_runtime:ident, @@ -472,21 +472,15 @@ macro_rules! transfer_native_token_from_parachain1_to_parachain2 { $parachain2_id:ident ) => {{ use crate::mock::{units, ALICE, BOB}; - use frame_support::{log::info, traits::fungibles::Inspect}; + use frame_support::traits::fungibles::Inspect; use polkadot_core_primitives::Balance; - use sp_tracing; use xcm::latest::{ - Junction, - Junction::AccountId32, - Junctions::{X2, X3}, - MultiLocation, WeightLimit, + Junction, Junction::AccountId32, Junctions::X2, MultiLocation, WeightLimit, }; use $parachain1_runtime::CurrencyId; $mocknet::reset(); - sp_tracing::try_init_simple(); - let transfer_amount: Balance = units(10); let asset_location = MultiLocation::new( 1, @@ -501,12 +495,18 @@ macro_rules! transfer_native_token_from_parachain1_to_parachain2 { native_tokens_before ); }); + $parachain2::execute_with(|| { + assert_eq!( + $parachain2_runtime::Tokens::balance(CurrencyId::Native, &BOB.into()), + native_tokens_before + ); + }); // Execute the transfer from parachain1 to parachain2 $parachain1::execute_with(|| { - use $parachain1_runtime::XTokens; + use $parachain1_runtime::{RuntimeEvent, System, XTokens}; - // transfer using multilocation + // Transfer using multilocation assert_ok!(XTokens::transfer_multiasset( $parachain1_runtime::RuntimeOrigin::signed(ALICE.into()), Box::new((asset_location.clone(), transfer_amount).into()), @@ -523,28 +523,49 @@ macro_rules! transfer_native_token_from_parachain1_to_parachain2 { WeightLimit::Unlimited )); - // transfer using currency id - // assert_ok!(XTokens::transfer( - // $parachain1_runtime::RuntimeOrigin::signed(ALICE.into()), - // CurrencyId::Native, - // transfer_amount, - // Box::new( - // MultiLocation::new( - // 1, - // X3( - // Junction::Parachain($parachain2_id), - // Junction::PalletInstance(10), - // Junction::AccountId32 { network: None, id: BOB.into() } - // ) - // ) - // .into() - // ), - // WeightLimit::Unlimited - // )); + assert!(System::events().iter().any(|r| matches!( + r.event, + RuntimeEvent::XTokens(orml_xtokens::Event::TransferredMultiAssets { .. }) + ))); + }); + + // Verify BOB's balance on parachain2 after receiving + // Should increase by the transfer amount + $parachain2::execute_with(|| { + assert_eq!( + $parachain2_runtime::Tokens::balance(CurrencyId::Native, &BOB.into()), + native_tokens_before + transfer_amount + ); }); + // Verify ALICE's balance on parachain1 after transfer $parachain1::execute_with(|| { - use $parachain1_runtime::{RuntimeEvent, System}; + assert_eq!( + $parachain1_runtime::Tokens::balance(CurrencyId::Native, &ALICE.into()), + native_tokens_before - transfer_amount + ); + }); + + // Send same amount back to ALICE on parachain1 + $parachain2::execute_with(|| { + use $parachain2_runtime::{RuntimeEvent, System, XTokens}; + + // Transfer using the same multilocation + assert_ok!(XTokens::transfer_multiasset( + $parachain2_runtime::RuntimeOrigin::signed(BOB.into()), + Box::new((asset_location.clone(), transfer_amount).into()), + Box::new( + MultiLocation { + parents: 1, + interior: X2( + Junction::Parachain($parachain1_id), + AccountId32 { network: None, id: ALICE } + ) + } + .into() + ), + WeightLimit::Unlimited + )); assert!(System::events().iter().any(|r| matches!( r.event, @@ -552,23 +573,21 @@ macro_rules! transfer_native_token_from_parachain1_to_parachain2 { ))); }); - // Verify the balance on the parachain2 after transfer + // Verify BOB's balance on parachain2 after transfer + // Should become the same amount as initial balance before both transfers $parachain2::execute_with(|| { - // Currently failing - // assert_eq!( - // $parachain2_runtime::Tokens::balance(CurrencyId::Native, &BOB.into()), - // transfer_amount - // ); - - // log BOB's balance to see if there's any amount - info!("{:?}", $parachain2_runtime::Tokens::balance(CurrencyId::Native, &BOB.into())); + assert_eq!( + $parachain2_runtime::Tokens::balance(CurrencyId::Native, &BOB.into()), + native_tokens_before + ); }); - // Verify the balance on the parachain1 after transfer + // Verify ALICE's balance on parachain1 after receiving + // Should become the same amount as initial balance before both transfers $parachain1::execute_with(|| { assert_eq!( $parachain1_runtime::Tokens::balance(CurrencyId::Native, &ALICE.into()), - native_tokens_before - transfer_amount + native_tokens_before ); }); }}; @@ -580,4 +599,4 @@ pub(super) use parachain1_transfer_asset_to_parachain2_and_back; pub(super) use parachain1_transfer_incorrect_asset_to_parachain2_should_fail; pub(super) use transfer_10_relay_token_from_parachain_to_relay_chain; pub(super) use transfer_20_relay_token_from_relay_chain_to_parachain; -pub(super) use transfer_native_token_from_parachain1_to_parachain2; +pub(super) use transfer_native_token_from_parachain1_to_parachain2_and_back;