From 81abe456f4bd5824ddccc3743c4f91772a69b963 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Tue, 24 Jan 2023 08:27:01 +0100 Subject: [PATCH 01/16] add foreign assets to westmint --- .../runtimes/assets/westmint/src/lib.rs | 32 +++++++++++- .../assets/westmint/src/xcm_config.rs | 50 +++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 9f065e0ef7c..80a34f65f18 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -65,7 +65,7 @@ use parachains_common::{ Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ForeignCreators, MultiLocationForAssetId, XcmConfig, XcmOriginToTransactDispatchOrigin}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -242,6 +242,34 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +/// Assets managed by some foreign location. Note: we do not declare a `ForeignAssetsCall` type, as +/// this type is used in proxy definitions. We assume that a foreign location would not want to set +/// an individual, local account as a proxy for the issuance of their assets. This issuance should +/// be managed by the foreign location's governance. +type ForeignAssetsInstance = pallet_assets::Instance2; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = MultiLocationForAssetId; + type AssetIdParameter = MultiLocationForAssetId; + type Currency = Balances; + type CreateOrigin = ForeignCreators; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = weights::pallet_assets::WeightInfo; + type CallbackHandle = (); + type AssetAccountDeposit = AssetAccountDeposit; + type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; +} + parameter_types! { // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -645,6 +673,7 @@ construct_runtime!( Assets: pallet_assets::::{Pallet, Call, Storage, Event} = 50, Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, Nfts: pallet_nfts::{Pallet, Call, Storage, Event} = 52, + ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 53, } ); @@ -694,6 +723,7 @@ mod benches { define_benchmarks!( [frame_system, SystemBench::] [pallet_assets, Assets] + [pallet_assets, ForeignAssets] [pallet_balances, Balances] [pallet_multisig, Multisig] [pallet_nfts, Nfts] diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index ca30415bd29..2aa6c277523 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -20,16 +20,16 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, + traits::{ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, PalletInfoAccess}, }; -use pallet_xcm::XcmPassthrough; +use pallet_xcm::{EnsureXcm, XcmPassthrough}; use parachains_common::{ impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, DenyReserveTransferToRelayChain, DenyThenTry, }, }; -use polkadot_parachain::primitives::Sibling; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ @@ -42,7 +42,7 @@ use xcm_builder::{ WithComputedOrigin, }; use xcm_executor::{ - traits::{JustTry, WithOriginFilter}, + traits::{Convert, JustTry, WithOriginFilter}, XcmExecutor, }; @@ -381,3 +381,45 @@ impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } + +pub type MultiLocationForAssetId = MultiLocation; + +pub type SovereignAccountOf = ( + SiblingParachainConvertsVia, + AccountId32Aliases, + ParentIsPreset, +); + +// `EnsureOriginWithArg` impl for `CreateOrigin` that allows only XCM origins that are locations +// containing the class location. +pub struct ForeignCreators; +impl EnsureOriginWithArg for ForeignCreators { + type Success = AccountId; + + fn try_origin( + o: RuntimeOrigin, + a: &MultiLocation, + ) -> sp_std::result::Result { + let origin_location = EnsureXcm::::try_origin(o.clone())?; + if !a.starts_with(&origin_location) { + return Err(o) + } + SovereignAccountOf::convert(origin_location).map_err(|_| o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin(a: &MultiLocation) -> RuntimeOrigin { + pallet_xcm::Origin::Xcm(a.clone()).into() + } +} + +/// Simple conversion of `u32` into an `AssetId` for use in benchmarking. +pub struct XcmBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +use pallet_assets::BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> MultiLocation { + MultiLocation { parents: 1, interior: X1(Parachain(id)) } + } +} From 0da76e02a646f9e3e0bfa71945e1cc43c5a04586 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Tue, 24 Jan 2023 10:24:09 +0100 Subject: [PATCH 02/16] add foreign assets to statemine --- .../runtimes/assets/statemine/src/lib.rs | 33 +++++++++++- .../assets/statemine/src/xcm_config.rs | 53 +++++++++++++++++-- .../runtimes/assets/westmint/src/lib.rs | 4 +- .../assets/westmint/src/xcm_config.rs | 5 +- 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index 4272ce71d8a..f91661bbe16 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -64,7 +64,7 @@ use parachains_common::{ Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{KsmLocation, XcmConfig}; +use xcm_config::{ForeignCreators, KsmLocation, MultiLocationForAssetId, XcmConfig}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -260,6 +260,34 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +/// Assets managed by some foreign location. Note: we do not declare a `ForeignAssetsCall` type, as +/// this type is used in proxy definitions. We assume that a foreign location would not want to set +/// an individual, local account as a proxy for the issuance of their assets. This issuance should +/// be managed by the foreign location's governance. +type ForeignAssetsInstance = pallet_assets::Instance2; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = MultiLocationForAssetId; + type AssetIdParameter = MultiLocationForAssetId; + type Currency = Balances; + type CreateOrigin = ForeignCreators; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = AssetsStringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = weights::pallet_assets::WeightInfo; + type CallbackHandle = (); + type AssetAccountDeposit = AssetAccountDeposit; + type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; +} + parameter_types! { // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -634,6 +662,8 @@ construct_runtime!( // The main stage. Assets: pallet_assets::::{Pallet, Call, Storage, Event} = 50, Uniques: pallet_uniques::{Pallet, Call, Storage, Event} = 51, + // Reserving 52 for pallet_nfts + ForeignAssets: pallet_assets::::{Pallet, Call, Storage, Event} = 53, #[cfg(feature = "state-trie-version-1")] StateTrieMigration: pallet_state_trie_migration = 70, @@ -686,6 +716,7 @@ mod benches { define_benchmarks!( [frame_system, SystemBench::] [pallet_assets, Assets] + [pallet_assets, ForeignAssets] [pallet_balances, Balances] [pallet_multisig, Multisig] [pallet_proxy, Proxy] diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 43b1c67f0bd..05b6765606f 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -20,16 +20,19 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, + traits::{ + ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, + PalletInfoAccess, + }, }; -use pallet_xcm::XcmPassthrough; +use pallet_xcm::{EnsureXcm, XcmPassthrough}; use parachains_common::{ impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, DenyReserveTransferToRelayChain, DenyThenTry, }, }; -use polkadot_parachain::primitives::Sibling; +use polkadot_parachain::primitives::{Id as ParaId, Sibling}; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ @@ -42,7 +45,7 @@ use xcm_builder::{ WithComputedOrigin, }; use xcm_executor::{ - traits::{JustTry, WithOriginFilter}, + traits::{Convert, JustTry, WithOriginFilter}, XcmExecutor, }; @@ -390,3 +393,45 @@ impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } + +pub type MultiLocationForAssetId = MultiLocation; + +pub type SovereignAccountOf = ( + SiblingParachainConvertsVia, + AccountId32Aliases, + ParentIsPreset, +); + +// `EnsureOriginWithArg` impl for `CreateOrigin` that allows only XCM origins that are locations +// containing the class location. +pub struct ForeignCreators; +impl EnsureOriginWithArg for ForeignCreators { + type Success = AccountId; + + fn try_origin( + o: RuntimeOrigin, + a: &MultiLocation, + ) -> sp_std::result::Result { + let origin_location = EnsureXcm::::try_origin(o.clone())?; + if !a.starts_with(&origin_location) { + return Err(o) + } + SovereignAccountOf::convert(origin_location).map_err(|_| o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin(a: &MultiLocation) -> RuntimeOrigin { + pallet_xcm::Origin::Xcm(a.clone()).into() + } +} + +/// Simple conversion of `u32` into an `AssetId` for use in benchmarking. +pub struct XcmBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +use pallet_assets::BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> MultiLocation { + MultiLocation { parents: 1, interior: X1(Parachain(id)) } + } +} diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index 80a34f65f18..19dc7002212 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -65,7 +65,9 @@ use parachains_common::{ Index, Signature, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; -use xcm_config::{ForeignCreators, MultiLocationForAssetId, XcmConfig, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + ForeignCreators, MultiLocationForAssetId, XcmConfig, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 2aa6c277523..df9f14cb141 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -20,7 +20,10 @@ use super::{ }; use frame_support::{ match_types, parameter_types, - traits::{ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, PalletInfoAccess}, + traits::{ + ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, + PalletInfoAccess, + }, }; use pallet_xcm::{EnsureXcm, XcmPassthrough}; use parachains_common::{ From 05902412e5b3aa2d9f109958e1efda3465da39d4 Mon Sep 17 00:00:00 2001 From: muharem Date: Mon, 13 Feb 2023 04:51:54 +0100 Subject: [PATCH 03/16] use updated api for ensure origin trait --- parachains/runtimes/assets/statemine/src/xcm_config.rs | 4 ++-- parachains/runtimes/assets/westmint/src/xcm_config.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 05b6765606f..a37b66a32bc 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -420,8 +420,8 @@ impl EnsureOriginWithArg for ForeignCreators { } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin(a: &MultiLocation) -> RuntimeOrigin { - pallet_xcm::Origin::Xcm(a.clone()).into() + fn try_successful_origin(a: &MultiLocation) -> Result { + Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 377fc1ebbd3..ed06d531822 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -410,8 +410,8 @@ impl EnsureOriginWithArg for ForeignCreators { } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin(a: &MultiLocation) -> RuntimeOrigin { - pallet_xcm::Origin::Xcm(a.clone()).into() + fn try_successful_origin(a: &MultiLocation) -> Result { + Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) } } From 60a60894f633bd97d519c12d444ad3800e2a11bf Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Mar 2023 12:34:46 +0100 Subject: [PATCH 04/16] Assets/ForeignAssets tests and fixes (#2167) * Test for create and transfer `TrustBackedAssets` with AssetTransactor * Test for transfer `local Currency` with AssetTransactor * Test for create foreign assets (covers foreign relaychain currency) * Added `ForeignFungiblesTransactor` and test for transfer `ForeignAssets` with AssetTransactor * Removed unused `pub const Local: MultiLocation` * Changed `ParaId -> Sibling` for `SiblingParachainConvertsVia` * Test for create foreign assets (covers local sibling parachain assets) * Reverted stuff for ForeignCreators from different global consensus (moved to transfer asset branch) * Refactor `weight_limit` for `execute_xcm` * Added test for `set_metadata` by ForeignCreator with `xcm::Transact(set_metadata)` * Renamed `receive_teleported_asset_works` -> `receive_teleported_asset_for_native_asset_works` * Allow `ForeignCreators` only for sibling parachains * Unify ReservedDmpWeight/ReservedXcmpWeight usage * Removed hack - replaced with `MatchedConvertedConcreteId` * Refactor `ForeignCreators` to assets-common * Add `ReceiveTeleportedAsset` test * Change test - `Utility::batch` -> Multiple `xcm::Transact` * Reusing the same deposits as for TrustBackedAssets * missing `try_successful_origin` ? * Finished `ForeignAssets` for westmint (converter, FungiblesApi, tests) * Refactoring tests - receive_teleported_asset_for_native_asset_works * ForeignAssets for statemine + refactored `receive_teleported_asset_from_foreign_creator_works` * Add `ForeignAssets` to statemine `FungiblesApi` * Add `asset_transactor_transfer_with_local_consensus_currency_works` to all runtimes * Added `asset_transactor_transfer_with_trust_backed_assets_works` test * Added `asset_transactor_transfer_with_foreign_assets_works` * Fix `missing `try_successful_origin` in implementation` * Added `create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works` * Added `ExpectTransactStatus` check * Small rename * Extended `test_assets_balances_api_works` with ForeignAssets for `statemine` * PR fixes * Update parachains/runtimes/assets/test-utils/src/test_cases.rs --------- Co-authored-by: parity-processbot <> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> --- Cargo.lock | 9 + parachains/runtimes/assets/common/Cargo.toml | 12 + .../assets/common/src/foreign_creators.rs | 56 + .../assets/common/src/fungible_conversion.rs | 60 +- parachains/runtimes/assets/common/src/lib.rs | 201 +++- .../runtimes/assets/common/src/matching.rs | 78 ++ .../runtimes/assets/statemine/Cargo.toml | 1 + .../runtimes/assets/statemine/src/lib.rs | 48 +- .../assets/statemine/src/xcm_config.rs | 116 +- .../runtimes/assets/statemine/tests/tests.rs | 224 +++- .../runtimes/assets/statemint/Cargo.toml | 1 + .../assets/statemint/src/xcm_config.rs | 3 +- .../runtimes/assets/statemint/tests/tests.rs | 110 +- .../runtimes/assets/test-utils/Cargo.toml | 14 +- .../runtimes/assets/test-utils/src/lib.rs | 61 +- .../assets/test-utils/src/test_cases.rs | 1016 +++++++++++++++++ .../runtimes/assets/westmint/Cargo.toml | 1 + .../runtimes/assets/westmint/src/lib.rs | 50 +- .../assets/westmint/src/xcm_config.rs | 119 +- .../runtimes/assets/westmint/tests/tests.rs | 229 +++- .../collectives-polkadot/src/xcm_config.rs | 1 - 21 files changed, 2088 insertions(+), 322 deletions(-) create mode 100644 parachains/runtimes/assets/common/src/foreign_creators.rs create mode 100644 parachains/runtimes/assets/common/src/matching.rs create mode 100644 parachains/runtimes/assets/test-utils/src/test_cases.rs diff --git a/Cargo.lock b/Cargo.lock index 9ce52f3fe3f..6c3eac10beb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,26 +327,35 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" name = "asset-test-utils" version = "1.0.0" dependencies = [ + "assets-common", + "cumulus-pallet-parachain-system", "frame-support", "frame-system", "hex-literal", + "pallet-assets", "pallet-balances", "pallet-collator-selection", "pallet-session", "parachains-common", + "parity-scale-codec", "sp-consensus-aura", "sp-core", "sp-io", "sp-runtime", "sp-std", "substrate-wasm-builder", + "xcm", + "xcm-executor", ] [[package]] name = "assets-common" version = "0.1.0" dependencies = [ + "cumulus-primitives-core", "frame-support", + "log", + "pallet-xcm", "parachains-common", "parity-scale-codec", "sp-api", diff --git a/parachains/runtimes/assets/common/Cargo.toml b/parachains/runtimes/assets/common/Cargo.toml index c551a97757b..7a795057cd5 100644 --- a/parachains/runtimes/assets/common/Cargo.toml +++ b/parachains/runtimes/assets/common/Cargo.toml @@ -7,6 +7,7 @@ description = "Assets common utilities" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4.17", default-features = false } # Substrate frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -14,12 +15,14 @@ sp-api = { git = "https://github.com/paritytech/substrate", default-features = f sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Polkadot +pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } # Cumulus parachains-common = { path = "../../../common", default-features = false } +cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -28,11 +31,20 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran default = [ "std" ] std = [ "codec/std", + "log/std", "frame-support/std", "parachains-common/std", + "cumulus-primitives-core/std", "sp-api/std", "sp-std/std", + "pallet-xcm/std", "xcm/std", "xcm-builder/std", "xcm-executor/std", ] + +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", +] diff --git a/parachains/runtimes/assets/common/src/foreign_creators.rs b/parachains/runtimes/assets/common/src/foreign_creators.rs new file mode 100644 index 00000000000..3d7567409f6 --- /dev/null +++ b/parachains/runtimes/assets/common/src/foreign_creators.rs @@ -0,0 +1,56 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support::traits::{ + ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, OriginTrait, +}; +use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; +use xcm::latest::MultiLocation; +use xcm_executor::traits::Convert; + +// `EnsureOriginWithArg` impl for `CreateOrigin` that allows only XCM origins that are locations +// containing the class location. +pub struct ForeignCreators( + sp_std::marker::PhantomData<(IsForeign, AccountOf, AccountId)>, +); +impl< + IsForeign: ContainsPair, + AccountOf: Convert, + AccountId: Clone, + RuntimeOrigin: From + OriginTrait + Clone, + > EnsureOriginWithArg + for ForeignCreators +where + RuntimeOrigin::PalletsOrigin: + From + TryInto, +{ + type Success = AccountId; + + fn try_origin( + origin: RuntimeOrigin, + asset_location: &MultiLocation, + ) -> sp_std::result::Result { + let origin_location = EnsureXcm::::try_origin(origin.clone())?; + if !IsForeign::contains(&asset_location, &origin_location) { + return Err(origin) + } + AccountOf::convert(origin_location).map_err(|_| origin) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &MultiLocation) -> Result { + Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) + } +} diff --git a/parachains/runtimes/assets/common/src/fungible_conversion.rs b/parachains/runtimes/assets/common/src/fungible_conversion.rs index 2b8413cfe6e..8ffb44b086b 100644 --- a/parachains/runtimes/assets/common/src/fungible_conversion.rs +++ b/parachains/runtimes/assets/common/src/fungible_conversion.rs @@ -1,27 +1,25 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program 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. - -// This program 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 this program. If not, see . +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Runtime API definition for assets. use crate::runtime_api::FungiblesAccessError; +use frame_support::traits::Contains; use sp_std::{borrow::Borrow, vec::Vec}; use xcm::latest::{MultiAsset, MultiLocation}; -use xcm_builder::ConvertedConcreteId; +use xcm_builder::{ConvertedConcreteId, MatchedConvertedConcreteId}; use xcm_executor::traits::{Convert, MatchesFungibles}; /// Converting any [`(AssetId, Balance)`] to [`MultiAsset`] @@ -60,6 +58,29 @@ impl< } } +impl< + AssetId: Clone, + Balance: Clone, + MatchAssetId: Contains, + ConvertAssetId: Convert, + ConvertBalance: Convert, + > MultiAssetConverter + for MatchedConvertedConcreteId +{ + fn convert_ref( + value: impl Borrow<(AssetId, Balance)>, + ) -> Result { + let (asset_id, balance) = value.borrow(); + match ConvertAssetId::reverse_ref(asset_id) { + Ok(asset_id_as_multilocation) => match ConvertBalance::reverse_ref(balance) { + Ok(amount) => Ok((asset_id_as_multilocation, amount).into()), + Err(_) => Err(FungiblesAccessError::AmountToBalanceConversionFailed), + }, + Err(_) => Err(FungiblesAccessError::AssetIdConversionFailed), + } + } +} + /// Helper function to convert collections with [`(AssetId, Balance)`] to [`MultiAsset`] pub fn convert<'a, AssetId, Balance, ConvertAssetId, ConvertBalance, Converter>( items: impl Iterator, @@ -90,11 +111,12 @@ pub fn convert_balance< #[cfg(test)] mod tests { use super::*; + use frame_support::traits::Everything; use xcm::latest::prelude::*; use xcm_executor::traits::{Identity, JustTry}; - type Converter = ConvertedConcreteId; + type Converter = MatchedConvertedConcreteId; #[test] fn converted_concrete_id_fungible_multi_asset_conversion_roundtrip_works() { diff --git a/parachains/runtimes/assets/common/src/lib.rs b/parachains/runtimes/assets/common/src/lib.rs index 28d8ca59106..fbf19b89914 100644 --- a/parachains/runtimes/assets/common/src/lib.rs +++ b/parachains/runtimes/assets/common/src/lib.rs @@ -15,39 +15,78 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod foreign_creators; pub mod fungible_conversion; +pub mod matching; pub mod runtime_api; +use crate::matching::{Equals, LocalMultiLocationPattern, ParentLocation, StartsWith}; +use frame_support::traits::EverythingBut; use parachains_common::AssetIdForTrustBackedAssets; -use xcm_builder::{AsPrefixedGeneralIndex, ConvertedConcreteId}; -use xcm_executor::traits::JustTry; +use xcm::prelude::MultiLocation; +use xcm_builder::{AsPrefixedGeneralIndex, MatchedConvertedConcreteId}; +use xcm_executor::traits::{Identity, JustTry}; /// `MultiLocation` vs `AssetIdForTrustBackedAssets` converter for `TrustBackedAssets` pub type AssetIdForTrustBackedAssetsConvert = AsPrefixedGeneralIndex; -/// [`ConvertedConcreteId`] converter dedicated for `TrustBackedAssets` +/// [`MatchedConvertedConcreteId`] converter dedicated for `TrustBackedAssets` pub type TrustBackedAssetsConvertedConcreteId = - ConvertedConcreteId< + MatchedConvertedConcreteId< AssetIdForTrustBackedAssets, Balance, + StartsWith, AssetIdForTrustBackedAssetsConvert, JustTry, >; +/// AssetId used for identifying assets by MultiLocation. +pub type MultiLocationForAssetId = MultiLocation; + +/// [`MatchedConvertedConcreteId`] converter dedicated for storing `AssetId` as `MultiLocation`. +pub type MultiLocationConvertedConcreteId = + MatchedConvertedConcreteId< + MultiLocationForAssetId, + Balance, + MultiLocationFilter, + Identity, + JustTry, + >; + +/// [`MatchedConvertedConcreteId`] converter dedicated for storing `ForeignAssets` with `AssetId` as `MultiLocation`. +/// +/// Excludes by default: +/// - parent as relay chain +/// - all local MultiLocations +/// +/// `AdditionalMultiLocationExclusionFilter` can customize additional excluded MultiLocations +pub type ForeignAssetsConvertedConcreteId = + MultiLocationConvertedConcreteId< + EverythingBut<( + // Excludes relay/parent chain currency + Equals, + // Here we rely on fact that something like this works: + // assert!(MultiLocation::new(1, X1(Parachain(100))).starts_with(&MultiLocation::parent())); + // assert!(X1(Parachain(100)).starts_with(&Here)); + StartsWith, + // Here we can exclude more stuff or leave it as `()` + AdditionalMultiLocationExclusionFilter, + )>, + Balance, + >; + #[cfg(test)] mod tests { - use super::*; use xcm::latest::prelude::*; - use xcm_executor::traits::Convert; - - frame_support::parameter_types! { - pub TrustBackedAssetsPalletLocation: MultiLocation = MultiLocation::new(5, X1(PalletInstance(13))); - } + use xcm_executor::traits::{Convert, Error as MatchError, MatchesFungibles}; #[test] fn asset_id_for_trust_backed_assets_convert_works() { + frame_support::parameter_types! { + pub TrustBackedAssetsPalletLocation: MultiLocation = MultiLocation::new(5, X1(PalletInstance(13))); + } let local_asset_id = 123456789 as AssetIdForTrustBackedAssets; let expected_reverse_ref = MultiLocation::new(5, X2(PalletInstance(13), GeneralIndex(local_asset_id.into()))); @@ -67,4 +106,146 @@ mod tests { local_asset_id ); } + + #[test] + fn trust_backed_assets_match_fungibles_works() { + frame_support::parameter_types! { + pub TrustBackedAssetsPalletLocation: MultiLocation = MultiLocation::new(0, X1(PalletInstance(13))); + } + // setup convert + type TrustBackAssetsConvert = + TrustBackedAssetsConvertedConcreteId; + + let test_data = vec![ + // missing GeneralIndex + (ma_1000(0, X1(PalletInstance(13))), Err(MatchError::AssetIdConversionFailed)), + ( + ma_1000(0, X2(PalletInstance(13), GeneralKey { data: [0; 32], length: 32 })), + Err(MatchError::AssetIdConversionFailed), + ), + ( + ma_1000(0, X2(PalletInstance(13), Parachain(1000))), + Err(MatchError::AssetIdConversionFailed), + ), + // OK + (ma_1000(0, X2(PalletInstance(13), GeneralIndex(1234))), Ok((1234, 1000))), + ( + ma_1000(0, X3(PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222))), + Ok((1234, 1000)), + ), + ( + ma_1000( + 0, + X4( + PalletInstance(13), + GeneralIndex(1234), + GeneralIndex(2222), + GeneralKey { data: [0; 32], length: 32 }, + ), + ), + Ok((1234, 1000)), + ), + // wrong pallet instance + ( + ma_1000(0, X2(PalletInstance(77), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(0, X3(PalletInstance(77), GeneralIndex(1234), GeneralIndex(2222))), + Err(MatchError::AssetNotHandled), + ), + // wrong parent + ( + ma_1000(1, X2(PalletInstance(13), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(1, X3(PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(1, X2(PalletInstance(77), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(1, X3(PalletInstance(77), GeneralIndex(1234), GeneralIndex(2222))), + Err(MatchError::AssetNotHandled), + ), + // wrong parent + ( + ma_1000(2, X2(PalletInstance(13), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(2, X3(PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222))), + Err(MatchError::AssetNotHandled), + ), + // missing GeneralIndex + (ma_1000(0, X1(PalletInstance(77))), Err(MatchError::AssetNotHandled)), + (ma_1000(1, X1(PalletInstance(13))), Err(MatchError::AssetNotHandled)), + (ma_1000(2, X1(PalletInstance(13))), Err(MatchError::AssetNotHandled)), + ]; + + for (multi_asset, expected_result) in test_data { + assert_eq!( + >::matches_fungibles(&multi_asset), + expected_result, "multi_asset: {:?}", multi_asset); + } + } + + #[test] + fn multi_location_converted_concrete_id_converter_works() { + frame_support::parameter_types! { + pub Parachain100Pattern: MultiLocation = MultiLocation::new(1, X1(Parachain(100))); + } + + // setup convert + type Convert = ForeignAssetsConvertedConcreteId, u128>; + + let test_data = vec![ + // excluded as local + (ma_1000(0, Here), Err(MatchError::AssetNotHandled)), + (ma_1000(0, X1(Parachain(100))), Err(MatchError::AssetNotHandled)), + ( + ma_1000(0, X2(PalletInstance(13), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + // excluded as parent + (ma_1000(1, Here), Err(MatchError::AssetNotHandled)), + // excluded as additional filter + (ma_1000(1, X1(Parachain(100))), Err(MatchError::AssetNotHandled)), + (ma_1000(1, X2(Parachain(100), GeneralIndex(1234))), Err(MatchError::AssetNotHandled)), + ( + ma_1000(1, X3(Parachain(100), PalletInstance(13), GeneralIndex(1234))), + Err(MatchError::AssetNotHandled), + ), + // ok + (ma_1000(1, X1(Parachain(200))), Ok((MultiLocation::new(1, X1(Parachain(200))), 1000))), + (ma_1000(2, X1(Parachain(200))), Ok((MultiLocation::new(2, X1(Parachain(200))), 1000))), + ( + ma_1000(1, X2(Parachain(200), GeneralIndex(1234))), + Ok((MultiLocation::new(1, X2(Parachain(200), GeneralIndex(1234))), 1000)), + ), + ( + ma_1000(2, X2(Parachain(200), GeneralIndex(1234))), + Ok((MultiLocation::new(2, X2(Parachain(200), GeneralIndex(1234))), 1000)), + ), + ]; + + for (multi_asset, expected_result) in test_data { + assert_eq!( + >::matches_fungibles( + &multi_asset + ), + expected_result, + "multi_asset: {:?}", + multi_asset + ); + } + } + + // Create MultiAsset + fn ma_1000(parents: u8, interior: Junctions) -> MultiAsset { + (MultiLocation::new(parents, interior), 1000).into() + } } diff --git a/parachains/runtimes/assets/common/src/matching.rs b/parachains/runtimes/assets/common/src/matching.rs new file mode 100644 index 00000000000..05acaff3990 --- /dev/null +++ b/parachains/runtimes/assets/common/src/matching.rs @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cumulus_primitives_core::ParaId; +use frame_support::{ + pallet_prelude::Get, + traits::{Contains, ContainsPair}, +}; +use xcm::{ + latest::prelude::{MultiAsset, MultiLocation}, + prelude::*, +}; + +pub struct StartsWith(sp_std::marker::PhantomData); +impl> Contains for StartsWith { + fn contains(t: &MultiLocation) -> bool { + t.starts_with(&Location::get()) + } +} + +pub struct Equals(sp_std::marker::PhantomData); +impl> Contains for Equals { + fn contains(t: &MultiLocation) -> bool { + t == &Location::get() + } +} + +frame_support::parameter_types! { + pub LocalMultiLocationPattern: MultiLocation = MultiLocation::new(0, Here); + pub ParentLocation: MultiLocation = MultiLocation::parent(); +} + +/// Accepts an asset if it is from the origin. +pub struct IsForeignConcreteAsset(sp_std::marker::PhantomData); +impl> ContainsPair + for IsForeignConcreteAsset +{ + fn contains(asset: &MultiAsset, origin: &MultiLocation) -> bool { + log::trace!(target: "xcm::contains", "IsForeignConcreteAsset asset: {:?}, origin: {:?}", asset, origin); + matches!(asset.id, Concrete(ref id) if IsForeign::contains(id, &origin)) + } +} + +/// Checks if 'a' is from sibling location `b`, so means that `MultiLocation-a' starts with `MultiLocation-b' +pub struct FromSiblingParachain(sp_std::marker::PhantomData); +impl> ContainsPair + for FromSiblingParachain +{ + fn contains(&a: &MultiLocation, b: &MultiLocation) -> bool { + // `a` needs to be from `b` at least + if !a.starts_with(&b) { + return false + } + + // here we check if sibling + match a { + MultiLocation { parents: 1, interior } => match interior.first() { + Some(Parachain(sibling_para_id)) + if sibling_para_id.ne(&u32::from(SelfParaId::get())) => + true, + _ => false, + }, + _ => false, + } + } +} diff --git a/parachains/runtimes/assets/statemine/Cargo.toml b/parachains/runtimes/assets/statemine/Cargo.toml index 0f0921976a8..bdcd3b550e9 100644 --- a/parachains/runtimes/assets/statemine/Cargo.toml +++ b/parachains/runtimes/assets/statemine/Cargo.toml @@ -108,6 +108,7 @@ runtime-benchmarks = [ "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", + "assets-common/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index de7ec75e88c..4f957ab7e0d 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -65,19 +65,22 @@ use parachains_common::{ NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use xcm_config::{ - ForeignCreators, KsmLocation, MultiLocationForAssetId, TrustBackedAssetsConvertedConcreteId, - XcmConfig, + ForeignAssetsConvertedConcreteId, KsmLocation, TrustBackedAssetsConvertedConcreteId, XcmConfig, }; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; // Polkadot imports +use assets_common::{ + foreign_creators::ForeignCreators, matching::FromSiblingParachain, MultiLocationForAssetId, +}; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::latest::BodyId; use xcm_executor::XcmExecutor; +use crate::xcm_config::ForeignCreatorsSovereignAccountOf; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -261,29 +264,43 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +parameter_types! { + // we just reuse the same deposits + pub const ForeignAssetsAssetDeposit: Balance = AssetDeposit::get(); + pub const ForeignAssetsAssetAccountDeposit: Balance = AssetAccountDeposit::get(); + pub const ForeignAssetsApprovalDeposit: Balance = ApprovalDeposit::get(); + pub const ForeignAssetsAssetsStringLimit: u32 = AssetsStringLimit::get(); + pub const ForeignAssetsMetadataDepositBase: Balance = MetadataDepositBase::get(); + pub const ForeignAssetsMetadataDepositPerByte: Balance = MetadataDepositPerByte::get(); +} + /// Assets managed by some foreign location. Note: we do not declare a `ForeignAssetsCall` type, as /// this type is used in proxy definitions. We assume that a foreign location would not want to set /// an individual, local account as a proxy for the issuance of their assets. This issuance should /// be managed by the foreign location's governance. -type ForeignAssetsInstance = pallet_assets::Instance2; +pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type AssetId = MultiLocationForAssetId; type AssetIdParameter = MultiLocationForAssetId; type Currency = Balances; - type CreateOrigin = ForeignCreators; + type CreateOrigin = ForeignCreators< + (FromSiblingParachain>,), + ForeignCreatorsSovereignAccountOf, + AccountId, + >; type ForceOrigin = AssetsForceOrigin; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; + type AssetDeposit = ForeignAssetsAssetDeposit; + type MetadataDepositBase = ForeignAssetsMetadataDepositBase; + type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; + type ApprovalDeposit = ForeignAssetsApprovalDeposit; + type StringLimit = ForeignAssetsAssetsStringLimit; type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets::WeightInfo; type CallbackHandle = (); - type AssetAccountDeposit = AssetAccountDeposit; + type AssetAccountDeposit = ForeignAssetsAssetAccountDeposit; type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; @@ -361,6 +378,7 @@ impl Default for ProxyType { Self::Any } } + impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { @@ -884,11 +902,17 @@ impl_runtime_apis! { }, // collect pallet_assets (TrustBackedAssets) convert::<_, _, _, _, TrustBackedAssetsConvertedConcreteId>( - Assets::account_balances(account) + Assets::account_balances(account.clone()) + .iter() + .filter(|(_, balance)| balance > &0) + )?, + // collect pallet_assets (ForeignAssets) + convert::<_, _, _, _, ForeignAssetsConvertedConcreteId>( + ForeignAssets::account_balances(account) .iter() .filter(|(_, balance)| balance > &0) )?, - // collect ... e.g. pallet_assets ForeignAssets + // collect ... e.g. other tokens ].concat()) } } diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index f3b5405cc40..3476da44a47 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -14,39 +14,34 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; +use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset, StartsWith}; use frame_support::{ match_types, parameter_types, - traits::{ - ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, - PalletInfoAccess, - }, + traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, }; -use pallet_xcm::{EnsureXcm, XcmPassthrough}; +use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, DenyReserveTransferToRelayChain, DenyThenTry, }, }; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use polkadot_parachain::primitives::Sibling; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, }; -use xcm_executor::{ - traits::{Convert, WithOriginFilter}, - XcmExecutor, -}; +use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const KsmLocation: MultiLocation = MultiLocation::parent(); @@ -54,7 +49,6 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); - pub const Local: MultiLocation = Here.into_location(); pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -86,7 +80,7 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; -/// `AssetId/Balancer` converter for `TrustBackedAssets` +/// `AssetId/Balance` converter for `TrustBackedAssets` pub type TrustBackedAssetsConvertedConcreteId = assets_common::TrustBackedAssetsConvertedConcreteId; @@ -106,8 +100,31 @@ pub type FungiblesTransactor = FungiblesAdapter< // The account to use for tracking teleports. CheckingAccount, >; + +/// `AssetId/Balance` converter for `TrustBackedAssets` +pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConvertedConcreteId< + StartsWith, + Balance, +>; + +/// Means for transacting foreign assets from different global consensus. +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + ForeignAssets, + // Use this currency when it is a fungible asset matching the given location or name: + ForeignAssetsConvertedConcreteId, + // 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, + // TODO:check-parameter - no teleports + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + /// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor, ForeignFungiblesTransactor); /// 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 @@ -216,6 +233,34 @@ impl Contains for SafeCallFilter { pallet_assets::Call::touch { .. } | pallet_assets::Call::refund { .. }, ) | + RuntimeCall::ForeignAssets( + /* avoided: mint, burn */ + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::set_metadata { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. }, + ) | RuntimeCall::Uniques( pallet_uniques::Call::create { .. } | pallet_uniques::Call::force_create { .. } | @@ -287,7 +332,13 @@ impl xcm_executor::Config for XcmConfig { // Statemine acting _as_ a reserve location for KSM and assets created under `pallet-assets`. // For KSM, users must use teleport where allowed (e.g. with the Relay Chain). type IsReserve = (); - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of KSM + // We allow: + // - teleportation of KSM + // - teleportation of sibling parachain's assets (as ForeignCreators) + type IsTeleporter = ( + NativeAsset, + IsForeignConcreteAsset>>, + ); type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = WeightInfoBounds< @@ -380,37 +431,12 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type MultiLocationForAssetId = MultiLocation; - -pub type SovereignAccountOf = ( - SiblingParachainConvertsVia, +pub type ForeignCreatorsSovereignAccountOf = ( + SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, ); -// `EnsureOriginWithArg` impl for `CreateOrigin` that allows only XCM origins that are locations -// containing the class location. -pub struct ForeignCreators; -impl EnsureOriginWithArg for ForeignCreators { - type Success = AccountId; - - fn try_origin( - o: RuntimeOrigin, - a: &MultiLocation, - ) -> sp_std::result::Result { - let origin_location = EnsureXcm::::try_origin(o.clone())?; - if !a.starts_with(&origin_location) { - return Err(o) - } - SovereignAccountOf::convert(origin_location).map_err(|_| o) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(a: &MultiLocation) -> Result { - Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) - } -} - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index b696e4ef1e3..7d19a88618c 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -1,25 +1,27 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; -use codec::Encode; +use codec::{Decode, Encode}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ - assert_noop, assert_ok, sp_io, + assert_noop, assert_ok, + traits::fungibles::InspectEnumerable, weights::{Weight, WeightToFee as WeightToFeeT}, }; -use parachains_common::{AccountId, AuraId, Balance}; +use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use statemine_runtime::xcm_config::{ AssetFeeAsExistentialDepositMultiplierFeeCharger, KsmLocation, TrustBackedAssetsPalletLocation, }; pub use statemine_runtime::{ - constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit, - ReservedDmpWeight, Runtime, SessionKeys, System, + constants::fee::WeightToFee, + xcm_config::{ForeignCreatorsSovereignAccountOf, XcmConfig}, + AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, + MetadataDepositBase, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, System, + TrustBackedAssetsInstance, }; use xcm::latest::prelude::*; -use xcm_executor::{ - traits::{Convert, WeightTrader}, - XcmExecutor, -}; +use xcm_executor::traits::{Convert, Identity, JustTry, WeightTrader}; -pub const ALICE: [u8; 32] = [1u8; 32]; +const ALICE: [u8; 32] = [1u8; 32]; +const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; @@ -371,9 +373,15 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; + let foreign_asset_id_multilocation = + MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); + assert_eq!( + ForeignAssets::balance(foreign_asset_id_multilocation, AccountId::from(ALICE)), + 0 + ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); assert!(Runtime::query_account_balances(AccountId::from(ALICE)).unwrap().is_empty()); @@ -400,15 +408,37 @@ fn test_assets_balances_api_works() { minimum_asset_balance )); + // create foreign asset + let foreign_asset_minimum_asset_balance = 3333333_u128; + assert_ok!(ForeignAssets::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_multilocation.clone().into(), + AccountId::from(SOME_ASSET_ADMIN).into(), + false, + foreign_asset_minimum_asset_balance + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(ForeignAssets::mint( + RuntimeHelper::::origin_of(AccountId::from(SOME_ASSET_ADMIN)), + foreign_asset_id_multilocation.clone().into(), + AccountId::from(ALICE).into(), + 6 * foreign_asset_minimum_asset_balance + )); + // check after assert_eq!( Assets::balance(local_asset_id, AccountId::from(ALICE)), minimum_asset_balance ); + assert_eq!( + ForeignAssets::balance(foreign_asset_id_multilocation, AccountId::from(ALICE)), + 6 * minimum_asset_balance + ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); let result = Runtime::query_account_balances(AccountId::from(ALICE)).unwrap(); - assert_eq!(result.len(), 2); + assert_eq!(result.len(), 3); // check currency assert!(result.iter().any(|asset| asset.eq( @@ -423,53 +453,131 @@ fn test_assets_balances_api_works() { minimum_asset_balance ) .into()))); + // check foreign asset + assert!(result.iter().any(|asset| asset.eq(&( + Identity::reverse_ref(foreign_asset_id_multilocation).unwrap(), + 6 * foreign_asset_minimum_asset_balance + ) + .into()))); }); } -#[test] -fn receive_teleported_asset_works() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - let xcm = Xcm(vec![ - ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }])), - ClearOrigin, - BuyExecution { - fees: MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }, - weight_limit: Limited(Weight::from_parts(303531000, 65536)), - }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: None, - id: [ - 18, 153, 85, 112, 1, 245, 88, 21, 211, 252, 181, 60, 116, 70, 58, - 203, 12, 246, 209, 77, 70, 57, 179, 64, 152, 44, 96, 135, 127, 56, - 70, 9, - ], - }), - }, - }, - ]); - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - - let weight_limit = ReservedDmpWeight::get(); - - let outcome = XcmExecutor::::execute_xcm(Parent, xcm, hash, weight_limit); - assert_eq!(outcome.ensure_complete(), Ok(())); - }) -} +asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ) +); + +asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( + Runtime, + XcmConfig, + WeightToFee, + ForeignCreatorsSovereignAccountOf, + ForeignAssetsInstance, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get() +); + +asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_works!( + asset_transactor_transfer_with_trust_backed_assets_works, + Runtime, + XcmConfig, + TrustBackedAssetsInstance, + AssetIdForTrustBackedAssets, + AssetIdForTrustBackedAssetsConvert, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + 12345, + Box::new(|| { + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_works!( + asset_transactor_transfer_with_foreign_assets_works, + Runtime, + XcmConfig, + ForeignAssetsInstance, + MultiLocation, + JustTry, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works!( + Runtime, + XcmConfig, + WeightToFee, + ForeignCreatorsSovereignAccountOf, + ForeignAssetsInstance, + MultiLocation, + JustTry, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + AssetDeposit::get(), + MetadataDepositBase::get(), + Box::new(|pallet_asset_call| RuntimeCall::ForeignAssets(pallet_asset_call).encode()), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ForeignAssets(pallet_asset_event)) => Some(pallet_asset_event), + _ => None, + } + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert_eq!(ForeignAssets::asset_ids().collect::>().len(), 1); + }) +); diff --git a/parachains/runtimes/assets/statemint/Cargo.toml b/parachains/runtimes/assets/statemint/Cargo.toml index f6d54cbd85f..02407e23d5c 100644 --- a/parachains/runtimes/assets/statemint/Cargo.toml +++ b/parachains/runtimes/assets/statemint/Cargo.toml @@ -100,6 +100,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "assets-common/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/parachains/runtimes/assets/statemint/src/xcm_config.rs b/parachains/runtimes/assets/statemint/src/xcm_config.rs index 0bb2c5ff5a7..11ef984483d 100644 --- a/parachains/runtimes/assets/statemint/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemint/src/xcm_config.rs @@ -48,7 +48,6 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); - pub const Local: MultiLocation = MultiLocation::here(); pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -80,7 +79,7 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; -/// `AssetId/Balancer` converter for `TrustBackedAssets`` +/// `AssetId/Balance` converter for `TrustBackedAssets`` pub type TrustBackedAssetsConvertedConcreteId = assets_common::TrustBackedAssetsConvertedConcreteId; diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index 81aa458b476..2e070dc1aec 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -1,25 +1,24 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; -use codec::Encode; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ - assert_noop, assert_ok, sp_io, + assert_noop, assert_ok, + traits::fungibles::InspectEnumerable, weights::{Weight, WeightToFee as WeightToFeeT}, }; -use parachains_common::{AccountId, Balance, StatemintAuraId as AuraId}; +use parachains_common::{ + AccountId, AssetIdForTrustBackedAssets, Balance, StatemintAuraId as AuraId, +}; use statemint_runtime::xcm_config::{ AssetFeeAsExistentialDepositMultiplierFeeCharger, DotLocation, TrustBackedAssetsPalletLocation, }; pub use statemint_runtime::{ - constants::fee::WeightToFee, xcm_config::XcmConfig, Assets, Balances, ExistentialDeposit, - ReservedDmpWeight, Runtime, SessionKeys, System, + constants::fee::WeightToFee, xcm_config::XcmConfig, AssetDeposit, Assets, Balances, + ExistentialDeposit, Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; use xcm::latest::prelude::*; -use xcm_executor::{ - traits::{Convert, WeightTrader}, - XcmExecutor, -}; +use xcm_executor::traits::{Convert, WeightTrader}; -pub const ALICE: [u8; 32] = [1u8; 32]; +const ALICE: [u8; 32] = [1u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; @@ -438,50 +437,47 @@ fn test_assets_balances_api_works() { }); } -#[test] -fn receive_teleported_asset_works() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - let xcm = Xcm(vec![ - ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }])), - ClearOrigin, - BuyExecution { - fees: MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }, - weight_limit: Limited(Weight::from_parts(303531000, 65536)), - }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: None, - id: [ - 18, 153, 85, 112, 1, 245, 88, 21, 211, 252, 181, 60, 116, 70, 58, - 203, 12, 246, 209, 77, 70, 57, 179, 64, 152, 44, 96, 135, 127, 56, - 70, 9, - ], - }), - }, - }, - ]); - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - - let weight_limit = ReservedDmpWeight::get(); - - let outcome = XcmExecutor::::execute_xcm(Parent, xcm, hash, weight_limit); - assert_eq!(outcome.ensure_complete(), Ok(())); - }) -} +asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } + ) +); + +asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_works!( + asset_transactor_transfer_with_pallet_assets_instance_works, + Runtime, + XcmConfig, + TrustBackedAssetsInstance, + AssetIdForTrustBackedAssets, + AssetIdForTrustBackedAssetsConvert, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + 12345, + Box::new(|| {}), + Box::new(|| {}) +); diff --git a/parachains/runtimes/assets/test-utils/Cargo.toml b/parachains/runtimes/assets/test-utils/Cargo.toml index 52ce1d2d9a8..c80bb7f67af 100644 --- a/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/parachains/runtimes/assets/test-utils/Cargo.toml @@ -6,11 +6,12 @@ edition = "2021" description = "Statemint parachain runtime" [dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } # Substrate - frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +pallet-assets = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } @@ -20,8 +21,14 @@ sp-std = { git = "https://github.com/paritytech/substrate", default-features = f sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } # Cumulus +cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachains-common = { path = "../../../common", default-features = false } +assets-common = { path = "../common", default-features = false } + +# Polkadot +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } [dev-dependencies] hex-literal = "0.3.4" @@ -34,12 +41,17 @@ default = [ "std" ] std = [ "frame-support/std", "frame-system/std", + "pallet-assets/std", "pallet-balances/std", + "cumulus-pallet-parachain-system/std", "pallet-collator-selection/std", "pallet-session/std", + "assets-common/std", "parachains-common/std", "sp-consensus-aura/std", "sp-io/std", "sp-runtime/std", "sp-std/std", + "xcm/std", + "xcm-executor/std", ] diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index fb4750bae9e..94435365bd0 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -1,11 +1,19 @@ use frame_support::traits::GenesisBuild; use sp_std::marker::PhantomData; -use frame_support::traits::OriginTrait; +use frame_support::{traits::OriginTrait, weights::Weight}; use parachains_common::AccountId; use sp_consensus_aura::AURA_ENGINE_ID; use sp_core::Encode; use sp_runtime::{Digest, DigestItem}; +use xcm::{ + latest::{MultiAsset, MultiLocation, XcmContext}, + prelude::{Concrete, Fungible, XcmError}, +}; +use xcm_executor::{traits::TransactAsset, Assets}; + +pub mod test_cases; +pub use test_cases::CollatorSessionKeys; pub type BalanceOf = ::Balance; pub type AccountIdOf = ::AccountId; @@ -56,6 +64,11 @@ impl Self { + frame_support::sp_tracing::try_init_simple(); + self + } + pub fn build(self) -> sp_io::TestExternalities where Runtime: @@ -132,3 +145,49 @@ where ::RuntimeOrigin::signed(account_id.into()) } } + +impl RuntimeHelper { + pub fn do_transfer( + from: MultiLocation, + to: MultiLocation, + (asset, amount): (MultiLocation, u128), + ) -> Result { + ::transfer_asset( + &MultiAsset { id: Concrete(asset), fun: Fungible(amount) }, + &from, + &to, + // We aren't able to track the XCM that initiated the fee deposit, so we create a + // fake message hash here + &XcmContext::with_message_hash([0; 32]), + ) + } +} + +pub enum XcmReceivedFrom { + Parent, + Sibling, +} + +impl RuntimeHelper { + pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight { + use frame_support::traits::Get; + match from { + XcmReceivedFrom::Parent => ParachainSystem::ReservedDmpWeight::get(), + XcmReceivedFrom::Sibling => ParachainSystem::ReservedXcmpWeight::get(), + } + } +} + +pub fn assert_metadata( + asset_id: &Assets::AssetId, + expected_name: &str, + expected_symbol: &str, + expected_decimals: u8, +) where + Assets: frame_support::traits::tokens::fungibles::InspectMetadata + + frame_support::traits::tokens::fungibles::Inspect, +{ + assert_eq!(Assets::name(asset_id), Vec::from(expected_name),); + assert_eq!(Assets::symbol(asset_id), Vec::from(expected_symbol),); + assert_eq!(Assets::decimals(asset_id), expected_decimals); +} diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs new file mode 100644 index 00000000000..c6f2f5f8e91 --- /dev/null +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -0,0 +1,1016 @@ +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Module contains predefined test-case scenarios for `Runtime` with various assets. + +use crate::{ + assert_metadata, AccountIdOf, BalanceOf, ExtBuilder, RuntimeHelper, SessionKeysOf, + ValidatorIdOf, XcmReceivedFrom, +}; +use codec::Encode; +use frame_support::{ + assert_noop, assert_ok, + traits::{fungibles::InspectEnumerable, OriginTrait}, + weights::Weight, +}; +use parachains_common::Balance; +use sp_runtime::{ + traits::{StaticLookup, Zero}, + DispatchError, +}; +use xcm::latest::prelude::*; +use xcm_executor::{traits::Convert, XcmExecutor}; + +pub struct CollatorSessionKeys< + Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config, +> { + collator: AccountIdOf, + validator: ValidatorIdOf, + key: SessionKeysOf, +} + +impl + CollatorSessionKeys +{ + pub fn new( + collator: AccountIdOf, + validator: ValidatorIdOf, + key: SessionKeysOf, + ) -> Self { + Self { collator, validator, key } + } + pub fn collators(&self) -> Vec> { + vec![self.collator.clone()] + } + + pub fn session_keys( + &self, + ) -> Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)> { + vec![(self.collator.clone(), self.validator.clone(), self.key.clone())] + } +} + +/// Test-case makes sure that `Runtime` can receive teleported native assets from relay chain +pub fn receive_teleported_asset_for_native_asset_works( + collator_session_keys: CollatorSessionKeys, + target_account: AccountIdOf, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + XcmConfig: xcm_executor::Config, +{ + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .build() + .execute_with(|| { + // check Balances before + assert_eq!(>::free_balance(&target_account), 0.into()); + + let native_asset_id = MultiLocation::parent(); + + let xcm = Xcm(vec![ + ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(native_asset_id), + fun: Fungible(10000000000000), + }])), + ClearOrigin, + BuyExecution { + fees: MultiAsset { + id: Concrete(native_asset_id), + fun: Fungible(10000000000000), + }, + weight_limit: Limited(Weight::from_parts(303531000, 65536)), + }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: target_account.clone().into(), + }), + }, + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + let outcome = XcmExecutor::::execute_xcm( + Parent, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Parent), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + // check Balances after + assert_ne!(>::free_balance(&target_account), 0.into()); + }) +} + +#[macro_export] +macro_rules! include_receive_teleported_asset_for_native_asset_works( + ( + $runtime:path, + $xcm_config:path, + $collator_session_key:expr + ) => { + #[test] + fn receive_teleported_asset_for_native_asset_works() { + const BOB: [u8; 32] = [2u8; 32]; + let target_account = parachains_common::AccountId::from(BOB); + + asset_test_utils::test_cases::receive_teleported_asset_for_native_asset_works::< + $runtime, + $xcm_config + >($collator_session_key, target_account) + } + } +); + +/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain relay chain +pub fn receive_teleported_asset_from_foreign_creator_works< + Runtime, + XcmConfig, + WeightToFee, + SovereignAccountOf, + ForeignAssetsPalletInstance, +>( + collator_session_keys: CollatorSessionKeys, + target_account: AccountIdOf, + existential_deposit: BalanceOf, + asset_owner: AccountIdOf, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_assets::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + XcmConfig: xcm_executor::Config, + WeightToFee: frame_support::weights::WeightToFee, + ::Balance: From + Into, + SovereignAccountOf: Convert>, + >::AssetId: + From + Into, + >::AssetIdParameter: + From + Into, + >::Balance: + From + Into, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + ForeignAssetsPalletInstance: 'static, +{ + // foreign parachain with the same consenus currency as asset + let foreign_asset_id_multilocation = + MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) }; + + // foreign creator, which can be sibling parachain to match ForeignCreators + let foreign_creator = MultiLocation { parents: 1, interior: X1(Parachain(2222)) }; + let foreign_creator_as_account_id = SovereignAccountOf::convert(foreign_creator).expect(""); + + // we want to buy execution with local relay chain currency + let buy_execution_fee_amount = + WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0)); + let buy_execution_fee = MultiAsset { + id: Concrete(MultiLocation::parent()), + fun: Fungible(buy_execution_fee_amount.into()), + }; + + let teleported_foreign_asset_amount = 10000000000000; + + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![ + ( + foreign_creator_as_account_id.clone(), + existential_deposit + (buy_execution_fee_amount * 2).into(), + ), + (target_account.clone(), existential_deposit), + ]) + .with_tracing() + .build() + .execute_with(|| { + // checks before + assert_eq!( + >::free_balance(&target_account), + existential_deposit + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &target_account + ), + 0.into() + ); + + // create foreign asset + let asset_minimum_asset_balance = 3333333_u128; + assert_ok!( + >::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_multilocation.clone().into(), + asset_owner.into(), + false, + asset_minimum_asset_balance.into() + ) + ); + assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance); + + // prepare xcm + let xcm = Xcm(vec![ + // BuyExecution with relaychain native token + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { + fees: MultiAsset { + id: Concrete(MultiLocation::parent()), + fun: Fungible(buy_execution_fee_amount.into()), + }, + weight_limit: Limited(Weight::from_parts(403531000, 1024)), + }, + // Process teleported asset + ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { + id: Concrete(foreign_asset_id_multilocation), + fun: Fungible(teleported_foreign_asset_amount), + }])), + DepositAsset { + assets: Wild(AllOf { + id: Concrete(foreign_asset_id_multilocation), + fun: WildFungibility::Fungible, + }), + beneficiary: MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: target_account.clone().into(), + }), + }, + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + let outcome = XcmExecutor::::execute_xcm( + foreign_creator, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + // checks after + assert_eq!( + >::free_balance(&target_account), + existential_deposit + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &target_account + ), + teleported_foreign_asset_amount.into() + ); + }) +} + +#[macro_export] +macro_rules! include_receive_teleported_asset_from_foreign_creator_works( + ( + $runtime:path, + $xcm_config:path, + $weight_to_fee:path, + $sovereign_account_of:path, + $assets_pallet_instance:path, + $collator_session_key:expr, + $existential_deposit:expr + ) => { + #[test] + fn receive_teleported_asset_from_foreign_creator_works() { + const BOB: [u8; 32] = [2u8; 32]; + let target_account = parachains_common::AccountId::from(BOB); + const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32]; + let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER); + + asset_test_utils::test_cases::receive_teleported_asset_from_foreign_creator_works::< + $runtime, + $xcm_config, + $weight_to_fee, + $sovereign_account_of, + $assets_pallet_instance + >($collator_session_key, target_account, $existential_deposit, asset_owner) + } + } +); + +/// Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain currency +pub fn asset_transactor_transfer_with_local_consensus_currency_works( + collator_session_keys: CollatorSessionKeys, + source_account: AccountIdOf, + target_account: AccountIdOf, + existential_deposit: BalanceOf, + additional_checks_before: Box, + additional_checks_after: Box, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + XcmConfig: xcm_executor::Config, + ::Balance: From + Into, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, +{ + let unit = existential_deposit; + + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![(source_account.clone(), (BalanceOf::::from(10_u128) * unit))]) + .with_tracing() + .build() + .execute_with(|| { + // check Balances before + assert_eq!( + >::free_balance(source_account.clone()), + (BalanceOf::::from(10_u128) * unit) + ); + assert_eq!( + >::free_balance(target_account.clone()), + (BalanceOf::::zero() * unit) + ); + + // additional check before + additional_checks_before(); + + // transfer_asset (deposit/withdraw) ALICE -> BOB + let _ = RuntimeHelper::::do_transfer( + MultiLocation { + parents: 0, + interior: X1(AccountId32 { network: None, id: source_account.clone().into() }), + }, + MultiLocation { + parents: 0, + interior: X1(AccountId32 { network: None, id: target_account.clone().into() }), + }, + // local_consensus_currency_asset, e.g.: relaychain token (KSM, DOT, ...) + ( + MultiLocation { parents: 1, interior: Here }, + (BalanceOf::::from(1_u128) * unit).into(), + ), + ) + .expect("no error"); + + // check Balances after + assert_eq!( + >::free_balance(source_account), + (BalanceOf::::from(9_u128) * unit) + ); + assert_eq!( + >::free_balance(target_account), + (BalanceOf::::from(1_u128) * unit) + ); + + additional_checks_after(); + }) +} + +#[macro_export] +macro_rules! include_asset_transactor_transfer_with_local_consensus_currency_works( + ( + $runtime:path, + $xcm_config:path, + $collator_session_key:expr, + $existential_deposit:expr, + $additional_checks_before:expr, + $additional_checks_after:expr + ) => { + #[test] + fn asset_transactor_transfer_with_local_consensus_currency_works() { + const ALICE: [u8; 32] = [1u8; 32]; + let source_account = parachains_common::AccountId::from(ALICE); + const BOB: [u8; 32] = [2u8; 32]; + let target_account = parachains_common::AccountId::from(BOB); + + asset_test_utils::test_cases::asset_transactor_transfer_with_local_consensus_currency_works::< + $runtime, + $xcm_config + >( + $collator_session_key, + source_account, + target_account, + $existential_deposit, + $additional_checks_before, + $additional_checks_after + ) + } + } +); + +///Test-case makes sure that `Runtime`'s `xcm::AssetTransactor` can handle native relay chain currency +pub fn asset_transactor_transfer_with_pallet_assets_instance_works< + Runtime, + XcmConfig, + AssetsPalletInstance, + AssetId, + AssetIdConverter, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + asset_id: AssetId, + asset_owner: AccountIdOf, + alice_account: AccountIdOf, + bob_account: AccountIdOf, + charlie_account: AccountIdOf, + additional_checks_before: Box, + additional_checks_after: Box, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_assets::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + XcmConfig: xcm_executor::Config, + >::AssetId: + From + Into, + >::AssetIdParameter: + From + Into, + >::Balance: From + Into, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + AssetsPalletInstance: 'static, + AssetId: Clone, + AssetIdConverter: Convert, +{ + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![ + (asset_owner.clone(), existential_deposit), + (alice_account.clone(), existential_deposit), + (bob_account.clone(), existential_deposit), + ]) + .with_tracing() + .build() + .execute_with(|| { + // create some asset class + let asset_minimum_asset_balance = 3333333_u128; + let asset_id_as_multilocation = AssetIdConverter::reverse_ref(&asset_id).unwrap(); + assert_ok!(>::force_create( + RuntimeHelper::::root_origin(), + asset_id.clone().into(), + asset_owner.clone().into(), + false, + asset_minimum_asset_balance.into() + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(>::mint( + RuntimeHelper::::origin_of(asset_owner.clone()), + asset_id.clone().into(), + alice_account.clone().into(), + (6 * asset_minimum_asset_balance).into() + )); + + // check Assets before + assert_eq!( + >::balance( + asset_id.clone().into(), + &alice_account + ), + (6 * asset_minimum_asset_balance).into() + ); + assert_eq!( + >::balance( + asset_id.clone().into(), + &bob_account + ), + 0.into() + ); + assert_eq!( + >::balance( + asset_id.clone().into(), + &charlie_account + ), + 0.into() + ); + assert_eq!( + >::balance( + asset_id.clone().into(), + &asset_owner + ), + 0.into() + ); + assert_eq!( + >::free_balance(&alice_account), + existential_deposit + ); + assert_eq!( + >::free_balance(&bob_account), + existential_deposit + ); + assert_eq!( + >::free_balance(&charlie_account), + 0.into() + ); + assert_eq!( + >::free_balance(&asset_owner), + existential_deposit + ); + additional_checks_before(); + + // transfer_asset (deposit/withdraw) ALICE -> CHARLIE (not ok - Charlie does not have ExistentialDeposit) + assert_noop!( + RuntimeHelper::::do_transfer( + MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: alice_account.clone().into() + }), + }, + MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: charlie_account.clone().into() + }), + }, + (asset_id_as_multilocation, 1 * asset_minimum_asset_balance), + ), + XcmError::FailedToTransactAsset(Into::<&str>::into( + sp_runtime::TokenError::CannotCreate + )) + ); + + // transfer_asset (deposit/withdraw) ALICE -> BOB (ok - has ExistentialDeposit) + assert!(matches!( + RuntimeHelper::::do_transfer( + MultiLocation { + parents: 0, + interior: X1(AccountId32 { + network: None, + id: alice_account.clone().into() + }), + }, + MultiLocation { + parents: 0, + interior: X1(AccountId32 { network: None, id: bob_account.clone().into() }), + }, + (asset_id_as_multilocation, 1 * asset_minimum_asset_balance), + ), + Ok(_) + )); + + // check Assets after + assert_eq!( + >::balance( + asset_id.clone().into(), + &alice_account + ), + (5 * asset_minimum_asset_balance).into() + ); + assert_eq!( + >::balance( + asset_id.clone().into(), + &bob_account + ), + (1 * asset_minimum_asset_balance).into() + ); + assert_eq!( + >::balance( + asset_id.clone().into(), + &charlie_account + ), + 0.into() + ); + assert_eq!( + >::balance( + asset_id.into(), + &asset_owner + ), + 0.into() + ); + assert_eq!( + >::free_balance(&alice_account), + existential_deposit + ); + assert_eq!( + >::free_balance(&bob_account), + existential_deposit + ); + assert_eq!( + >::free_balance(&charlie_account), + 0.into() + ); + assert_eq!( + >::free_balance(&asset_owner), + existential_deposit + ); + + additional_checks_after(); + }) +} + +#[macro_export] +macro_rules! include_asset_transactor_transfer_with_pallet_assets_instance_works( + ( + $test_name:tt, + $runtime:path, + $xcm_config:path, + $assets_pallet_instance:path, + $asset_id:path, + $asset_id_converter:path, + $collator_session_key:expr, + $existential_deposit:expr, + $tested_asset_id:expr, + $additional_checks_before:expr, + $additional_checks_after:expr + ) => { + #[test] + fn $test_name() { + const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32]; + let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER); + const ALICE: [u8; 32] = [1u8; 32]; + let alice_account = parachains_common::AccountId::from(ALICE); + const BOB: [u8; 32] = [2u8; 32]; + let bob_account = parachains_common::AccountId::from(BOB); + const CHARLIE: [u8; 32] = [3u8; 32]; + let charlie_account = parachains_common::AccountId::from(CHARLIE); + + asset_test_utils::test_cases::asset_transactor_transfer_with_pallet_assets_instance_works::< + $runtime, + $xcm_config, + $assets_pallet_instance, + $asset_id, + $asset_id_converter + >( + $collator_session_key, + $existential_deposit, + $tested_asset_id, + asset_owner, + alice_account, + bob_account, + charlie_account, + $additional_checks_before, + $additional_checks_after + ) + } + } +); + +pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works< + Runtime, + XcmConfig, + WeightToFee, + SovereignAccountOf, + ForeignAssetsPalletInstance, + AssetId, + AssetIdConverter, +>( + collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, + asset_deposit: BalanceOf, + metadata_deposit_base: BalanceOf, + alice_account: AccountIdOf, + bob_account: AccountIdOf, + runtime_call_encode: Box< + dyn Fn(pallet_assets::Call) -> Vec, + >, + unwrap_pallet_assets_event: Box< + dyn Fn(Vec) -> Option>, + >, + additional_checks_before: Box, + additional_checks_after: Box, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + pallet_assets::Config, + AccountIdOf: Into<[u8; 32]>, + ValidatorIdOf: From>, + BalanceOf: From, + XcmConfig: xcm_executor::Config, + WeightToFee: frame_support::weights::WeightToFee, + ::Balance: From + Into, + SovereignAccountOf: Convert>, + >::AssetId: + From + Into, + >::AssetIdParameter: + From + Into, + >::Balance: + From + Into, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + ForeignAssetsPalletInstance: 'static, + AssetId: Clone, + AssetIdConverter: Convert, +{ + // foreign parachain with the same consenus currency as asset + let foreign_asset_id_multilocation = + MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) }; + let asset_id = AssetIdConverter::convert(foreign_asset_id_multilocation).unwrap(); + + // foreign creator, which can be sibling parachain to match ForeignCreators + let foreign_creator = MultiLocation { parents: 1, interior: X1(Parachain(2222)) }; + let foreign_creator_as_account_id = SovereignAccountOf::convert(foreign_creator).expect(""); + + // we want to buy execution with local relay chain currency + let buy_execution_fee_amount = + WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0)); + let buy_execution_fee = MultiAsset { + id: Concrete(MultiLocation::parent()), + fun: Fungible(buy_execution_fee_amount), + }; + + ExtBuilder::::default() + .with_collators(collator_session_keys.collators()) + .with_session_keys(collator_session_keys.session_keys()) + .with_balances(vec![( + foreign_creator_as_account_id.clone(), + existential_deposit + + asset_deposit + metadata_deposit_base + + buy_execution_fee_amount.into() + + buy_execution_fee_amount.into(), + )]) + .with_tracing() + .build() + .execute_with(|| { + assert!(>::asset_ids() + .collect::>() + .is_empty()); + assert_eq!( + >::free_balance(&foreign_creator_as_account_id), + existential_deposit + + asset_deposit + metadata_deposit_base + + buy_execution_fee_amount.into() + + buy_execution_fee_amount.into() + ); + additional_checks_before(); + + // execute XCM with Transacts to create/manage foreign assets by foreign governance + // prepapre data for xcm::Transact(create) + let foreign_asset_create = runtime_call_encode(pallet_assets::Call::< + Runtime, + ForeignAssetsPalletInstance, + >::create { + id: asset_id.clone().into(), + // admin as sovereign_account + admin: foreign_creator_as_account_id.clone().into(), + min_balance: 1.into(), + }); + // prepapre data for xcm::Transact(set_metadata) + let foreign_asset_set_metadata = runtime_call_encode(pallet_assets::Call::< + Runtime, + ForeignAssetsPalletInstance, + >::set_metadata { + id: asset_id.clone().into(), + name: Vec::from("My super coin"), + symbol: Vec::from("MY_S_COIN"), + decimals: 12, + }); + // prepapre data for xcm::Transact(set_team - change just freezer to Bob) + let foreign_asset_set_team = runtime_call_encode(pallet_assets::Call::< + Runtime, + ForeignAssetsPalletInstance, + >::set_team { + id: asset_id.clone().into(), + issuer: foreign_creator_as_account_id.clone().into(), + admin: foreign_creator_as_account_id.clone().into(), + freezer: bob_account.clone().into(), + }); + + // lets simulate this was triggered by relay chain from local consensus sibling parachain + let xcm = Xcm(vec![ + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { fees: buy_execution_fee.clone().into(), weight_limit: Unlimited }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(40_000_000_000, 6000), + call: foreign_asset_create.into(), + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(20_000_000_000, 6000), + call: foreign_asset_set_metadata.into(), + }, + Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::from_parts(20_000_000_000, 6000), + call: foreign_asset_set_team.into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + // messages with different consensus should go through the local bridge-hub + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + // execute xcm as XcmpQueue would do + let outcome = XcmExecutor::::execute_xcm( + foreign_creator, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + // check events + let mut events = >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_assets_event(e.event.encode())); + assert!(events.any(|e| matches!(e, pallet_assets::Event::Created { .. }))); + assert!(events.any(|e| matches!(e, pallet_assets::Event::MetadataSet { .. }))); + assert!(events.any(|e| matches!(e, pallet_assets::Event::TeamChanged { .. }))); + + // check assets after + assert!(!>::asset_ids() + .collect::>() + .is_empty()); + + // check update metadata + use frame_support::traits::tokens::fungibles::roles::Inspect as InspectRoles; + assert_eq!( + >::owner( + asset_id.clone().into() + ), + Some(foreign_creator_as_account_id.clone()) + ); + assert_eq!( + >::admin( + asset_id.clone().into() + ), + Some(foreign_creator_as_account_id.clone()) + ); + assert_eq!( + >::issuer( + asset_id.clone().into() + ), + Some(foreign_creator_as_account_id.clone()) + ); + assert_eq!( + >::freezer( + asset_id.clone().into() + ), + Some(bob_account.clone()) + ); + assert!( + >::free_balance(&foreign_creator_as_account_id) < + existential_deposit + + buy_execution_fee_amount.into() + + buy_execution_fee_amount.into() + ); + assert_metadata::< + pallet_assets::Pallet, + AccountIdOf, + >(&asset_id.clone().into(), "My super coin", "MY_S_COIN", 12); + + // check if changed freezer, can freeze + assert_noop!( + >::freeze( + RuntimeHelper::::origin_of(bob_account), + asset_id.clone().into(), + alice_account.clone().into() + ), + pallet_assets::Error::::NoAccount + ); + assert_noop!( + >::freeze( + RuntimeHelper::::origin_of(foreign_creator_as_account_id.clone()), + asset_id.clone().into(), + alice_account.into() + ), + pallet_assets::Error::::NoPermission + ); + + // lets try create asset for different parachain(3333) (foreign_creator(2222) can create just his assets) + let foreign_asset_id_multilocation = + MultiLocation { parents: 1, interior: X2(Parachain(3333), GeneralIndex(1234567)) }; + let asset_id = AssetIdConverter::convert(foreign_asset_id_multilocation).unwrap(); + + // prepare data for xcm::Transact(create) + let foreign_asset_create = runtime_call_encode(pallet_assets::Call::< + Runtime, + ForeignAssetsPalletInstance, + >::create { + id: asset_id.clone().into(), + // admin as sovereign_account + admin: foreign_creator_as_account_id.clone().into(), + min_balance: 1.into(), + }); + let xcm = Xcm(vec![ + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { fees: buy_execution_fee.clone().into(), weight_limit: Unlimited }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: Weight::from_parts(20_000_000_000, 6000), + call: foreign_asset_create.into(), + }, + ExpectTransactStatus(MaybeErrorCode::from(DispatchError::BadOrigin.encode())), + ]); + + // messages with different consensus should go through the local bridge-hub + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + + // execute xcm as XcmpQueue would do + let outcome = XcmExecutor::::execute_xcm( + foreign_creator, + xcm, + hash, + RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + ); + assert_eq!(outcome.ensure_complete(), Ok(())); + + additional_checks_after(); + }) +} + +#[macro_export] +macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works( + ( + $runtime:path, + $xcm_config:path, + $weight_to_fee:path, + $sovereign_account_of:path, + $assets_pallet_instance:path, + $asset_id:path, + $asset_id_converter:path, + $collator_session_key:expr, + $existential_deposit:expr, + $asset_deposit:expr, + $metadata_deposit_base:expr, + $runtime_call_encode:expr, + $unwrap_pallet_assets_event:expr, + $additional_checks_before:expr, + $additional_checks_after:expr + ) => { + #[test] + fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works() { + const ALICE: [u8; 32] = [1u8; 32]; + let alice_account = parachains_common::AccountId::from(ALICE); + const BOB: [u8; 32] = [2u8; 32]; + let bob_account = parachains_common::AccountId::from(BOB); + + asset_test_utils::test_cases::create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works::< + $runtime, + $xcm_config, + $weight_to_fee, + $sovereign_account_of, + $assets_pallet_instance, + $asset_id, + $asset_id_converter + >( + $collator_session_key, + $existential_deposit, + $asset_deposit, + $metadata_deposit_base, + alice_account, + bob_account, + $runtime_call_encode, + $unwrap_pallet_assets_event, + $additional_checks_before, + $additional_checks_after + ) + } + } +); diff --git a/parachains/runtimes/assets/westmint/Cargo.toml b/parachains/runtimes/assets/westmint/Cargo.toml index 591c7699f8b..1707a17e191 100644 --- a/parachains/runtimes/assets/westmint/Cargo.toml +++ b/parachains/runtimes/assets/westmint/Cargo.toml @@ -103,6 +103,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "assets-common/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/parachains/runtimes/assets/westmint/src/lib.rs b/parachains/runtimes/assets/westmint/src/lib.rs index e3d36588b0c..c9e8143beaf 100644 --- a/parachains/runtimes/assets/westmint/src/lib.rs +++ b/parachains/runtimes/assets/westmint/src/lib.rs @@ -69,17 +69,20 @@ use parachains_common::{ NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use xcm_config::{ - ForeignCreators, MultiLocationForAssetId, TrustBackedAssetsConvertedConcreteId, - WestendLocation, XcmConfig, XcmOriginToTransactDispatchOrigin, + ForeignAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, WestendLocation, + XcmConfig, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -// Polkadot imports +use assets_common::{ + foreign_creators::ForeignCreators, matching::FromSiblingParachain, MultiLocationForAssetId, +}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm_executor::XcmExecutor; +use crate::xcm_config::ForeignCreatorsSovereignAccountOf; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; impl_opaque_keys! { @@ -246,29 +249,43 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +parameter_types! { + // we just reuse the same deposits + pub const ForeignAssetsAssetDeposit: Balance = AssetDeposit::get(); + pub const ForeignAssetsAssetAccountDeposit: Balance = AssetAccountDeposit::get(); + pub const ForeignAssetsApprovalDeposit: Balance = ApprovalDeposit::get(); + pub const ForeignAssetsAssetsStringLimit: u32 = AssetsStringLimit::get(); + pub const ForeignAssetsMetadataDepositBase: Balance = MetadataDepositBase::get(); + pub const ForeignAssetsMetadataDepositPerByte: Balance = MetadataDepositPerByte::get(); +} + /// Assets managed by some foreign location. Note: we do not declare a `ForeignAssetsCall` type, as /// this type is used in proxy definitions. We assume that a foreign location would not want to set /// an individual, local account as a proxy for the issuance of their assets. This issuance should /// be managed by the foreign location's governance. -type ForeignAssetsInstance = pallet_assets::Instance2; +pub type ForeignAssetsInstance = pallet_assets::Instance2; impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type AssetId = MultiLocationForAssetId; type AssetIdParameter = MultiLocationForAssetId; type Currency = Balances; - type CreateOrigin = ForeignCreators; + type CreateOrigin = ForeignCreators< + (FromSiblingParachain>,), + ForeignCreatorsSovereignAccountOf, + AccountId, + >; type ForceOrigin = AssetsForceOrigin; - type AssetDeposit = AssetDeposit; - type MetadataDepositBase = MetadataDepositBase; - type MetadataDepositPerByte = MetadataDepositPerByte; - type ApprovalDeposit = ApprovalDeposit; - type StringLimit = AssetsStringLimit; + type AssetDeposit = ForeignAssetsAssetDeposit; + type MetadataDepositBase = ForeignAssetsMetadataDepositBase; + type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; + type ApprovalDeposit = ForeignAssetsApprovalDeposit; + type StringLimit = ForeignAssetsAssetsStringLimit; type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets::WeightInfo; type CallbackHandle = (); - type AssetAccountDeposit = AssetAccountDeposit; + type AssetAccountDeposit = ForeignAssetsAssetAccountDeposit; type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; @@ -346,6 +363,7 @@ impl Default for ProxyType { Self::Any } } + impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { @@ -942,11 +960,17 @@ impl_runtime_apis! { }, // collect pallet_assets (TrustBackedAssets) convert::<_, _, _, _, TrustBackedAssetsConvertedConcreteId>( - Assets::account_balances(account) + Assets::account_balances(account.clone()) + .iter() + .filter(|(_, balance)| balance > &0) + )?, + // collect pallet_assets (ForeignAssets) + convert::<_, _, _, _, ForeignAssetsConvertedConcreteId>( + ForeignAssets::account_balances(account) .iter() .filter(|(_, balance)| balance > &0) )?, - // collect ... e.g. pallet_assets ForeignAssets + // collect ... e.g. other tokens ].concat()) } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index e604315467b..b6587c7dc97 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -18,35 +18,31 @@ use super::{ ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; +use crate::ForeignAssets; +use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset, StartsWith}; use frame_support::{ match_types, parameter_types, - traits::{ - ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Everything, Nothing, - PalletInfoAccess, - }, + traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, }; -use pallet_xcm::{EnsureXcm, XcmPassthrough}; +use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, DenyReserveTransferToRelayChain, DenyThenTry, }, }; -use polkadot_parachain::primitives::{Id as ParaId, Sibling}; +use polkadot_parachain::primitives::Sibling; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, }; -use xcm_executor::{ - traits::{Convert, WithOriginFilter}, - XcmExecutor, -}; +use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; parameter_types! { pub const WestendLocation: MultiLocation = MultiLocation::parent(); @@ -54,7 +50,6 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); - pub const Local: MultiLocation = Here.into_location(); pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -86,7 +81,7 @@ pub type CurrencyTransactor = CurrencyAdapter< (), >; -/// `AssetId/Balancer` converter for `TrustBackedAssets` +/// `AssetId/Balance` converter for `TrustBackedAssets` pub type TrustBackedAssetsConvertedConcreteId = assets_common::TrustBackedAssetsConvertedConcreteId; @@ -106,8 +101,31 @@ pub type FungiblesTransactor = FungiblesAdapter< // The account to use for tracking teleports. CheckingAccount, >; + +/// `AssetId/Balance` converter for `TrustBackedAssets` +pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConvertedConcreteId< + StartsWith, + Balance, +>; + +/// Means for transacting foreign assets from different global consensus. +pub type ForeignFungiblesTransactor = FungiblesAdapter< + // Use this fungibles implementation: + ForeignAssets, + // Use this currency when it is a fungible asset matching the given location or name: + ForeignAssetsConvertedConcreteId, + // 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, + // TODO:check-parameter - no teleports + NoChecking, + // The account to use for tracking teleports. + CheckingAccount, +>; + /// Means for transacting assets on this chain. -pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor); +pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor, ForeignFungiblesTransactor); /// 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 @@ -182,7 +200,11 @@ impl Contains for SafeCallFilter { RuntimeCall::Session(pallet_session::Call::purge_keys { .. }) | RuntimeCall::XcmpQueue(..) | RuntimeCall::DmpQueue(..) | - RuntimeCall::Utility(pallet_utility::Call::as_derivative { .. }) | + RuntimeCall::Utility( + pallet_utility::Call::as_derivative { .. } | + pallet_utility::Call::batch { .. } | + pallet_utility::Call::batch_all { .. }, + ) | RuntimeCall::Assets( pallet_assets::Call::create { .. } | pallet_assets::Call::force_create { .. } | @@ -211,6 +233,34 @@ impl Contains for SafeCallFilter { pallet_assets::Call::touch { .. } | pallet_assets::Call::refund { .. }, ) | + RuntimeCall::ForeignAssets( + /* avoided: mint, burn */ + pallet_assets::Call::create { .. } | + pallet_assets::Call::force_create { .. } | + pallet_assets::Call::start_destroy { .. } | + pallet_assets::Call::destroy_accounts { .. } | + pallet_assets::Call::destroy_approvals { .. } | + pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::transfer { .. } | + pallet_assets::Call::transfer_keep_alive { .. } | + pallet_assets::Call::force_transfer { .. } | + pallet_assets::Call::freeze { .. } | + pallet_assets::Call::thaw { .. } | + pallet_assets::Call::freeze_asset { .. } | + pallet_assets::Call::thaw_asset { .. } | + pallet_assets::Call::transfer_ownership { .. } | + pallet_assets::Call::set_team { .. } | + pallet_assets::Call::set_metadata { .. } | + pallet_assets::Call::clear_metadata { .. } | + pallet_assets::Call::force_clear_metadata { .. } | + pallet_assets::Call::force_asset_status { .. } | + pallet_assets::Call::approve_transfer { .. } | + pallet_assets::Call::cancel_approval { .. } | + pallet_assets::Call::force_cancel_approval { .. } | + pallet_assets::Call::transfer_approved { .. } | + pallet_assets::Call::touch { .. } | + pallet_assets::Call::refund { .. }, + ) | RuntimeCall::Uniques( pallet_uniques::Call::create { .. } | pallet_uniques::Call::force_create { .. } | @@ -282,7 +332,13 @@ impl xcm_executor::Config for XcmConfig { // Westmint acting _as_ a reserve location for WND and assets created under `pallet-assets`. // For WND, users must use teleport where allowed (e.g. with the Relay Chain). type IsReserve = (); - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation of WND + // We allow: + // - teleportation of WND + // - teleportation of sibling parachain's assets (as ForeignCreators) + type IsTeleporter = ( + NativeAsset, + IsForeignConcreteAsset>>, + ); type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = WeightInfoBounds< @@ -370,37 +426,12 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -pub type MultiLocationForAssetId = MultiLocation; - -pub type SovereignAccountOf = ( - SiblingParachainConvertsVia, +pub type ForeignCreatorsSovereignAccountOf = ( + SiblingParachainConvertsVia, AccountId32Aliases, ParentIsPreset, ); -// `EnsureOriginWithArg` impl for `CreateOrigin` that allows only XCM origins that are locations -// containing the class location. -pub struct ForeignCreators; -impl EnsureOriginWithArg for ForeignCreators { - type Success = AccountId; - - fn try_origin( - o: RuntimeOrigin, - a: &MultiLocation, - ) -> sp_std::result::Result { - let origin_location = EnsureXcm::::try_origin(o.clone())?; - if !a.starts_with(&origin_location) { - return Err(o) - } - SovereignAccountOf::convert(origin_location).map_err(|_| o) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(a: &MultiLocation) -> Result { - Ok(pallet_xcm::Origin::Xcm(a.clone()).into()) - } -} - /// Simple conversion of `u32` into an `AssetId` for use in benchmarking. pub struct XcmBenchmarkHelper; #[cfg(feature = "runtime-benchmarks")] diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index 5d7a6187869..77125a4884c 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -1,27 +1,34 @@ -use asset_test_utils::{ExtBuilder, RuntimeHelper}; -use codec::{DecodeLimit, Encode}; +use asset_test_utils::{ExtBuilder, RuntimeHelper, XcmReceivedFrom}; +use codec::{Decode, DecodeLimit, Encode}; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, sp_io, + traits::fungibles::InspectEnumerable, weights::{Weight, WeightToFee as WeightToFeeT}, }; -use parachains_common::{AccountId, AuraId, Balance}; +use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; +use std::convert::Into; pub use westmint_runtime::{ constants::fee::WeightToFee, xcm_config::{TrustBackedAssetsPalletLocation, XcmConfig}, - Assets, Balances, ExistentialDeposit, ReservedDmpWeight, Runtime, SessionKeys, System, + AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, + Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; use westmint_runtime::{ - xcm_config::{AssetFeeAsExistentialDepositMultiplierFeeCharger, WestendLocation}, - RuntimeCall, + xcm_config::{ + AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, + WestendLocation, + }, + MetadataDepositBase, RuntimeCall, RuntimeEvent, }; use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; use xcm_executor::{ - traits::{Convert, WeightTrader}, + traits::{Convert, Identity, JustTry, WeightTrader}, XcmExecutor, }; -pub const ALICE: [u8; 32] = [1u8; 32]; +const ALICE: [u8; 32] = [1u8; 32]; +const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; type AssetIdForTrustBackedAssetsConvert = assets_common::AssetIdForTrustBackedAssetsConvert; @@ -371,9 +378,15 @@ fn test_assets_balances_api_works() { .build() .execute_with(|| { let local_asset_id = 1; + let foreign_asset_id_multilocation = + MultiLocation { parents: 1, interior: X2(Parachain(1234), GeneralIndex(12345)) }; // check before assert_eq!(Assets::balance(local_asset_id, AccountId::from(ALICE)), 0); + assert_eq!( + ForeignAssets::balance(foreign_asset_id_multilocation, AccountId::from(ALICE)), + 0 + ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), 0); assert!(Runtime::query_account_balances(AccountId::from(ALICE)).unwrap().is_empty()); @@ -400,15 +413,37 @@ fn test_assets_balances_api_works() { minimum_asset_balance )); + // create foreign asset + let foreign_asset_minimum_asset_balance = 3333333_u128; + assert_ok!(ForeignAssets::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_multilocation.clone().into(), + AccountId::from(SOME_ASSET_ADMIN).into(), + false, + foreign_asset_minimum_asset_balance + )); + + // We first mint enough asset for the account to exist for assets + assert_ok!(ForeignAssets::mint( + RuntimeHelper::::origin_of(AccountId::from(SOME_ASSET_ADMIN)), + foreign_asset_id_multilocation.clone().into(), + AccountId::from(ALICE).into(), + 6 * foreign_asset_minimum_asset_balance + )); + // check after assert_eq!( Assets::balance(local_asset_id, AccountId::from(ALICE)), minimum_asset_balance ); + assert_eq!( + ForeignAssets::balance(foreign_asset_id_multilocation, AccountId::from(ALICE)), + 6 * minimum_asset_balance + ); assert_eq!(Balances::free_balance(AccountId::from(ALICE)), some_currency); let result = Runtime::query_account_balances(AccountId::from(ALICE)).unwrap(); - assert_eq!(result.len(), 2); + assert_eq!(result.len(), 3); // check currency assert!(result.iter().any(|asset| asset.eq( @@ -423,56 +458,134 @@ fn test_assets_balances_api_works() { minimum_asset_balance ) .into()))); + // check foreign asset + assert!(result.iter().any(|asset| asset.eq(&( + Identity::reverse_ref(foreign_asset_id_multilocation).unwrap(), + 6 * foreign_asset_minimum_asset_balance + ) + .into()))); }); } -#[test] -fn receive_teleported_asset_works() { - ExtBuilder::::default() - .with_collators(vec![AccountId::from(ALICE)]) - .with_session_keys(vec![( - AccountId::from(ALICE), - AccountId::from(ALICE), - SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) }, - )]) - .build() - .execute_with(|| { - let xcm = Xcm(vec![ - ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }])), - ClearOrigin, - BuyExecution { - fees: MultiAsset { - id: Concrete(MultiLocation { parents: 1, interior: Here }), - fun: Fungible(10000000000000), - }, - weight_limit: Limited(Weight::from_parts(303531000, 65536)), - }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: None, - id: [ - 18, 153, 85, 112, 1, 245, 88, 21, 211, 252, 181, 60, 116, 70, 58, - 203, 12, 246, 209, 77, 70, 57, 179, 64, 152, 44, 96, 135, 127, 56, - 70, 9, - ], - }), - }, - }, - ]); - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - - let weight_limit = ReservedDmpWeight::get(); - - let outcome = XcmExecutor::::execute_xcm(Parent, xcm, hash, weight_limit); - assert_eq!(outcome.ensure_complete(), Ok(())); - }) -} +asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ) +); + +asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( + Runtime, + XcmConfig, + WeightToFee, + ForeignCreatorsSovereignAccountOf, + ForeignAssetsInstance, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get() +); + +asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( + Runtime, + XcmConfig, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_works!( + asset_transactor_transfer_with_trust_backed_assets_works, + Runtime, + XcmConfig, + TrustBackedAssetsInstance, + AssetIdForTrustBackedAssets, + AssetIdForTrustBackedAssetsConvert, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + 12345, + Box::new(|| { + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_asset_transactor_transfer_with_pallet_assets_instance_works!( + asset_transactor_transfer_with_foreign_assets_works, + Runtime, + XcmConfig, + ForeignAssetsInstance, + MultiLocation, + JustTry, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + MultiLocation { parents: 1, interior: X2(Parachain(1313), GeneralIndex(12345)) }, + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + }) +); + +asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_parachain_assets_works!( + Runtime, + XcmConfig, + WeightToFee, + ForeignCreatorsSovereignAccountOf, + ForeignAssetsInstance, + MultiLocation, + JustTry, + asset_test_utils::CollatorSessionKeys::new( + AccountId::from(ALICE), + AccountId::from(ALICE), + SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } + ), + ExistentialDeposit::get(), + AssetDeposit::get(), + MetadataDepositBase::get(), + Box::new(|pallet_asset_call| RuntimeCall::ForeignAssets(pallet_asset_call).encode()), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::ForeignAssets(pallet_asset_event)) => Some(pallet_asset_event), + _ => None, + } + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert!(ForeignAssets::asset_ids().collect::>().is_empty()); + }), + Box::new(|| { + assert!(Assets::asset_ids().collect::>().is_empty()); + assert_eq!(ForeignAssets::asset_ids().collect::>().len(), 1); + }) +); #[test] fn plain_receive_teleported_asset_works() { @@ -494,10 +607,8 @@ fn plain_receive_teleported_asset_works() { ) .map(xcm::v3::Xcm::::try_from).expect("failed").expect("failed"); - let weight_limit = ReservedDmpWeight::get(); - let outcome = - XcmExecutor::::execute_xcm(Parent, maybe_msg, message_id, weight_limit); + XcmExecutor::::execute_xcm(Parent, maybe_msg, message_id, RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Parent)); assert_eq!(outcome.ensure_complete(), Ok(())); }) } diff --git a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 5c6332472aa..661b8403238 100644 --- a/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -45,7 +45,6 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); - pub const Local: MultiLocation = Here.into_location(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); } From 018b800e55ef60e382af907bf07f4bfededf5a11 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 16 Mar 2023 14:59:23 +0100 Subject: [PATCH 05/16] Added `StartsWithExplicitGlobalConsensus` to ignores (#2338) --- parachains/runtimes/assets/common/src/lib.rs | 60 ++++++++++++++++++- .../runtimes/assets/common/src/matching.rs | 12 ++++ .../assets/statemine/src/xcm_config.rs | 14 ++++- .../assets/westmint/src/xcm_config.rs | 14 ++++- 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/parachains/runtimes/assets/common/src/lib.rs b/parachains/runtimes/assets/common/src/lib.rs index fbf19b89914..999b0ae990a 100644 --- a/parachains/runtimes/assets/common/src/lib.rs +++ b/parachains/runtimes/assets/common/src/lib.rs @@ -79,6 +79,7 @@ pub type ForeignAssetsConvertedConcreteId, u128>; + type Convert = ForeignAssetsConvertedConcreteId< + ( + StartsWith, + StartsWithExplicitGlobalConsensus, + ), + u128, + >; let test_data = vec![ // excluded as local @@ -212,13 +220,33 @@ mod tests { ), // excluded as parent (ma_1000(1, Here), Err(MatchError::AssetNotHandled)), - // excluded as additional filter + // excluded as additional filter - Parachain100Pattern (ma_1000(1, X1(Parachain(100))), Err(MatchError::AssetNotHandled)), (ma_1000(1, X2(Parachain(100), GeneralIndex(1234))), Err(MatchError::AssetNotHandled)), ( ma_1000(1, X3(Parachain(100), PalletInstance(13), GeneralIndex(1234))), Err(MatchError::AssetNotHandled), ), + // excluded as additional filter - StartsWithExplicitGlobalConsensus + ( + ma_1000(1, X1(GlobalConsensus(NetworkId::ByGenesis([9; 32])))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000(2, X1(GlobalConsensus(NetworkId::ByGenesis([9; 32])))), + Err(MatchError::AssetNotHandled), + ), + ( + ma_1000( + 2, + X3( + GlobalConsensus(NetworkId::ByGenesis([9; 32])), + Parachain(200), + GeneralIndex(1234), + ), + ), + Err(MatchError::AssetNotHandled), + ), // ok (ma_1000(1, X1(Parachain(200))), Ok((MultiLocation::new(1, X1(Parachain(200))), 1000))), (ma_1000(2, X1(Parachain(200))), Ok((MultiLocation::new(2, X1(Parachain(200))), 1000))), @@ -230,6 +258,34 @@ mod tests { ma_1000(2, X2(Parachain(200), GeneralIndex(1234))), Ok((MultiLocation::new(2, X2(Parachain(200), GeneralIndex(1234))), 1000)), ), + ( + ma_1000(2, X1(GlobalConsensus(NetworkId::ByGenesis([7; 32])))), + Ok(( + MultiLocation::new(2, X1(GlobalConsensus(NetworkId::ByGenesis([7; 32])))), + 1000, + )), + ), + ( + ma_1000( + 2, + X3( + GlobalConsensus(NetworkId::ByGenesis([7; 32])), + Parachain(200), + GeneralIndex(1234), + ), + ), + Ok(( + MultiLocation::new( + 2, + X3( + GlobalConsensus(NetworkId::ByGenesis([7; 32])), + Parachain(200), + GeneralIndex(1234), + ), + ), + 1000, + )), + ), ]; for (multi_asset, expected_result) in test_data { diff --git a/parachains/runtimes/assets/common/src/matching.rs b/parachains/runtimes/assets/common/src/matching.rs index 05acaff3990..5a60b18b8c9 100644 --- a/parachains/runtimes/assets/common/src/matching.rs +++ b/parachains/runtimes/assets/common/src/matching.rs @@ -37,6 +37,18 @@ impl> Contains for Equals } } +pub struct StartsWithExplicitGlobalConsensus(sp_std::marker::PhantomData); +impl> Contains + for StartsWithExplicitGlobalConsensus +{ + fn contains(t: &MultiLocation) -> bool { + match t.interior.global_consensus() { + Ok(requested_network) if requested_network.eq(&Network::get()) => true, + _ => false, + } + } +} + frame_support::parameter_types! { pub LocalMultiLocationPattern: MultiLocation = MultiLocation::new(0, Here); pub ParentLocation: MultiLocation = MultiLocation::parent(); diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 3476da44a47..bb591d5bcab 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -18,7 +18,9 @@ use super::{ ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; -use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset, StartsWith}; +use assets_common::matching::{ + FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus, +}; use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, @@ -49,6 +51,7 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocationNetworkId: NetworkId = UniversalLocation::get().global_consensus().unwrap(); pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -103,7 +106,14 @@ pub type FungiblesTransactor = FungiblesAdapter< /// `AssetId/Balance` converter for `TrustBackedAssets` pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConvertedConcreteId< - StartsWith, + ( + // Ignore `TrustBackedAssets` explicitly + StartsWith, + // Ignore asset which starts explicitly with our `GlobalConsensus(NetworkId)`, means: + // - foreign assets from our consensus should be: `MultiLocation {parent: 1, X*(Parachain(xyz))} + // - foreign assets outside our consensus with the same `GlobalConsensus(NetworkId)` wont be accepted here + StartsWithExplicitGlobalConsensus, + ), Balance, >; diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index b6587c7dc97..b13e88b2b06 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -19,7 +19,9 @@ use super::{ TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; use crate::ForeignAssets; -use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset, StartsWith}; +use assets_common::matching::{ + FromSiblingParachain, IsForeignConcreteAsset, StartsWith, StartsWithExplicitGlobalConsensus, +}; use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, @@ -50,6 +52,7 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())); + pub UniversalLocationNetworkId: NetworkId = UniversalLocation::get().global_consensus().unwrap(); pub TrustBackedAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -104,7 +107,14 @@ pub type FungiblesTransactor = FungiblesAdapter< /// `AssetId/Balance` converter for `TrustBackedAssets` pub type ForeignAssetsConvertedConcreteId = assets_common::ForeignAssetsConvertedConcreteId< - StartsWith, + ( + // Ignore `TrustBackedAssets` explicitly + StartsWith, + // Ignore asset which starts explicitly with our `GlobalConsensus(NetworkId)`, means: + // - foreign assets from our consensus should be: `MultiLocation {parent: 1, X*(Parachain(xyz))} + // - foreign assets outside our consensus with the same `GlobalConsensus(NetworkId)` wont be accepted here + StartsWithExplicitGlobalConsensus, + ), Balance, >; From c33236ae41de5c10979828e1bcf5befe09dda955 Mon Sep 17 00:00:00 2001 From: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:12:11 +0100 Subject: [PATCH 06/16] Update parachains/runtimes/assets/common/src/lib.rs Co-authored-by: Gavin Wood --- parachains/runtimes/assets/common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/common/src/lib.rs b/parachains/runtimes/assets/common/src/lib.rs index 999b0ae990a..050b8871f6e 100644 --- a/parachains/runtimes/assets/common/src/lib.rs +++ b/parachains/runtimes/assets/common/src/lib.rs @@ -114,7 +114,7 @@ mod tests { pub TrustBackedAssetsPalletLocation: MultiLocation = MultiLocation::new(0, X1(PalletInstance(13))); } // setup convert - type TrustBackAssetsConvert = + type TrustBackedAssetsConvert = TrustBackedAssetsConvertedConcreteId; let test_data = vec![ From 4ca0d6d71f6c0cc7bd294e059bfeadd44dc4b126 Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Mon, 20 Mar 2023 13:14:51 +0100 Subject: [PATCH 07/16] include mint and burn in SafeCallFilter --- parachains/runtimes/assets/common/src/lib.rs | 2 +- parachains/runtimes/assets/westmint/src/xcm_config.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/parachains/runtimes/assets/common/src/lib.rs b/parachains/runtimes/assets/common/src/lib.rs index 050b8871f6e..8a321ad97aa 100644 --- a/parachains/runtimes/assets/common/src/lib.rs +++ b/parachains/runtimes/assets/common/src/lib.rs @@ -189,7 +189,7 @@ mod tests { for (multi_asset, expected_result) in test_data { assert_eq!( - >::matches_fungibles(&multi_asset), + >::matches_fungibles(&multi_asset), expected_result, "multi_asset: {:?}", multi_asset); } } diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index b13e88b2b06..55675ab2708 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -244,13 +244,14 @@ impl Contains for SafeCallFilter { pallet_assets::Call::refund { .. }, ) | RuntimeCall::ForeignAssets( - /* avoided: mint, burn */ pallet_assets::Call::create { .. } | pallet_assets::Call::force_create { .. } | pallet_assets::Call::start_destroy { .. } | pallet_assets::Call::destroy_accounts { .. } | pallet_assets::Call::destroy_approvals { .. } | pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | pallet_assets::Call::transfer { .. } | pallet_assets::Call::transfer_keep_alive { .. } | pallet_assets::Call::force_transfer { .. } | From 086f50781754ad25f8dab4b5bb6dce879bd29ebb Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Tue, 21 Mar 2023 13:39:34 +0100 Subject: [PATCH 08/16] include mint and burn in SafeCallFilter (statemine) --- parachains/runtimes/assets/statemine/src/xcm_config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 4101a71bf88..e8b1a3d057d 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -244,13 +244,14 @@ impl Contains for SafeCallFilter { pallet_assets::Call::refund { .. }, ) | RuntimeCall::ForeignAssets( - /* avoided: mint, burn */ pallet_assets::Call::create { .. } | pallet_assets::Call::force_create { .. } | pallet_assets::Call::start_destroy { .. } | pallet_assets::Call::destroy_accounts { .. } | pallet_assets::Call::destroy_approvals { .. } | pallet_assets::Call::finish_destroy { .. } | + pallet_assets::Call::mint { .. } | + pallet_assets::Call::burn { .. } | pallet_assets::Call::transfer { .. } | pallet_assets::Call::transfer_keep_alive { .. } | pallet_assets::Call::force_transfer { .. } | From 5bbdebbf901a7245c70a626abc64cf11bd87f85b Mon Sep 17 00:00:00 2001 From: joepetrowski Date: Tue, 21 Mar 2023 14:54:30 +0100 Subject: [PATCH 09/16] clarify doc --- parachains/runtimes/assets/common/src/matching.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/common/src/matching.rs b/parachains/runtimes/assets/common/src/matching.rs index 5a60b18b8c9..a5e030412b9 100644 --- a/parachains/runtimes/assets/common/src/matching.rs +++ b/parachains/runtimes/assets/common/src/matching.rs @@ -65,7 +65,8 @@ impl> ContainsPair(sp_std::marker::PhantomData); impl> ContainsPair for FromSiblingParachain From 7099ebc91cae37fb6d63102bcc47eed3e562f2e1 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 21 Mar 2023 15:13:55 +0100 Subject: [PATCH 10/16] Fix compilation (moved trait `InspectMetadata`) --- parachains/runtimes/assets/test-utils/src/lib.rs | 4 ++-- parachains/runtimes/assets/test-utils/src/test_cases.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index 94435365bd0..fab4d86a53c 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -179,12 +179,12 @@ impl RuntimeHelper( - asset_id: &Assets::AssetId, + asset_id: Assets::AssetId, expected_name: &str, expected_symbol: &str, expected_decimals: u8, ) where - Assets: frame_support::traits::tokens::fungibles::InspectMetadata + Assets: frame_support::traits::tokens::fungibles::metadata::Inspect + frame_support::traits::tokens::fungibles::Inspect, { assert_eq!(Assets::name(asset_id), Vec::from(expected_name),); diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index c6f2f5f8e91..be5c531f317 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -903,7 +903,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor assert_metadata::< pallet_assets::Pallet, AccountIdOf, - >(&asset_id.clone().into(), "My super coin", "MY_S_COIN", 12); + >(asset_id.clone().into(), "My super coin", "MY_S_COIN", 12); // check if changed freezer, can freeze assert_noop!( From 76b144debc596f731e761b6e57cd291b1f96a9b4 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Tue, 21 Mar 2023 16:33:25 +0100 Subject: [PATCH 11/16] Fix test --- .../runtimes/assets/statemine/tests/tests.rs | 5 ++-- .../assets/test-utils/src/test_cases.rs | 27 +++++++++++++------ .../runtimes/assets/westmint/tests/tests.rs | 3 ++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index 7d19a88618c..70aba65ba8a 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -14,8 +14,8 @@ pub use statemine_runtime::{ constants::fee::WeightToFee, xcm_config::{ForeignCreatorsSovereignAccountOf, XcmConfig}, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, - MetadataDepositBase, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, System, - TrustBackedAssetsInstance, + MetadataDepositBase, MetadataDepositPerByte, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, + System, TrustBackedAssetsInstance, }; use xcm::latest::prelude::*; use xcm_executor::traits::{Convert, Identity, JustTry, WeightTrader}; @@ -565,6 +565,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ExistentialDeposit::get(), AssetDeposit::get(), MetadataDepositBase::get(), + MetadataDepositPerByte::get(), Box::new(|pallet_asset_call| RuntimeCall::ForeignAssets(pallet_asset_call).encode()), Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index be5c531f317..5e69671023a 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -28,7 +28,7 @@ use frame_support::{ use parachains_common::Balance; use sp_runtime::{ traits::{StaticLookup, Zero}, - DispatchError, + DispatchError, Saturating, }; use xcm::latest::prelude::*; use xcm_executor::{traits::Convert, XcmExecutor}; @@ -709,6 +709,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor existential_deposit: BalanceOf, asset_deposit: BalanceOf, metadata_deposit_base: BalanceOf, + metadata_deposit_per_byte: BalanceOf, alice_account: AccountIdOf, bob_account: AccountIdOf, runtime_call_encode: Box< @@ -764,6 +765,11 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor fun: Fungible(buy_execution_fee_amount), }; + const ASSET_NAME: &str = "My super coin"; + const ASSET_SYMBOL: &str = "MY_S_COIN"; + let metadata_deposit_per_byte_eta = metadata_deposit_per_byte + .saturating_mul(((ASSET_NAME.len() + ASSET_SYMBOL.len()) as u128).into()); + ExtBuilder::::default() .with_collators(collator_session_keys.collators()) .with_session_keys(collator_session_keys.session_keys()) @@ -771,6 +777,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor foreign_creator_as_account_id.clone(), existential_deposit + asset_deposit + metadata_deposit_base + + metadata_deposit_per_byte_eta + buy_execution_fee_amount.into() + buy_execution_fee_amount.into(), )]) @@ -784,6 +791,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor >::free_balance(&foreign_creator_as_account_id), existential_deposit + asset_deposit + metadata_deposit_base + + metadata_deposit_per_byte_eta + buy_execution_fee_amount.into() + buy_execution_fee_amount.into() ); @@ -806,8 +814,8 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor ForeignAssetsPalletInstance, >::set_metadata { id: asset_id.clone().into(), - name: Vec::from("My super coin"), - symbol: Vec::from("MY_S_COIN"), + name: Vec::from(ASSET_NAME), + symbol: Vec::from(ASSET_SYMBOL), decimals: 12, }); // prepapre data for xcm::Transact(set_team - change just freezer to Bob) @@ -895,15 +903,16 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Some(bob_account.clone()) ); assert!( - >::free_balance(&foreign_creator_as_account_id) < - existential_deposit + - buy_execution_fee_amount.into() + - buy_execution_fee_amount.into() + >::free_balance(&foreign_creator_as_account_id) >= + existential_deposit + buy_execution_fee_amount.into(), + "Free balance: {:?} should be ge {:?}", + >::free_balance(&foreign_creator_as_account_id), + existential_deposit + buy_execution_fee_amount.into() ); assert_metadata::< pallet_assets::Pallet, AccountIdOf, - >(asset_id.clone().into(), "My super coin", "MY_S_COIN", 12); + >(asset_id.clone().into(), ASSET_NAME, ASSET_SYMBOL, 12); // check if changed freezer, can freeze assert_noop!( @@ -979,6 +988,7 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach $existential_deposit:expr, $asset_deposit:expr, $metadata_deposit_base:expr, + $metadata_deposit_per_byte:expr, $runtime_call_encode:expr, $unwrap_pallet_assets_event:expr, $additional_checks_before:expr, @@ -1004,6 +1014,7 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach $existential_deposit, $asset_deposit, $metadata_deposit_base, + $metadata_deposit_per_byte, alice_account, bob_account, $runtime_call_encode, diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index 77125a4884c..bd1b7e5f385 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -19,7 +19,7 @@ use westmint_runtime::{ AssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf, WestendLocation, }, - MetadataDepositBase, RuntimeCall, RuntimeEvent, + MetadataDepositBase, MetadataDepositPerByte, RuntimeCall, RuntimeEvent, }; use xcm::{latest::prelude::*, VersionedXcm, MAX_XCM_DECODE_DEPTH}; use xcm_executor::{ @@ -570,6 +570,7 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p ExistentialDeposit::get(), AssetDeposit::get(), MetadataDepositBase::get(), + MetadataDepositPerByte::get(), Box::new(|pallet_asset_call| RuntimeCall::ForeignAssets(pallet_asset_call).encode()), Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { From 852a32a33f526948e5be18bc5c52fd424c03233a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 22 Mar 2023 16:45:27 +0100 Subject: [PATCH 12/16] Extended test for `teleport` from/to relaychain + `CheckingAccount` (Part I) --- Cargo.lock | 7 + .../runtimes/assets/statemine/tests/tests.rs | 27 ++- .../runtimes/assets/statemint/tests/tests.rs | 27 ++- .../runtimes/assets/test-utils/Cargo.toml | 15 ++ .../runtimes/assets/test-utils/src/lib.rs | 200 ++++++++++++++++- .../assets/test-utils/src/test_cases.rs | 209 ++++++++++++++++-- .../runtimes/assets/westmint/tests/tests.rs | 25 ++- .../bridge-hub-kusama/src/xcm_config.rs | 2 +- .../bridge-hub-polkadot/src/xcm_config.rs | 2 +- .../bridge-hub-rococo/src/xcm_config.rs | 2 +- 10 files changed, 477 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb4194c45eb..83699b3ce97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,6 +336,10 @@ version = "1.0.0" dependencies = [ "assets-common", "cumulus-pallet-parachain-system", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "cumulus-test-relay-sproof-builder", "frame-support", "frame-system", "hex-literal", @@ -343,8 +347,11 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-session", + "pallet-xcm", + "parachain-info", "parachains-common", "parity-scale-codec", + "polkadot-parachain", "sp-consensus-aura", "sp-core", "sp-io", diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index 70aba65ba8a..bdf4f6a4e1c 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -12,10 +12,10 @@ use statemine_runtime::xcm_config::{ }; pub use statemine_runtime::{ constants::fee::WeightToFee, - xcm_config::{ForeignCreatorsSovereignAccountOf, XcmConfig}, + xcm_config::{CheckingAccount, ForeignCreatorsSovereignAccountOf, XcmConfig}, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, - MetadataDepositBase, MetadataDepositPerByte, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, - System, TrustBackedAssetsInstance, + MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, + RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, }; use xcm::latest::prelude::*; use xcm_executor::traits::{Convert, Identity, JustTry, WeightTrader}; @@ -462,19 +462,36 @@ fn test_assets_balances_api_works() { }); } -asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( +asset_test_utils::include_teleports_for_native_asset_works!( Runtime, XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, asset_test_utils::CollatorSessionKeys::new( AccountId::from(ALICE), AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ) + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }) ); asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( Runtime, XcmConfig, + CheckingAccount, WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, diff --git a/parachains/runtimes/assets/statemint/tests/tests.rs b/parachains/runtimes/assets/statemint/tests/tests.rs index 2e070dc1aec..501c16960aa 100644 --- a/parachains/runtimes/assets/statemint/tests/tests.rs +++ b/parachains/runtimes/assets/statemint/tests/tests.rs @@ -1,4 +1,5 @@ use asset_test_utils::{ExtBuilder, RuntimeHelper}; +use codec::Decode; use cumulus_primitives_utility::ChargeWeightInFungibles; use frame_support::{ assert_noop, assert_ok, @@ -9,11 +10,13 @@ use parachains_common::{ AccountId, AssetIdForTrustBackedAssets, Balance, StatemintAuraId as AuraId, }; use statemint_runtime::xcm_config::{ - AssetFeeAsExistentialDepositMultiplierFeeCharger, DotLocation, TrustBackedAssetsPalletLocation, + AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, DotLocation, + TrustBackedAssetsPalletLocation, }; pub use statemint_runtime::{ constants::fee::WeightToFee, xcm_config::XcmConfig, AssetDeposit, Assets, Balances, - ExistentialDeposit, Runtime, SessionKeys, System, TrustBackedAssetsInstance, + ExistentialDeposit, ParachainSystem, Runtime, RuntimeEvent, SessionKeys, System, + TrustBackedAssetsInstance, }; use xcm::latest::prelude::*; use xcm_executor::traits::{Convert, WeightTrader}; @@ -437,14 +440,30 @@ fn test_assets_balances_api_works() { }); } -asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( +asset_test_utils::include_teleports_for_native_asset_works!( Runtime, XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, asset_test_utils::CollatorSessionKeys::new( AccountId::from(ALICE), AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::ed25519::Public::from_raw(ALICE)) } - ) + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }) ); asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( diff --git a/parachains/runtimes/assets/test-utils/Cargo.toml b/parachains/runtimes/assets/test-utils/Cargo.toml index c80bb7f67af..2a9e70ce2dd 100644 --- a/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/parachains/runtimes/assets/test-utils/Cargo.toml @@ -22,13 +22,20 @@ sp-core = { git = "https://github.com/paritytech/substrate", default-features = # Cumulus cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false } +cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false } pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false } parachains-common = { path = "../../../common", default-features = false } assets-common = { path = "../common", default-features = false } +cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false } +cumulus-primitives-parachain-inherent = { path = "../../../../primitives/parachain-inherent", default-features = false } +cumulus-test-relay-sproof-builder = { path = "../../../../test/relay-sproof-builder", default-features = false } +parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false } # Polkadot xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } +polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" } [dev-dependencies] hex-literal = "0.3.4" @@ -39,6 +46,10 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", bran [features] default = [ "std" ] std = [ + "cumulus-pallet-parachain-system/std", + "cumulus-primitives-core/std", + "cumulus-test-relay-sproof-builder/std", + "cumulus-primitives-parachain-inherent/std", "frame-support/std", "frame-system/std", "pallet-assets/std", @@ -48,10 +59,14 @@ std = [ "pallet-session/std", "assets-common/std", "parachains-common/std", + "parachain-info/std", + "polkadot-parachain/std", "sp-consensus-aura/std", "sp-io/std", "sp-runtime/std", "sp-std/std", "xcm/std", "xcm-executor/std", + "pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", ] diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index fab4d86a53c..10f5e5a7fff 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -1,14 +1,22 @@ -use frame_support::traits::GenesisBuild; use sp_std::marker::PhantomData; -use frame_support::{traits::OriginTrait, weights::Weight}; +use cumulus_primitives_core::{AbridgedHrmpChannel, ParaId, PersistedValidationData}; +use cumulus_primitives_parachain_inherent::ParachainInherentData; +use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use frame_support::{ + dispatch::{DispatchResult, RawOrigin, UnfilteredDispatchable}, + inherent::{InherentData, ProvideInherent}, + traits::{GenesisBuild, OriginTrait}, + weights::Weight, +}; use parachains_common::AccountId; +use polkadot_parachain::primitives::{HrmpChannelId, RelayChainBlockNumber}; use sp_consensus_aura::AURA_ENGINE_ID; use sp_core::Encode; use sp_runtime::{Digest, DigestItem}; use xcm::{ - latest::{MultiAsset, MultiLocation, XcmContext}, - prelude::{Concrete, Fungible, XcmError}, + latest::{MultiAsset, MultiLocation, XcmContext, XcmHash}, + prelude::{Concrete, Fungible, Outcome, XcmError, XcmVersion}, }; use xcm_executor::{traits::TransactAsset, Assets}; @@ -22,7 +30,11 @@ pub type SessionKeysOf = ::Keys; // Basic builder based on balances, collators and pallet_sessopm pub struct ExtBuilder< - Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config, + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config, > { // endowed accounts with balances balances: Vec<(AccountIdOf, BalanceOf)>, @@ -30,19 +42,40 @@ pub struct ExtBuilder< collators: Vec>, // keys added to pallet session keys: Vec<(AccountIdOf, ValidatorIdOf, SessionKeysOf)>, + // safe xcm version for pallet_xcm + safe_xcm_version: Option, + // para id + para_id: Option, _runtime: PhantomData, } -impl Default - for ExtBuilder +impl< + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config, + > Default for ExtBuilder { fn default() -> ExtBuilder { - ExtBuilder { balances: vec![], collators: vec![], keys: vec![], _runtime: PhantomData } + ExtBuilder { + balances: vec![], + collators: vec![], + keys: vec![], + safe_xcm_version: None, + para_id: None, + _runtime: PhantomData, + } } } -impl - ExtBuilder +impl< + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config, + > ExtBuilder { pub fn with_balances( mut self, @@ -69,6 +102,16 @@ impl Self { + self.safe_xcm_version = Some(safe_xcm_version); + self + } + + pub fn with_para_id(mut self, para_id: ParaId) -> Self { + self.para_id = Some(para_id); + self + } + pub fn build(self) -> sp_io::TestExternalities where Runtime: @@ -77,6 +120,20 @@ impl().unwrap(); + >::assimilate_storage( + &pallet_xcm::GenesisConfig { safe_xcm_version: self.safe_xcm_version }, + &mut t, + ) + .unwrap(); + + if let Some(para_id) = self.para_id { + >::assimilate_storage( + ¶chain_info::GenesisConfig { parachain_id: para_id }, + &mut t, + ) + .unwrap(); + } + pallet_balances::GenesisConfig:: { balances: self.balances.into() } .assimilate_storage(&mut t) .unwrap(); @@ -163,6 +220,38 @@ impl RuntimeHelper { } } +impl RuntimeHelper { + pub fn do_teleport_assets( + origin: ::RuntimeOrigin, + dest: MultiLocation, + beneficiary: MultiLocation, + (asset, amount): (MultiLocation, u128), + open_hrmp_channel: Option<(u32, u32)>, + ) -> DispatchResult + where + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, + { + // open hrmp (if needed) + if let Some((source_para_id, target_para_id)) = open_hrmp_channel { + mock_open_hrmp_channel::( + source_para_id.into(), + target_para_id.into(), + ); + } + + // do teleport + >::teleport_assets( + origin, + Box::new(dest.into()), + Box::new(beneficiary.into()), + Box::new((Concrete(asset), amount).into()), + 0, + ) + } +} + pub enum XcmReceivedFrom { Parent, Sibling, @@ -178,6 +267,41 @@ impl RuntimeHelper RuntimeHelper { + pub fn assert_pallet_xcm_event_outcome( + unwrap_pallet_xcm_event: &Box) -> Option>>, + assert_outcome: fn(Outcome), + ) { + let outcome = >::events() + .into_iter() + .filter_map(|e| unwrap_pallet_xcm_event(e.event.encode())) + .find_map(|e| match e { + pallet_xcm::Event::Attempted(outcome) => Some(outcome), + _ => None, + }); + match outcome { + Some(outcome) => assert_outcome(outcome), + None => assert!(false, "No `pallet_xcm::Event::Attempted(outcome)` event found!"), + } + } +} + +impl RuntimeHelper { + pub fn xcmp_queue_message_sent( + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, + ) -> Option { + >::events() + .into_iter() + .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode())) + .find_map(|e| match e { + cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => message_hash, + _ => None, + }) + } +} + pub fn assert_metadata( asset_id: Assets::AssetId, expected_name: &str, @@ -191,3 +315,59 @@ pub fn assert_metadata( assert_eq!(Assets::symbol(asset_id), Vec::from(expected_symbol),); assert_eq!(Assets::decimals(asset_id), expected_decimals); } + +/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass +pub fn mock_open_hrmp_channel< + C: cumulus_pallet_parachain_system::Config, + T: ProvideInherent>, +>( + sender: ParaId, + recipient: ParaId, +) { + let n = 1_u32; + let mut sproof_builder = RelayStateSproofBuilder::default(); + sproof_builder.para_id = sender; + sproof_builder.hrmp_channels.insert( + HrmpChannelId { sender, recipient }, + AbridgedHrmpChannel { + max_capacity: 10, + max_total_size: 10_000_000_u32, + max_message_size: 10_000_000_u32, + msg_count: 0, + total_size: 0_u32, + mqc_head: None, + }, + ); + sproof_builder.hrmp_egress_channel_index = Some(vec![recipient]); + + let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); + let vfp = PersistedValidationData { + relay_parent_number: n as RelayChainBlockNumber, + relay_parent_storage_root, + ..Default::default() + }; + // It is insufficient to push the validation function params + // to storage; they must also be included in the inherent data. + let inherent_data = { + let mut inherent_data = InherentData::default(); + let system_inherent_data = ParachainInherentData { + validation_data: vfp.clone(), + relay_chain_state, + downward_messages: Default::default(), + horizontal_messages: Default::default(), + }; + inherent_data + .put_data( + cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER, + &system_inherent_data, + ) + .expect("failed to put VFP inherent"); + inherent_data + }; + + // execute the block + T::create_inherent(&inherent_data) + .expect("got an inherent") + .dispatch_bypass_filter(RawOrigin::None.into()) + .expect("dispatch succeeded"); +} diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 5e69671023a..6b665e03d73 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -22,7 +22,7 @@ use crate::{ use codec::Encode; use frame_support::{ assert_noop, assert_ok, - traits::{fungibles::InspectEnumerable, OriginTrait}, + traits::{fungibles::InspectEnumerable, Get, OriginTrait}, weights::Weight, }; use parachains_common::Balance; @@ -62,41 +62,79 @@ impl( +/// Test-case makes sure that `Runtime` can receive native asset from relay chain +/// and can teleport it back and to the other parachains +pub fn teleports_for_native_asset_works< + Runtime, + XcmConfig, + CheckingAccount, + WeightToFee, + HrmpChannelOpener, +>( collator_session_keys: CollatorSessionKeys, + existential_deposit: BalanceOf, target_account: AccountIdOf, + unwrap_pallet_xcm_event: Box) -> Option>>, + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, ) where Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + pallet_collator_selection::Config - + cumulus_pallet_parachain_system::Config, + + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, - BalanceOf: From, + BalanceOf: From + Into, + WeightToFee: frame_support::weights::WeightToFee, + ::Balance: From + Into, + ::AccountId: + Into<<::RuntimeOrigin as OriginTrait>::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, XcmConfig: xcm_executor::Config, + CheckingAccount: Get>, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, { + let runtime_para_id = 1000; ExtBuilder::::default() .with_collators(collator_session_keys.collators()) .with_session_keys(collator_session_keys.session_keys()) + .with_safe_xcm_version(3) + .with_para_id(runtime_para_id.into()) .build() .execute_with(|| { // check Balances before assert_eq!(>::free_balance(&target_account), 0.into()); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); let native_asset_id = MultiLocation::parent(); + let buy_execution_fee_amount_eta = + WeightToFee::weight_to_fee(&Weight::from_parts(90_000_000_000, 0)); + let native_asset_amount_unit = existential_deposit; + let native_asset_amount_received = + native_asset_amount_unit * 10.into() + buy_execution_fee_amount_eta.into(); + // process received teleported assets from relaychain let xcm = Xcm(vec![ ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { id: Concrete(native_asset_id), - fun: Fungible(10000000000000), + fun: Fungible(native_asset_amount_received.into()), }])), ClearOrigin, BuyExecution { fees: MultiAsset { id: Concrete(native_asset_id), - fun: Fungible(10000000000000), + fun: Fungible(buy_execution_fee_amount_eta.into()), }, weight_limit: Limited(Weight::from_parts(303531000, 65536)), }, @@ -125,25 +163,136 @@ pub fn receive_teleported_asset_for_native_asset_works( // check Balances after assert_ne!(>::free_balance(&target_account), 0.into()); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); + + // 2. try to teleport asset back to the relaychain + { + let dest = MultiLocation::parent(); + let dest_beneficiary = MultiLocation::parent() + .appended_with(AccountId32 { + network: None, + id: sp_runtime::AccountId32::new([3; 32]).into(), + }) + .unwrap(); + + let target_account_balance_before_teleport = + >::free_balance(&target_account); + let native_asset_to_teleport_away = native_asset_amount_unit * 3.into(); + assert!( + native_asset_to_teleport_away < + target_account_balance_before_teleport - existential_deposit + ); + + assert_ok!(RuntimeHelper::::do_teleport_assets::( + RuntimeHelper::::origin_of(target_account.clone()), + dest, + dest_beneficiary, + (native_asset_id, native_asset_to_teleport_away.clone().into()), + None, + )); + // check balances + assert_eq!( + >::free_balance(&target_account), + target_account_balance_before_teleport - native_asset_to_teleport_away + ); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); + + // check events + RuntimeHelper::::assert_pallet_xcm_event_outcome( + &unwrap_pallet_xcm_event, + |outcome| { + assert_ok!(outcome.ensure_complete()); + }, + ); + } + + // 3. try to teleport asset away to other parachain (1234) + { + let other_para_id = 1234; + let dest = MultiLocation::new(1, X1(Parachain(other_para_id))); + let dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id))) + .appended_with(AccountId32 { + network: None, + id: sp_runtime::AccountId32::new([3; 32]).into(), + }) + .unwrap(); + + let target_account_balance_before_teleport = + >::free_balance(&target_account); + let native_asset_to_teleport_away = native_asset_amount_unit * 3.into(); + assert!( + native_asset_to_teleport_away < + target_account_balance_before_teleport - existential_deposit + ); + + assert_ok!(RuntimeHelper::::do_teleport_assets::( + RuntimeHelper::::origin_of(target_account.clone()), + dest, + dest_beneficiary, + (native_asset_id, native_asset_to_teleport_away.clone().into()), + Some((runtime_para_id, other_para_id)), + )); + + // check balances + assert_eq!( + >::free_balance(&target_account), + target_account_balance_before_teleport - native_asset_to_teleport_away + ); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); + + // check events + RuntimeHelper::::assert_pallet_xcm_event_outcome( + &unwrap_pallet_xcm_event, + |outcome| { + assert_ok!(outcome.ensure_complete()); + }, + ); + assert!(RuntimeHelper::::xcmp_queue_message_sent(unwrap_xcmp_queue_event) + .is_some()); + } }) } #[macro_export] -macro_rules! include_receive_teleported_asset_for_native_asset_works( +macro_rules! include_teleports_for_native_asset_works( ( $runtime:path, $xcm_config:path, - $collator_session_key:expr + $checking_account:path, + $weight_to_fee:path, + $hrmp_channel_opener:path, + $collator_session_key:expr, + $existential_deposit:expr, + $unwrap_pallet_xcm_event:expr, + $unwrap_xcmp_queue_event:expr ) => { #[test] - fn receive_teleported_asset_for_native_asset_works() { + fn teleports_for_native_asset_works() { const BOB: [u8; 32] = [2u8; 32]; let target_account = parachains_common::AccountId::from(BOB); - asset_test_utils::test_cases::receive_teleported_asset_for_native_asset_works::< + asset_test_utils::test_cases::teleports_for_native_asset_works::< $runtime, - $xcm_config - >($collator_session_key, target_account) + $xcm_config, + $checking_account, + $weight_to_fee, + $hrmp_channel_opener + >( + $collator_session_key, + $existential_deposit, + target_account, + $unwrap_pallet_xcm_event, + $unwrap_xcmp_queue_event + ) } } ); @@ -152,6 +301,7 @@ macro_rules! include_receive_teleported_asset_for_native_asset_works( pub fn receive_teleported_asset_from_foreign_creator_works< Runtime, XcmConfig, + CheckingAccount, WeightToFee, SovereignAccountOf, ForeignAssetsPalletInstance, @@ -164,6 +314,8 @@ pub fn receive_teleported_asset_from_foreign_creator_works< Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + pallet_assets::Config, @@ -171,6 +323,7 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ValidatorIdOf: From>, BalanceOf: From, XcmConfig: xcm_executor::Config, + CheckingAccount: Get>, WeightToFee: frame_support::weights::WeightToFee, ::Balance: From + Into, SovereignAccountOf: Convert>, @@ -222,6 +375,10 @@ pub fn receive_teleported_asset_from_foreign_creator_works< >::free_balance(&target_account), existential_deposit ); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); assert_eq!( >::balance( foreign_asset_id_multilocation.into(), @@ -229,6 +386,13 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ), 0.into() ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &CheckingAccount::get() + ), + 0.into() + ); // create foreign asset let asset_minimum_asset_balance = 3333333_u128; @@ -296,6 +460,17 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ), teleported_foreign_asset_amount.into() ); + assert_eq!( + >::free_balance(&CheckingAccount::get()), + 0.into() + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &CheckingAccount::get() + ), + 0.into() + ); }) } @@ -304,6 +479,7 @@ macro_rules! include_receive_teleported_asset_from_foreign_creator_works( ( $runtime:path, $xcm_config:path, + $checking_account:path, $weight_to_fee:path, $sovereign_account_of:path, $assets_pallet_instance:path, @@ -320,6 +496,7 @@ macro_rules! include_receive_teleported_asset_from_foreign_creator_works( asset_test_utils::test_cases::receive_teleported_asset_from_foreign_creator_works::< $runtime, $xcm_config, + $checking_account, $weight_to_fee, $sovereign_account_of, $assets_pallet_instance @@ -340,6 +517,8 @@ pub fn asset_transactor_transfer_with_local_consensus_currency_works: Into<[u8; 32]>, @@ -459,6 +638,8 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + pallet_assets::Config, @@ -724,6 +905,8 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Runtime: frame_system::Config + pallet_balances::Config + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + pallet_assets::Config, diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index bd1b7e5f385..e426f453277 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -10,9 +10,9 @@ use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance} use std::convert::Into; pub use westmint_runtime::{ constants::fee::WeightToFee, - xcm_config::{TrustBackedAssetsPalletLocation, XcmConfig}, + xcm_config::{CheckingAccount, TrustBackedAssetsPalletLocation, XcmConfig}, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, - Runtime, SessionKeys, System, TrustBackedAssetsInstance, + ParachainSystem, Runtime, SessionKeys, System, TrustBackedAssetsInstance, }; use westmint_runtime::{ xcm_config::{ @@ -467,19 +467,36 @@ fn test_assets_balances_api_works() { }); } -asset_test_utils::include_receive_teleported_asset_for_native_asset_works!( +asset_test_utils::include_teleports_for_native_asset_works!( Runtime, XcmConfig, + CheckingAccount, + WeightToFee, + ParachainSystem, asset_test_utils::CollatorSessionKeys::new( AccountId::from(ALICE), AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } - ) + ), + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }) ); asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( Runtime, XcmConfig, + CheckingAccount, WeightToFee, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index b9992438c83..076790a9708 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -73,7 +73,7 @@ pub type CurrencyTransactor = CurrencyAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We don't track any teleports. + // We don't track any teleports of `Balances`. (), >; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs index e52bca88daf..48f2b969e26 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs @@ -73,7 +73,7 @@ pub type CurrencyTransactor = CurrencyAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We don't track any teleports. + // We don't track any teleports of `Balances`. (), >; diff --git a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 3067cb3f507..08226b9047b 100644 --- a/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -71,7 +71,7 @@ pub type CurrencyTransactor = CurrencyAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We don't track any teleports. + // We don't track any teleports of `Balances`. (), >; From 74c70c8b29058939dc4abdf6149dc82a6937fe95 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 22 Mar 2023 17:18:54 +0100 Subject: [PATCH 13/16] Extended test for `teleport` from/to foreign parachain + `CheckingAccount` (Part II) --- .../runtimes/assets/statemine/tests/tests.rs | 17 ++- .../assets/test-utils/src/test_cases.rs | 123 ++++++++++++++++-- .../runtimes/assets/westmint/tests/tests.rs | 17 ++- 3 files changed, 139 insertions(+), 18 deletions(-) diff --git a/parachains/runtimes/assets/statemine/tests/tests.rs b/parachains/runtimes/assets/statemine/tests/tests.rs index bdf4f6a4e1c..972abe9d427 100644 --- a/parachains/runtimes/assets/statemine/tests/tests.rs +++ b/parachains/runtimes/assets/statemine/tests/tests.rs @@ -488,11 +488,12 @@ asset_test_utils::include_teleports_for_native_asset_works!( }) ); -asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( +asset_test_utils::include_teleports_for_foreign_assets_works!( Runtime, XcmConfig, CheckingAccount, WeightToFee, + ParachainSystem, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, asset_test_utils::CollatorSessionKeys::new( @@ -500,7 +501,19 @@ asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - ExistentialDeposit::get() + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }) ); asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 6b665e03d73..92a4f39b9e6 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -106,7 +106,7 @@ pub fn teleports_for_native_asset_works< ExtBuilder::::default() .with_collators(collator_session_keys.collators()) .with_session_keys(collator_session_keys.session_keys()) - .with_safe_xcm_version(3) + .with_safe_xcm_version(XCM_VERSION) .with_para_id(runtime_para_id.into()) .build() .execute_with(|| { @@ -124,7 +124,7 @@ pub fn teleports_for_native_asset_works< let native_asset_amount_received = native_asset_amount_unit * 10.into() + buy_execution_fee_amount_eta.into(); - // process received teleported assets from relaychain + // 1. process received teleported assets from relaychain let xcm = Xcm(vec![ ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset { id: Concrete(native_asset_id), @@ -298,11 +298,12 @@ macro_rules! include_teleports_for_native_asset_works( ); /// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain relay chain -pub fn receive_teleported_asset_from_foreign_creator_works< +pub fn teleports_for_foreign_assets_works< Runtime, XcmConfig, CheckingAccount, WeightToFee, + HrmpChannelOpener, SovereignAccountOf, ForeignAssetsPalletInstance, >( @@ -310,6 +311,10 @@ pub fn receive_teleported_asset_from_foreign_creator_works< target_account: AccountIdOf, existential_deposit: BalanceOf, asset_owner: AccountIdOf, + unwrap_pallet_xcm_event: Box) -> Option>>, + unwrap_xcmp_queue_event: Box< + dyn Fn(Vec) -> Option>, + >, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -318,12 +323,16 @@ pub fn receive_teleported_asset_from_foreign_creator_works< + parachain_info::Config + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config + pallet_assets::Config, AccountIdOf: Into<[u8; 32]>, ValidatorIdOf: From>, BalanceOf: From, XcmConfig: xcm_executor::Config, CheckingAccount: Get>, + HrmpChannelOpener: frame_support::inherent::ProvideInherent< + Call = cumulus_pallet_parachain_system::Call, + >, WeightToFee: frame_support::weights::WeightToFee, ::Balance: From + Into, SovereignAccountOf: Convert>, @@ -340,11 +349,14 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ForeignAssetsPalletInstance: 'static, { // foreign parachain with the same consenus currency as asset - let foreign_asset_id_multilocation = - MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) }; + let foreign_para_id = 2222; + let foreign_asset_id_multilocation = MultiLocation { + parents: 1, + interior: X2(Parachain(foreign_para_id), GeneralIndex(1234567)), + }; // foreign creator, which can be sibling parachain to match ForeignCreators - let foreign_creator = MultiLocation { parents: 1, interior: X1(Parachain(2222)) }; + let foreign_creator = MultiLocation { parents: 1, interior: X1(Parachain(foreign_para_id)) }; let foreign_creator_as_account_id = SovereignAccountOf::convert(foreign_creator).expect(""); // we want to buy execution with local relay chain currency @@ -356,7 +368,7 @@ pub fn receive_teleported_asset_from_foreign_creator_works< }; let teleported_foreign_asset_amount = 10000000000000; - + let runtime_para_id = 1000; ExtBuilder::::default() .with_collators(collator_session_keys.collators()) .with_session_keys(collator_session_keys.session_keys()) @@ -367,6 +379,8 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ), (target_account.clone(), existential_deposit), ]) + .with_safe_xcm_version(XCM_VERSION) + .with_para_id(runtime_para_id.into()) .with_tracing() .build() .execute_with(|| { @@ -394,7 +408,7 @@ pub fn receive_teleported_asset_from_foreign_creator_works< 0.into() ); - // create foreign asset + // create foreign asset (0 total issuance) let asset_minimum_asset_balance = 3333333_u128; assert_ok!( >::force_create( @@ -405,9 +419,15 @@ pub fn receive_teleported_asset_from_foreign_creator_works< asset_minimum_asset_balance.into() ) ); + assert_eq!( + >::total_supply( + foreign_asset_id_multilocation.clone().into() + ), + 0.into() + ); assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance); - // prepare xcm + // 1. process received teleported assets from relaychain let xcm = Xcm(vec![ // BuyExecution with relaychain native token WithdrawAsset(buy_execution_fee.clone().into()), @@ -471,36 +491,111 @@ pub fn receive_teleported_asset_from_foreign_creator_works< ), 0.into() ); + assert_eq!( + >::total_supply( + foreign_asset_id_multilocation.into() + ), + teleported_foreign_asset_amount.into() + ); + + // 2. try to teleport asset back to source parachain (foreign_para_id) + { + let dest = MultiLocation::new(1, X1(Parachain(foreign_para_id))); + let dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id))) + .appended_with(AccountId32 { + network: None, + id: sp_runtime::AccountId32::new([3; 32]).into(), + }) + .unwrap(); + + let target_account_balance_before_teleport = + >::balance( + foreign_asset_id_multilocation.into(), + &target_account, + ); + let asset_to_teleport_away = asset_minimum_asset_balance * 3; + assert!( + asset_to_teleport_away < + (target_account_balance_before_teleport - + asset_minimum_asset_balance.into()) + .into() + ); + + assert_ok!(RuntimeHelper::::do_teleport_assets::( + RuntimeHelper::::origin_of(target_account.clone()), + dest, + dest_beneficiary, + (foreign_asset_id_multilocation, asset_to_teleport_away.clone().into()), + Some((runtime_para_id, foreign_para_id)), + )); + + // check balances + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &target_account + ), + (target_account_balance_before_teleport - asset_to_teleport_away.into()) + ); + assert_eq!( + >::balance( + foreign_asset_id_multilocation.into(), + &CheckingAccount::get() + ), + 0.into() + ); + + // check events + RuntimeHelper::::assert_pallet_xcm_event_outcome( + &unwrap_pallet_xcm_event, + |outcome| { + assert_ok!(outcome.ensure_complete()); + }, + ); + assert!(RuntimeHelper::::xcmp_queue_message_sent(unwrap_xcmp_queue_event) + .is_some()); + } }) } #[macro_export] -macro_rules! include_receive_teleported_asset_from_foreign_creator_works( +macro_rules! include_teleports_for_foreign_assets_works( ( $runtime:path, $xcm_config:path, $checking_account:path, $weight_to_fee:path, + $hrmp_channel_opener:path, $sovereign_account_of:path, $assets_pallet_instance:path, $collator_session_key:expr, - $existential_deposit:expr + $existential_deposit:expr, + $unwrap_pallet_xcm_event:expr, + $unwrap_xcmp_queue_event:expr ) => { #[test] - fn receive_teleported_asset_from_foreign_creator_works() { + fn teleports_for_foreign_assets_works() { const BOB: [u8; 32] = [2u8; 32]; let target_account = parachains_common::AccountId::from(BOB); const SOME_ASSET_OWNER: [u8; 32] = [5u8; 32]; let asset_owner = parachains_common::AccountId::from(SOME_ASSET_OWNER); - asset_test_utils::test_cases::receive_teleported_asset_from_foreign_creator_works::< + asset_test_utils::test_cases::teleports_for_foreign_assets_works::< $runtime, $xcm_config, $checking_account, $weight_to_fee, + $hrmp_channel_opener, $sovereign_account_of, $assets_pallet_instance - >($collator_session_key, target_account, $existential_deposit, asset_owner) + >( + $collator_session_key, + target_account, + $existential_deposit, + asset_owner, + $unwrap_pallet_xcm_event, + $unwrap_xcmp_queue_event + ) } } ); diff --git a/parachains/runtimes/assets/westmint/tests/tests.rs b/parachains/runtimes/assets/westmint/tests/tests.rs index e426f453277..e04c163b01f 100644 --- a/parachains/runtimes/assets/westmint/tests/tests.rs +++ b/parachains/runtimes/assets/westmint/tests/tests.rs @@ -493,11 +493,12 @@ asset_test_utils::include_teleports_for_native_asset_works!( }) ); -asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( +asset_test_utils::include_teleports_for_foreign_assets_works!( Runtime, XcmConfig, CheckingAccount, WeightToFee, + ParachainSystem, ForeignCreatorsSovereignAccountOf, ForeignAssetsInstance, asset_test_utils::CollatorSessionKeys::new( @@ -505,7 +506,19 @@ asset_test_utils::include_receive_teleported_asset_from_foreign_creator_works!( AccountId::from(ALICE), SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) } ), - ExistentialDeposit::get() + ExistentialDeposit::get(), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event), + _ => None, + } + }), + Box::new(|runtime_event_encoded: Vec| { + match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { + Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), + _ => None, + } + }) ); asset_test_utils::include_asset_transactor_transfer_with_local_consensus_currency_works!( From e203b2a2e55b7b3af5b87b75e178c7b7e142fbe2 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Wed, 22 Mar 2023 21:45:49 +0100 Subject: [PATCH 14/16] Fixed TODO - `NonLocal` for `ForeignAssets` --- parachains/common/src/impls.rs | 12 +++++++ .../assets/statemine/src/xcm_config.rs | 6 ++-- .../assets/test-utils/src/test_cases.rs | 35 +++++++++++++++---- .../assets/westmint/src/xcm_config.rs | 6 ++-- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/parachains/common/src/impls.rs b/parachains/common/src/impls.rs index 40387617bcd..cc89a248e2e 100644 --- a/parachains/common/src/impls.rs +++ b/parachains/common/src/impls.rs @@ -95,6 +95,18 @@ where } } +/// Allow checking in assets that exists. +pub struct AssetExists(PhantomData<(AccountId, Assets)>); +impl Contains<>::AssetId> + for AssetExists +where + Assets: fungibles::Inspect, +{ + fn contains(id: &>::AssetId) -> bool { + Assets::asset_exists(*id) + } +} + /// Asset filter that allows all assets from a certain location. pub struct AssetsFrom(PhantomData); impl> ContainsPair for AssetsFrom { diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index ae1951c1c09..5cd168b6316 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -38,7 +38,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NonLocalMint, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -129,8 +129,8 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // TODO:check-parameter - no teleports - NoChecking, + // We only want to allow teleports of known assets. + NonLocalMint>, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 92a4f39b9e6..4b4d982d6db 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -378,21 +378,23 @@ pub fn teleports_for_foreign_assets_works< existential_deposit + (buy_execution_fee_amount * 2).into(), ), (target_account.clone(), existential_deposit), + (CheckingAccount::get(), existential_deposit), ]) .with_safe_xcm_version(XCM_VERSION) .with_para_id(runtime_para_id.into()) .with_tracing() .build() .execute_with(|| { - // checks before + // checks target_account before assert_eq!( >::free_balance(&target_account), existential_deposit ); assert_eq!( >::free_balance(&CheckingAccount::get()), - 0.into() + existential_deposit ); + // check `CheckingAccount` before assert_eq!( >::balance( foreign_asset_id_multilocation.into(), @@ -407,6 +409,13 @@ pub fn teleports_for_foreign_assets_works< ), 0.into() ); + // check total supply before + assert_eq!( + >::total_supply( + foreign_asset_id_multilocation.into() + ), + 0.into() + ); // create foreign asset (0 total issuance) let asset_minimum_asset_balance = 3333333_u128; @@ -468,7 +477,7 @@ pub fn teleports_for_foreign_assets_works< ); assert_eq!(outcome.ensure_complete(), Ok(())); - // checks after + // checks target_account after assert_eq!( >::free_balance(&target_account), existential_deposit @@ -480,22 +489,24 @@ pub fn teleports_for_foreign_assets_works< ), teleported_foreign_asset_amount.into() ); + // checks `CheckingAccount` after assert_eq!( >::free_balance(&CheckingAccount::get()), - 0.into() + existential_deposit ); assert_eq!( >::balance( foreign_asset_id_multilocation.into(), &CheckingAccount::get() ), - 0.into() + teleported_foreign_asset_amount.into() ); + // check total supply after (twice: target_account + CheckingAccount) assert_eq!( >::total_supply( foreign_asset_id_multilocation.into() ), - teleported_foreign_asset_amount.into() + (teleported_foreign_asset_amount + teleported_foreign_asset_amount).into() ); // 2. try to teleport asset back to source parachain (foreign_para_id) @@ -542,7 +553,17 @@ pub fn teleports_for_foreign_assets_works< foreign_asset_id_multilocation.into(), &CheckingAccount::get() ), - 0.into() + (target_account_balance_before_teleport - asset_to_teleport_away.into()) + ); + // check total supply after (twice: target_account + CheckingAccount) + assert_eq!( + >::total_supply( + foreign_asset_id_multilocation.into() + ), + (teleported_foreign_asset_amount - asset_to_teleport_away + + teleported_foreign_asset_amount - + asset_to_teleport_away) + .into() ); // check events diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 46f8ce9af10..41431d61925 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -39,7 +39,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NonLocalMint, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -128,8 +128,8 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // TODO:check-parameter - no teleports - NoChecking, + // We only want to allow teleports of known assets. + NonLocalMint>, // The account to use for tracking teleports. CheckingAccount, >; From 71d35771897c99347a8298f66a60c50c4146deac Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 23 Mar 2023 13:54:15 +0100 Subject: [PATCH 15/16] Changed `NonLocal` to `NoChecking` --- .../assets/statemine/src/xcm_config.rs | 6 +- .../runtimes/assets/test-utils/src/lib.rs | 24 +++- .../assets/test-utils/src/test_cases.rs | 111 +++++++++--------- .../assets/westmint/src/xcm_config.rs | 6 +- 4 files changed, 78 insertions(+), 69 deletions(-) diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 5cd168b6316..9484c27a6be 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -38,7 +38,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NonLocalMint, ParentAsSuperuser, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -129,8 +129,8 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We only want to allow teleports of known assets. - NonLocalMint>, + // We dont need to check teleports here. + NoChecking, // The account to use for tracking teleports. CheckingAccount, >; diff --git a/parachains/runtimes/assets/test-utils/src/lib.rs b/parachains/runtimes/assets/test-utils/src/lib.rs index 10f5e5a7fff..06d6282e008 100644 --- a/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/parachains/runtimes/assets/test-utils/src/lib.rs @@ -302,18 +302,30 @@ impl RuntimeH } } -pub fn assert_metadata( - asset_id: Assets::AssetId, +pub fn assert_metadata( + asset_id: impl Into + Copy, expected_name: &str, expected_symbol: &str, expected_decimals: u8, ) where - Assets: frame_support::traits::tokens::fungibles::metadata::Inspect + Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect + frame_support::traits::tokens::fungibles::Inspect, { - assert_eq!(Assets::name(asset_id), Vec::from(expected_name),); - assert_eq!(Assets::symbol(asset_id), Vec::from(expected_symbol),); - assert_eq!(Assets::decimals(asset_id), expected_decimals); + assert_eq!(Fungibles::name(asset_id.into()), Vec::from(expected_name),); + assert_eq!(Fungibles::symbol(asset_id.into()), Vec::from(expected_symbol),); + assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals); +} + +pub fn assert_total( + asset_id: impl Into + Copy, + expected_total_issuance: impl Into, + expected_active_issuance: impl Into, +) where + Fungibles: frame_support::traits::tokens::fungibles::metadata::Inspect + + frame_support::traits::tokens::fungibles::Inspect, +{ + assert_eq!(Fungibles::total_issuance(asset_id.into()), expected_total_issuance.into()); + assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into()); } /// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 4b4d982d6db..0f962f6341c 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -16,8 +16,8 @@ //! Module contains predefined test-case scenarios for `Runtime` with various assets. use crate::{ - assert_metadata, AccountIdOf, BalanceOf, ExtBuilder, RuntimeHelper, SessionKeysOf, - ValidatorIdOf, XcmReceivedFrom, + assert_metadata, assert_total, AccountIdOf, BalanceOf, ExtBuilder, RuntimeHelper, + SessionKeysOf, ValidatorIdOf, XcmReceivedFrom, }; use codec::Encode; use frame_support::{ @@ -409,31 +409,27 @@ pub fn teleports_for_foreign_assets_works< ), 0.into() ); - // check total supply before - assert_eq!( - >::total_supply( - foreign_asset_id_multilocation.into() - ), - 0.into() - ); + // check totals before + assert_total::< + pallet_assets::Pallet, + AccountIdOf, + >(foreign_asset_id_multilocation, 0, 0); // create foreign asset (0 total issuance) let asset_minimum_asset_balance = 3333333_u128; assert_ok!( >::force_create( RuntimeHelper::::root_origin(), - foreign_asset_id_multilocation.clone().into(), + foreign_asset_id_multilocation.into(), asset_owner.into(), false, asset_minimum_asset_balance.into() ) ); - assert_eq!( - >::total_supply( - foreign_asset_id_multilocation.clone().into() - ), - 0.into() - ); + assert_total::< + pallet_assets::Pallet, + AccountIdOf, + >(foreign_asset_id_multilocation, 0, 0); assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance); // 1. process received teleported assets from relaychain @@ -499,14 +495,16 @@ pub fn teleports_for_foreign_assets_works< foreign_asset_id_multilocation.into(), &CheckingAccount::get() ), - teleported_foreign_asset_amount.into() + 0.into() ); - // check total supply after (twice: target_account + CheckingAccount) - assert_eq!( - >::total_supply( - foreign_asset_id_multilocation.into() - ), - (teleported_foreign_asset_amount + teleported_foreign_asset_amount).into() + // check total after (twice: target_account + CheckingAccount) + assert_total::< + pallet_assets::Pallet, + AccountIdOf, + >( + foreign_asset_id_multilocation, + teleported_foreign_asset_amount, + teleported_foreign_asset_amount, ); // 2. try to teleport asset back to source parachain (foreign_para_id) @@ -553,17 +551,16 @@ pub fn teleports_for_foreign_assets_works< foreign_asset_id_multilocation.into(), &CheckingAccount::get() ), - (target_account_balance_before_teleport - asset_to_teleport_away.into()) + 0.into() ); - // check total supply after (twice: target_account + CheckingAccount) - assert_eq!( - >::total_supply( - foreign_asset_id_multilocation.into() - ), - (teleported_foreign_asset_amount - asset_to_teleport_away + - teleported_foreign_asset_amount - - asset_to_teleport_away) - .into() + // check total after (twice: target_account + CheckingAccount) + assert_total::< + pallet_assets::Pallet, + AccountIdOf, + >( + foreign_asset_id_multilocation, + teleported_foreign_asset_amount - asset_to_teleport_away, + teleported_foreign_asset_amount - asset_to_teleport_away, ); // check events @@ -658,11 +655,11 @@ pub fn asset_transactor_transfer_with_local_consensus_currency_works>::free_balance(source_account.clone()), + >::free_balance(&source_account), (BalanceOf::::from(10_u128) * unit) ); assert_eq!( - >::free_balance(target_account.clone()), + >::free_balance(&target_account), (BalanceOf::::zero() * unit) ); @@ -773,7 +770,7 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< <::Lookup as StaticLookup>::Source: From<::AccountId>, AssetsPalletInstance: 'static, - AssetId: Clone, + AssetId: Clone + Copy, AssetIdConverter: Convert, { ExtBuilder::::default() @@ -792,7 +789,7 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< let asset_id_as_multilocation = AssetIdConverter::reverse_ref(&asset_id).unwrap(); assert_ok!(>::force_create( RuntimeHelper::::root_origin(), - asset_id.clone().into(), + asset_id.into(), asset_owner.clone().into(), false, asset_minimum_asset_balance.into() @@ -801,7 +798,7 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< // We first mint enough asset for the account to exist for assets assert_ok!(>::mint( RuntimeHelper::::origin_of(asset_owner.clone()), - asset_id.clone().into(), + asset_id.into(), alice_account.clone().into(), (6 * asset_minimum_asset_balance).into() )); @@ -809,28 +806,28 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< // check Assets before assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &alice_account ), (6 * asset_minimum_asset_balance).into() ); assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &bob_account ), 0.into() ); assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &charlie_account ), 0.into() ); assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &asset_owner ), 0.into() @@ -899,21 +896,21 @@ pub fn asset_transactor_transfer_with_pallet_assets_instance_works< // check Assets after assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &alice_account ), (5 * asset_minimum_asset_balance).into() ); assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &bob_account ), (1 * asset_minimum_asset_balance).into() ); assert_eq!( >::balance( - asset_id.clone().into(), + asset_id.into(), &charlie_account ), 0.into() @@ -1044,7 +1041,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor <::Lookup as StaticLookup>::Source: From<::AccountId>, ForeignAssetsPalletInstance: 'static, - AssetId: Clone, + AssetId: Clone + Copy, AssetIdConverter: Convert, { // foreign parachain with the same consenus currency as asset @@ -1102,7 +1099,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Runtime, ForeignAssetsPalletInstance, >::create { - id: asset_id.clone().into(), + id: asset_id.into(), // admin as sovereign_account admin: foreign_creator_as_account_id.clone().into(), min_balance: 1.into(), @@ -1112,7 +1109,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Runtime, ForeignAssetsPalletInstance, >::set_metadata { - id: asset_id.clone().into(), + id: asset_id.into(), name: Vec::from(ASSET_NAME), symbol: Vec::from(ASSET_SYMBOL), decimals: 12, @@ -1122,7 +1119,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Runtime, ForeignAssetsPalletInstance, >::set_team { - id: asset_id.clone().into(), + id: asset_id.into(), issuer: foreign_creator_as_account_id.clone().into(), admin: foreign_creator_as_account_id.clone().into(), freezer: bob_account.clone().into(), @@ -1179,25 +1176,25 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor use frame_support::traits::tokens::fungibles::roles::Inspect as InspectRoles; assert_eq!( >::owner( - asset_id.clone().into() + asset_id.into() ), Some(foreign_creator_as_account_id.clone()) ); assert_eq!( >::admin( - asset_id.clone().into() + asset_id.into() ), Some(foreign_creator_as_account_id.clone()) ); assert_eq!( >::issuer( - asset_id.clone().into() + asset_id.into() ), Some(foreign_creator_as_account_id.clone()) ); assert_eq!( >::freezer( - asset_id.clone().into() + asset_id.into() ), Some(bob_account.clone()) ); @@ -1211,13 +1208,13 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor assert_metadata::< pallet_assets::Pallet, AccountIdOf, - >(asset_id.clone().into(), ASSET_NAME, ASSET_SYMBOL, 12); + >(asset_id, ASSET_NAME, ASSET_SYMBOL, 12); // check if changed freezer, can freeze assert_noop!( >::freeze( RuntimeHelper::::origin_of(bob_account), - asset_id.clone().into(), + asset_id.into(), alice_account.clone().into() ), pallet_assets::Error::::NoAccount @@ -1225,7 +1222,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor assert_noop!( >::freeze( RuntimeHelper::::origin_of(foreign_creator_as_account_id.clone()), - asset_id.clone().into(), + asset_id.into(), alice_account.into() ), pallet_assets::Error::::NoPermission @@ -1241,7 +1238,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor Runtime, ForeignAssetsPalletInstance, >::create { - id: asset_id.clone().into(), + id: asset_id.into(), // admin as sovereign_account admin: foreign_creator_as_account_id.clone().into(), min_balance: 1.into(), diff --git a/parachains/runtimes/assets/westmint/src/xcm_config.rs b/parachains/runtimes/assets/westmint/src/xcm_config.rs index 41431d61925..72e765380c5 100644 --- a/parachains/runtimes/assets/westmint/src/xcm_config.rs +++ b/parachains/runtimes/assets/westmint/src/xcm_config.rs @@ -39,7 +39,7 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, - FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NonLocalMint, ParentAsSuperuser, + FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, WeightInfoBounds, WithComputedOrigin, @@ -128,8 +128,8 @@ pub type ForeignFungiblesTransactor = FungiblesAdapter< LocationToAccountId, // Our chain's account ID type (we can't get away without mentioning it explicitly): AccountId, - // We only want to allow teleports of known assets. - NonLocalMint>, + // We dont need to check teleports here. + NoChecking, // The account to use for tracking teleports. CheckingAccount, >; From d371389c5bbeb2400ac7da26f41eb01ac880448a Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Thu, 23 Mar 2023 14:42:42 +0100 Subject: [PATCH 16/16] Fix weight in test --- parachains/runtimes/assets/test-utils/src/test_cases.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parachains/runtimes/assets/test-utils/src/test_cases.rs b/parachains/runtimes/assets/test-utils/src/test_cases.rs index 0f962f6341c..3614f03e7d9 100644 --- a/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -441,7 +441,7 @@ pub fn teleports_for_foreign_assets_works< id: Concrete(MultiLocation::parent()), fun: Fungible(buy_execution_fee_amount.into()), }, - weight_limit: Limited(Weight::from_parts(403531000, 1024)), + weight_limit: Limited(Weight::from_parts(403531000, 65536)), }, // Process teleported asset ReceiveTeleportedAsset(MultiAssets::from(vec![MultiAsset {