diff --git a/Cargo.lock b/Cargo.lock index f69325552b6d..1fde5a0474db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4699,6 +4699,19 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13370dae44474229701bb69b90b4f4dca6404cb0357a2d50d635f1171dc3aa7b" +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=master#4d28ebeb8b027ca0227fe7779c5beb70a7b56467" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-authority-discovery" version = "4.0.0-dev" @@ -5456,6 +5469,27 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "pallet-xcm-benchmarks" +version = "0.9.8" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "parity-scale-codec", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", + "xcm", + "xcm-builder", + "xcm-executor", +] + [[package]] name = "parity-db" version = "0.2.4" @@ -12118,6 +12152,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-parachain", "polkadot-primitives", @@ -12276,6 +12311,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", + "log", "pallet-transaction-payment", "parity-scale-codec", "polkadot-parachain", diff --git a/Cargo.toml b/Cargo.toml index 668b61db7ee0..57b1fd679301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ members = [ "xcm/xcm-builder", "xcm/xcm-executor", "xcm/pallet-xcm", + "xcm/pallet-xcm-benchmarks", "node/client", "node/collation-generation", "node/core/approval-voting", diff --git a/node/core/av-store/src/lib.rs b/node/core/av-store/src/lib.rs index 09a3a28fed5f..754e8ce51ba6 100644 --- a/node/core/av-store/src/lib.rs +++ b/node/core/av-store/src/lib.rs @@ -16,7 +16,7 @@ //! Implements a `AvailabilityStoreSubsystem`. -#![recursion_limit="256"] +#![recursion_limit = "256"] #![warn(missing_docs)] use std::collections::{HashMap, HashSet, BTreeSet}; diff --git a/node/core/bitfield-signing/src/lib.rs b/node/core/bitfield-signing/src/lib.rs index 1edc89886a3b..c745da9e57f2 100644 --- a/node/core/bitfield-signing/src/lib.rs +++ b/node/core/bitfield-signing/src/lib.rs @@ -18,7 +18,7 @@ #![deny(unused_crate_dependencies)] #![warn(missing_docs)] -#![recursion_limit="256"] +#![recursion_limit = "256"] use futures::{channel::{mpsc, oneshot}, lock::Mutex, prelude::*, future, Future}; use sp_keystore::{Error as KeystoreError, SyncCryptoStorePtr}; diff --git a/node/network/collator-protocol/src/lib.rs b/node/network/collator-protocol/src/lib.rs index 96af19aa1e58..029274f403d7 100644 --- a/node/network/collator-protocol/src/lib.rs +++ b/node/network/collator-protocol/src/lib.rs @@ -18,7 +18,7 @@ //! This subsystem implements both sides of the collator protocol. #![deny(missing_docs, unused_crate_dependencies)] -#![recursion_limit="256"] +#![recursion_limit = "256"] use std::time::Duration; diff --git a/parachain/test-parachains/adder/src/lib.rs b/parachain/test-parachains/adder/src/lib.rs index 37208efbca1b..06a705c3557b 100644 --- a/parachain/test-parachains/adder/src/lib.rs +++ b/parachain/test-parachains/adder/src/lib.rs @@ -17,8 +17,10 @@ //! Basic parachain that adds a number as part of its state. #![no_std] - -#![cfg_attr(not(feature = "std"), feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler))] +#![cfg_attr( + not(feature = "std"), + feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler) +)] use parity_scale_codec::{Encode, Decode}; use tiny_keccak::{Hasher as _, Keccak}; diff --git a/parachain/test-parachains/halt/src/lib.rs b/parachain/test-parachains/halt/src/lib.rs index 00314033a4dd..b0a44bca3415 100644 --- a/parachain/test-parachains/halt/src/lib.rs +++ b/parachain/test-parachains/halt/src/lib.rs @@ -17,7 +17,10 @@ //! Basic parachain that executes forever. #![no_std] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler))] +#![cfg_attr( + not(feature = "std"), + feature(core_intrinsics, lang_items, core_panic_info, alloc_error_handler) +)] // Make the WASM binary available. #[cfg(feature = "std")] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 82a5e7ca2e03..1c549da94279 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -17,7 +17,6 @@ //! Polkadot types shared between the runtime and the Node-side code. #![warn(missing_docs)] - #![cfg_attr(not(feature = "std"), no_std)] pub mod v0; diff --git a/runtime/kusama/src/weights/mod.rs b/runtime/kusama/src/weights/mod.rs index a83b57bfcdf8..ebe810d62907 100644 --- a/runtime/kusama/src/weights/mod.rs +++ b/runtime/kusama/src/weights/mod.rs @@ -20,8 +20,8 @@ pub mod pallet_balances; pub mod pallet_bounties; pub mod pallet_collective; pub mod pallet_democracy; -pub mod pallet_elections_phragmen; pub mod pallet_election_provider_multi_phase; +pub mod pallet_elections_phragmen; pub mod pallet_gilt; pub mod pallet_identity; pub mod pallet_im_online; diff --git a/runtime/westend/Cargo.toml b/runtime/westend/Cargo.toml index 090b45006af3..7c590c469915 100644 --- a/runtime/westend/Cargo.toml +++ b/runtime/westend/Cargo.toml @@ -69,6 +69,7 @@ pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "m pallet-utility = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-xcm = { path = "../../xcm/pallet-xcm", default-features = false } +pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-try-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } @@ -199,6 +200,7 @@ runtime-benchmarks = [ "frame-system-benchmarking", "hex-literal", "xcm-builder/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", "frame-election-provider-support/runtime-benchmarks", ] try-runtime = [ diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 82fe6e70900b..93ffa5441bde 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1452,6 +1452,26 @@ sp_api::impl_runtime_apis! { impl pallet_offences_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} + use pallet_xcm_benchmarks::AsFungibles; + use xcm::v0::MultiAsset; + + impl pallet_xcm_benchmarks::Config for Runtime { + type XcmConfig = XcmConfig; + type FungibleTransactAsset = Balances; + + fn fungible_asset(amount: u32) -> Option<(MultiAsset, u128)> { + let amount: Balance = amount.into(); + let amount = ExistentialDeposit::get() * amount; + Some((MultiAsset::ConcreteFungible { id: WndLocation::get(), amount }, amount)) + } + + // We pass in this adapter for the fungibles stuff, so our weights for fungibles + // will be quite similar to fungible, understandably. We don't need these weights + // anyways, westend does not have or use fungibles.. + type FungiblesTransactAsset = AsFungibles; + } + type XcmPalletBenchmarks= pallet_xcm_benchmarks::Pallet::; + let whitelist: Vec = vec![ // Block Number hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), @@ -1493,6 +1513,7 @@ sp_api::impl_runtime_apis! { add_benchmark!(params, batches, pallet_timestamp, Timestamp); add_benchmark!(params, batches, pallet_utility, Utility); add_benchmark!(params, batches, pallet_vesting, Vesting); + add_benchmark!(params, batches, pallet_xcm_benchmarks, XcmPalletBenchmarks); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } let storage_info = AllPalletsWithSystem::storage_info(); diff --git a/xcm/Cargo.toml b/xcm/Cargo.toml index 66d565efde4e..8f6ef6584a43 100644 --- a/xcm/Cargo.toml +++ b/xcm/Cargo.toml @@ -14,6 +14,7 @@ log = { version = "0.4.14", default-features = false } [features] default = ["std"] wasm-api = [] +runtime-benchmarks = [] std = [ "parity-scale-codec/std", ] diff --git a/xcm/pallet-xcm-benchmarks/Cargo.toml b/xcm/pallet-xcm-benchmarks/Cargo.toml new file mode 100644 index 000000000000..f294a268d37f --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "pallet-xcm-benchmarks" +authors = ["Parity Technologies "] +edition = "2018" +version = "0.9.8" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +frame-support = { version = "4.0.0-dev", default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" } +frame-system = { version = "4.0.0-dev", default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" } +sp-runtime = { version = "4.0.0-dev", default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" } +sp-std = { version = "4.0.0-dev", default-features = false, branch = "master", git = "https://github.com/paritytech/substrate" } +xcm-executor = { path = "../xcm-executor", default-features = false } + +# Benchmarks dependencies. +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, branch = "master", git = "https://github.com/paritytech/substrate" } +xcm = { path = "..", default-features = false, optional = true } + +[dev-dependencies] +pallet-balances = { version = "4.0.0-dev", branch = "master", git = "https://github.com/paritytech/substrate" } +pallet-assets = { version = "4.0.0-dev", branch = "master", git = "https://github.com/paritytech/substrate" } +sp-core = { version = "4.0.0-dev", branch = "master", git = "https://github.com/paritytech/substrate" } +sp-io = { version = "4.0.0-dev", branch = "master", git = "https://github.com/paritytech/substrate" } +sp-tracing = { version = "4.0.0-dev", branch = "master", git = "https://github.com/paritytech/substrate" } +xcm-builder = { path = "../xcm-builder" } +xcm = { path = ".." } +log = "0.4.0" + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std" +] +runtime-benchmarks = [ + "frame-benchmarking", + "xcm/runtime-benchmarks" +] diff --git a/xcm/pallet-xcm-benchmarks/src/benchmarking.rs b/xcm/pallet-xcm-benchmarks/src/benchmarking.rs new file mode 100644 index 000000000000..cf02aef7d98c --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/src/benchmarking.rs @@ -0,0 +1,421 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use crate::*; +use codec::Encode; +use frame_benchmarking::benchmarks; +use frame_support::{ + assert_ok, + traits::{fungible::Inspect as FungibleInspect, fungibles::Inspect as FungiblesInspect}, + weights::Weight, +}; +use sp_runtime::traits::Zero; +use sp_std::{convert::TryInto, prelude::*, vec}; +use xcm::{ + opaque::v0::{AssetInstance, ExecuteXcm, Junction, MultiAsset, MultiLocation, NetworkId}, + v0::{Error as XcmError, Order, Outcome, Xcm}, +}; +use xcm_executor::{traits::TransactAsset, Assets}; + +/// The xcm executor to use for doing stuff. +pub type ExecutorOf = xcm_executor::XcmExecutor<::XcmConfig>; +/// The asset transactor of our executor +pub type AssetTransactorOf = <::XcmConfig as xcm_executor::Config>::AssetTransactor; +/// The overarching call type. +pub type OverArchingCallOf = ::Call; +/// The call type of executor's config. Should eventually resolve to the same overarching call type. +pub type XcmCallOf = <::XcmConfig as xcm_executor::Config>::Call; + +// TODO: def. needs to be become a config, might also want to use bounded vec. +const MAX_ASSETS: u32 = 25; +const SEED: u32 = 0; + +/// The number of fungible assets in the holding. +const HOLDING_FUNGIBLES: u32 = 99; +const HOLDING_NON_FUNGIBLES: u32 = 99; + +fn create_holding( + fungibles_count: u32, + fungibles_amount: u128, + non_fungibles_count: u32, +) -> Assets { + (0..fungibles_count) + .map(|i| { + MultiAsset::ConcreteFungible { + id: MultiLocation::X1(Junction::GeneralIndex { id: i as u128 }), + amount: fungibles_amount * i as u128, + } + .into() + }) + .chain((0..non_fungibles_count).map(|i| { + MultiAsset::ConcreteNonFungible { + class: MultiLocation::X1(Junction::GeneralIndex { id: i as u128 }), + instance: asset_instance_from(i), + } + })) + .collect::>() + .into() +} + +fn asset_instance_from(x: u32) -> AssetInstance { + let bytes = x.encode(); + let mut instance = [0u8; 4]; + instance.copy_from_slice(&bytes); + AssetInstance::Array4(instance) +} + +/// wrapper to execute single order. Can be any hack, for now we just do a noop-xcm with a single +/// order. +fn execute_order( + origin: MultiLocation, + mut holding: Assets, + order: Order>, +) -> Result { + ExecutorOf::::do_execute_effects(&origin, &mut holding, order) +} + +/// Execute an xcm. +fn execute_xcm(origin: MultiLocation, xcm: Xcm>) -> Outcome { + ExecutorOf::::execute_xcm(origin, xcm, 999_999_999_999) // TODO: very large weight to ensure all benchmarks execute, sensible? +} + +fn account(index: u32) -> T::AccountId { + frame_benchmarking::account::("account", index, SEED) +} + +/// Build a multi-location from an account id. +fn account_id_junction(index: u32) -> Junction { + let account = account::(index); + let mut encoded = account.encode(); + encoded.resize(32, 0u8); + let mut id = [0u8; 32]; + id.copy_from_slice(&encoded); + Junction::AccountId32 { network: NetworkId::Any, id } +} + +// Thoughts: +// +// All XCMs should have all of their internals as parameter, regardless of it being used or not. +// This is because some implementations might need them and depend upon them, and some might not. + +// Rationale: +// +// Benchmarks ending with _fungible typically indicate the case where there is only one asset in +// the order/xcm, and it is fungible. Typically, an order/xcm that is being weighed with such a +// benchmark will have a `asset: Vec<_>` of length one. +// +// Benchmarks ending with fungibles_per_asset imply that this benchmark is for the case of a chain +// with multiple asset types (thus the name `fungibles`). Such a benchmark will still only work +// with one asset, and it is meant to be summed +benchmarks! { + where_clause { where + T::XcmConfig: xcm_executor::Config>, + >::AssetId: From, + < + < + ::FungibleTransactAsset + as + FungibleInspect<::AccountId> + >::Balance + as + TryInto + >::Error: sp_std::fmt::Debug, + < + < + ::FungiblesTransactAsset + as + FungiblesInspect<::AccountId> + >::Balance + as + TryInto + >::Error: sp_std::fmt::Debug, + } + + // a xcm-send operation. This is useful for effects of an order. + send_xcm {}: {} + + // orders. + order_null { + let order = Order::>::Null; + let origin = MultiLocation::X1(account_id_junction::(1)); + let holding = Assets::default(); + }: { + assert_ok!(execute_order::(origin, holding, order)); + } + + order_deposit_asset_fungible { + let origin = MultiLocation::X1(account_id_junction::(1)); + let (holding, order) = if let Some((asset, amount)) = T::fungible_asset(1) { + let order = Order::>::DepositAsset { + assets: vec![asset.clone()], + dest: MultiLocation::X1(account_id_junction::(77)), + }; + + // generate the holding with a bunch of stuff.. + let mut holding = create_holding(HOLDING_FUNGIBLES, amount, HOLDING_NON_FUNGIBLES); + // .. and the specific asset that we want to take out. + holding.saturating_subsume(asset); + assert!(T::FungibleTransactAsset::balance(&account::(77)).is_zero()); + + (holding, order) + } else { + // just put some mock values in there. + (Default::default(), Order::>::Null) + }; + }: { + if T::fungible_asset(1).is_some() { + assert_ok!(execute_order::(origin, holding, order)); + } + } verify { + if T::fungible_asset(1).is_some() { + assert!(!T::FungibleTransactAsset::balance(&account::(77)).is_zero()) + } + } + + order_deposit_asset_fungibles_per_asset { + // create one asset with our desired id. + let asset_id = 9; + let origin = MultiLocation::X1(account_id_junction::(1)); + + let (holding, order) = if let Some((asset, amount)) = T::fungibles_asset(1, asset_id) { + let order = Order::>::DepositAsset { + assets: vec![ asset.clone() ], + dest: MultiLocation::X1(account_id_junction::(2)), + }; + let mut holding: Assets = create_holding(HOLDING_FUNGIBLES, amount, HOLDING_NON_FUNGIBLES); + holding.saturating_subsume(asset); + assert!(T::FungibleTransactAsset::balance(&account::(2)).is_zero()); + + (holding, order) + } else { + (Default::default(), Order::>::Null) + }; + }: { + if T::fungibles_asset(1, asset_id).is_some() { + assert_ok!(execute_order::(origin, holding, order)); + } + } verify { + if T::fungibles_asset(1, asset_id).is_some() { + assert!(!T::FungiblesTransactAsset::balance(asset_id.into(), &account::(2)).is_zero()) + } + } + + order_deposit_reserved_asset_fungible {}: {} verify {} + order_deposit_reserved_asset_fungibles {}: {} verify {} + + order_exchange_asset_fungible {}: {} verify {} + order_exchange_asset_fungibles {}: {} verify {} + + order_initiate_reserve_withdraw_fungible {}: {} verify {} + order_initiate_reserve_withdraw_fungibles {}: {} verify {} + + order_initiate_teleport_fungible {}: {} verify {} + order_initiate_teleport_fungibles {}: {} verify {} + + order_query_holding_fungible {}: {} verify {} + order_query_holding_fungibles {}: {} verify {} + + order_buy_execution_fungible {}: {} verify {} + order_buy_execution_fungibles {}: {} verify {} + + + // Xcm::WithdrawAsset notes: There are two components to this benchmark: + // 1. based on the asset type, the complexity of withdraw could be different. + // 2. based on the length of the assets being withdraw, the complexity of insertion into the + // holding increases per operation. + // + // The worse case of the latter is if all assets are of one type (fungible, or non-fungible), + // since that will incur the maximum insertion cost. + // + // The former entirely depends on the asset type. + xcm_withdraw_asset_fungible { + let origin: MultiLocation = (account_id_junction::(1)).into(); + let xcm = if T::fungibles_asset(0, 0).is_some() { + let (asset, _) = T::fungible_asset(1).unwrap(); + >::deposit_asset(&asset, &origin).unwrap(); + // check one of the assets of origin. + assert!(!T::FungibleTransactAsset::balance(&account::(1)).is_zero()); + Xcm::WithdrawAsset::> { assets: vec![asset], effects: vec![] } + } else { + Xcm::TransferAsset { assets: Default::default(), dest: origin.clone() } + }; + }: { + if T::fungible_asset(0).is_some() { + assert_ok!(execute_xcm::(origin, xcm).ensure_complete()); + } + } verify { + if T::fungible_asset(0).is_some() { + // check one of the assets of origin. + assert!(T::FungibleTransactAsset::balance(&account::(1)).is_zero()); + } + } + xcm_withdraw_asset_fungibles { + // number of fungible assets, regardless of `fungible` + let a in 0..MAX_ASSETS; + // number of non-fungible assets. + let b in 0..0; // TODO: make this work. + + let origin: MultiLocation = (account_id_junction::(1)).into(); + let xcm = if T::fungibles_asset(0, 0).is_some() { + let assets = (1..=a).map(|i| { + let (asset, _) = T::fungibles_asset(i, i).unwrap(); + >::deposit_asset(&asset, &origin).unwrap(); + asset + }) + .chain((0..b).map(|i| unreachable!())) + .collect::>(); + + if a > 0 { + // check one of the assets of origin. + assert!(!T::FungiblesTransactAsset::balance(1u32.into(), &account::(1)).is_zero()); + } + + Xcm::WithdrawAsset::> { assets, effects: vec![] } + } else { + Xcm::TransferAsset { assets: Default::default(), dest: origin.clone() } + }; + }: { + if T::fungibles_asset(0, 0).is_some() { + assert_ok!(execute_xcm::(origin, xcm).ensure_complete()); + } + } verify { + if T::fungibles_asset(0, 0).is_some() { + // check one of the assets of origin. All assets must have been withdrawn. + assert!(T::FungiblesTransactAsset::balance(1u32.into(), &account::(1)).is_zero()); + } + } + + xcm_reserve_asset_deposit {}: {} verify {} + xcm_teleport_asset {}: {} verify {} + xcm_query_response {}: {} verify {} + xcm_transfer_asset_fungible { + let origin: MultiLocation = (account_id_junction::(1)).into(); + let dest = account_id_junction::(2).into(); + + let xcm = if let Some((asset, amount)) = T::fungible_asset(1) { + >::deposit_asset(&asset, &origin).unwrap(); + let assets = vec![ asset ]; + assert!(T::FungibleTransactAsset::balance(&account::(2)).is_zero()); + Xcm::TransferAsset { assets, dest } + } else { + Xcm::TransferAsset { assets: Default::default(), dest } + }; + }: { + if T::fungible_asset(1).is_some() { + assert_ok!(execute_xcm::(origin, xcm).ensure_complete()); + } + } verify { + if T::fungible_asset(1).is_some() { + assert!(!T::FungibleTransactAsset::balance(&account::(2)).is_zero()); + } + } + xcm_transfer_asset_fungibles_per_asset { + let origin: MultiLocation = (account_id_junction::(1)).into(); + let dest = (account_id_junction::(2)).into(); + let asset_id = 9; + + let xcm = if let Some((asset, amount)) = T::fungibles_asset(1, asset_id) { + // Note that we deposit a new asset with twice the amount into the sender to prevent it + // being dying. + >::deposit_asset( + &T::fungibles_asset(2, asset_id) + .expect("call to fungibles_asset has already returned `Some`, this must work") + .0, + &origin + ).unwrap(); + + assert!(T::FungiblesTransactAsset::balance(asset_id.into(), &account::(2)).is_zero()); + assert!(!T::FungiblesTransactAsset::balance(asset_id.into(), &account::(1)).is_zero()); + + Xcm::TransferAsset { assets: vec![asset], dest } + } else { + Xcm::TransferAsset { assets: Default::default(), dest } + }; + }: { + if T::fungibles_asset(0, 0).is_some() { + assert_ok!(execute_xcm::(origin, xcm).ensure_complete()); + } + } verify { + if T::fungibles_asset(0, 0).is_some() { + assert!(!T::FungiblesTransactAsset::balance(asset_id.into(), &account::(2)).is_zero()); + } + } + + xcm_transfer_reserved_asset_fungible {}: {} verify {} + xcm_transfer_reserved_asset_fungibles {}: {} verify {} + + xcm_transact {}: {} verify {} + xcm_hrmp_channel_open_request {}: {} verify {} + xcm_hrmp_channel_accepted {}: {} verify {} + xcm_hrmp_channel_closing {}: {} verify {} + xcm_relayed_from {}: {} verify {} +} + +#[cfg(test)] +mod benchmark_tests { + use super::*; + + #[test] + fn order_deposit_asset_fungible() { + crate::mock_fungible::new_test_ext().execute_with(|| { + test_bench_by_name::(b"order_deposit_asset_fungible") + .unwrap(); + }) + } + + #[test] + fn order_deposit_asset_fungibles_per_asset() { + crate::mock_fungibles::new_test_ext().execute_with(|| { + test_bench_by_name::( + b"order_deposit_asset_fungibles_per_asset", + ) + .unwrap(); + }) + } + + #[test] + fn xcm_transfer_asset_fungible() { + crate::mock_fungible::new_test_ext().execute_with(|| { + test_bench_by_name::(b"xcm_transfer_asset_fungible") + .unwrap(); + }) + } + #[test] + fn xcm_transfer_asset_fungibles_per_asset() { + crate::mock_fungibles::new_test_ext().execute_with(|| { + test_bench_by_name::( + b"xcm_transfer_asset_fungibles_per_asset", + ) + .unwrap(); + }) + } + + #[test] + fn xcm_withdraw_asset_fungible() { + crate::mock_fungible::new_test_ext().execute_with(|| { + test_bench_by_name::(b"xcm_withdraw_asset_fungible") + .unwrap(); + }) + } + + #[test] + fn xcm_withdraw_asset_fungibles() { + crate::mock_fungibles::new_test_ext().execute_with(|| { + test_bench_by_name::(b"xcm_withdraw_asset_fungibles") + .unwrap(); + }) + } +} diff --git a/xcm/pallet-xcm-benchmarks/src/lib.rs b/xcm/pallet-xcm-benchmarks/src/lib.rs new file mode 100644 index 000000000000..9f4e97147183 --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -0,0 +1,126 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Pallet that serves no other purpose than benchmarking raw messages [`Xcm`]. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +use xcm::v0::MultiAsset; + +#[cfg(test)] +mod mock_fungible; +#[cfg(test)] +mod mock_fungibles; +#[cfg(test)] +mod mock_shared; + +mod benchmarking; +pub mod weights; +// pub use weights::*; + +#[frame_support::pallet] +pub mod pallet { + use crate::MultiAsset; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The XCM configurations. + /// + /// These might affect the execution of XCM messages, such as defining how the + /// `TransactAsset` is implemented. + type XcmConfig: xcm_executor::Config; + + /// Give me a fungible asset that your asset transactor is going to accept. Give me none if + /// you don't really want to support this type of asset. + /// + /// A fungible asset always has an amount, return that too. + fn fungible_asset(_amount: u32) -> Option<(MultiAsset, u128)> { + None + } + /// Same as `fungible_asset`, but for an asset of multiple instances. + fn fungibles_asset(_amount: u32, _id: u32) -> Option<(MultiAsset, u128)> { + None + } + + type FungibleTransactAsset: frame_support::traits::fungible::Inspect; + type FungiblesTransactAsset: frame_support::traits::fungibles::Inspect; + // type NonFungibleTransactAsset: traits::tokens::nonfungible::Inspect; + // type NonFungiblesTransactAsset: traits::tokens::nonfungibles::Inspect; + } + + // transact asset that works with balances and asset + // + // transact asset that works with 3 assets + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); +} + +// With this, we measure all weights per asset, so NONE of the benchmarks need to have a component +// that is the number of assets, that's pretty pointless. You need to iterate the `Vec` +// down the road +enum AssetTransactorType { + Fungible, // Balances + Fungibles, // Assets, + NonFungible, // Uniques +} + +trait IdentifyAsset { + fn identify_asset(asset: MultiAsset) -> R; +} + +use frame_support::traits::{ + fungible::Inspect as FungibleInspect, + fungibles::Inspect as FungiblesInspect, + tokens::{DepositConsequence, WithdrawConsequence}, +}; + +pub struct AsFungibles(sp_std::marker::PhantomData<(AccountId, AssetId, B)>); +impl< + AccountId: sp_runtime::traits::Member + frame_support::dispatch::Parameter, + AssetId: sp_runtime::traits::Member + frame_support::dispatch::Parameter + Copy, + B: FungibleInspect, + > FungiblesInspect for AsFungibles +{ + type AssetId = AssetId; + type Balance = B::Balance; + + fn total_issuance(_: Self::AssetId) -> Self::Balance { + B::total_issuance() + } + fn minimum_balance(_: Self::AssetId) -> Self::Balance { + B::minimum_balance() + } + fn balance(_: Self::AssetId, who: &AccountId) -> Self::Balance { + B::balance(who) + } + fn reducible_balance(_: Self::AssetId, who: &AccountId, keep_alive: bool) -> Self::Balance { + B::reducible_balance(who, keep_alive) + } + fn can_deposit(_: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DepositConsequence { + B::can_deposit(who, amount) + } + + fn can_withdraw( + _: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + B::can_withdraw(who, amount) + } +} diff --git a/xcm/pallet-xcm-benchmarks/src/mock_fungible.rs b/xcm/pallet-xcm-benchmarks/src/mock_fungible.rs new file mode 100644 index 000000000000..f733d861f6b6 --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/src/mock_fungible.rs @@ -0,0 +1,157 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! A mock runtime for xcm benchmarking. + +use crate as pallet_xcm_benchmarks; +use crate::mock_shared::*; +use frame_support::parameter_types; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; +use xcm::opaque::v0::{MultiAsset, MultiLocation}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// For testing the pallet, we construct a mock runtime. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + XcmPalletBenchmarks: pallet_xcm_benchmarks::{Pallet}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(1024); +} +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::AllowAll; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = Call; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 7; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDeposit: u64 = 100 * ExistentialDeposit::get(); + pub const ApprovalDeposit: u64 = 1 * ExistentialDeposit::get(); + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 10 * ExistentialDeposit::get(); + pub const MetadataDepositPerByte: u64 = 1 * ExistentialDeposit::get(); +} + +pub struct MatchAnyFungible; +impl xcm_executor::traits::MatchesFungible for MatchAnyFungible { + fn matches_fungible(m: &MultiAsset) -> Option { + use sp_runtime::traits::SaturatedConversion; + match m { + MultiAsset::ConcreteFungible { amount, .. } => Some((*amount).saturated_into::()), + _ => None, + } + } +} + +parameter_types! { + pub const CheckedAccount: Option = Some(100); +} + +// Use balances as the asset transactor. +pub type AssetTransactor = xcm_builder::CurrencyAdapter< + Balances, + MatchAnyFungible, + AccountIdConverter, + u64, + CheckedAccount, +>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = DevNull; + type AssetTransactor = AssetTransactor; + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); + type LocationInverter = xcm_builder::LocationInverter; + type Barrier = YesItShould; + type Weigher = xcm_builder::FixedWeightBounds; + type Trader = xcm_builder::FixedRateOfConcreteFungible; + type ResponseHandler = DevNull; +} + +impl pallet_xcm_benchmarks::Config for Test { + type XcmConfig = XcmConfig; + + fn fungible_asset(amount: u32) -> Option<(MultiAsset, u128)> { + let amount = >::minimum_balance() + as u128 * amount as u128; + Some((MultiAsset::ConcreteFungible { id: MultiLocation::Null, amount }, amount)) + } + + type FungibleTransactAsset = Balances; + type FungiblesTransactAsset = crate::AsFungibles; +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = GenesisConfig { ..Default::default() }.build_storage().unwrap(); + sp_tracing::try_init_simple(); + t.into() +} diff --git a/xcm/pallet-xcm-benchmarks/src/mock_fungibles.rs b/xcm/pallet-xcm-benchmarks/src/mock_fungibles.rs new file mode 100644 index 000000000000..1f072f960960 --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/src/mock_fungibles.rs @@ -0,0 +1,205 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! A mock runtime for xcm benchmarking. + +use crate as pallet_xcm_benchmarks; +use crate::{mock_shared::*, *}; +use frame_support::{parameter_types, traits::Contains}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup, Zero}, + BuildStorage, +}; +use xcm::{ + opaque::v0::{MultiAsset, MultiLocation}, + v0::Junction, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// For testing the pallet, we construct a mock runtime. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + XcmPalletBenchmarks: pallet_xcm_benchmarks::{Pallet}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(1024); +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::AllowAll; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = Call; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 7; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); +} + +parameter_types! { + pub const AssetDeposit: u64 = 100 * ExistentialDeposit::get(); + pub const ApprovalDeposit: u64 = 1 * ExistentialDeposit::get(); + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 10 * ExistentialDeposit::get(); + pub const MetadataDepositPerByte: u64 = 1 * ExistentialDeposit::get(); +} + +impl pallet_assets::Config for Test { + type Event = Event; + type Balance = u64; + type AssetId = u32; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type AssetDeposit = AssetDeposit; + type MetadataDepositBase = MetadataDepositBase; + type MetadataDepositPerByte = MetadataDepositPerByte; + type ApprovalDeposit = ApprovalDeposit; + type StringLimit = StringLimit; + type Freezer = (); + type Extra = (); + type WeightInfo = (); +} + +pub struct CheckAsset; +impl Contains for CheckAsset { + fn contains(_: &u32) -> bool { + true + } +} + +pub struct MatchAnyFungibles; +impl xcm_executor::traits::MatchesFungibles for MatchAnyFungibles { + fn matches_fungibles(m: &MultiAsset) -> Result<(u32, u64), xcm_executor::traits::Error> { + // ^^ TODO: this error is too out of scope. + use sp_runtime::traits::SaturatedConversion; + match m { + MultiAsset::ConcreteFungible { + amount, + id: MultiLocation::X1(Junction::GeneralIndex { id }), + } => Ok(((*id).saturated_into(), (*amount).saturated_into::())), + _ => Err(xcm_executor::traits::Error::AssetNotFound), + } + } +} + +parameter_types! { + pub const CheckedAccount: u64 = 100; +} + +pub type AssetTransactor = xcm_builder::FungiblesAdapter< + Assets, + MatchAnyFungibles, + AccountIdConverter, + u64, + CheckAsset, + CheckedAccount, +>; + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type Call = Call; + type XcmSender = DevNull; + type AssetTransactor = AssetTransactor; + type OriginConverter = (); + type IsReserve = (); + type IsTeleporter = (); // no one can teleport. + type LocationInverter = xcm_builder::LocationInverter; + type Barrier = YesItShould; + type Weigher = xcm_builder::FixedWeightBounds; + type Trader = xcm_builder::FixedRateOfConcreteFungible; + type ResponseHandler = DevNull; +} + +use frame_support::traits::fungibles::Inspect; +impl pallet_xcm_benchmarks::Config for Test { + type XcmConfig = XcmConfig; + + fn fungibles_asset(amount: u32, id: u32) -> Option<(MultiAsset, u128)> { + // create this asset, if it does not exists. + if >::minimum_balance(id).is_zero() { + assert!(!ExistentialDeposit::get().is_zero()); + let root = frame_system::RawOrigin::Root.into(); + assert!(Assets::force_create(root, id, 777, true, ExistentialDeposit::get(),).is_ok()); + assert!(!>::minimum_balance(id).is_zero()); + } + + let amount = >::minimum_balance(id) as u128 * amount as u128; + Some(( + MultiAsset::ConcreteFungible { + id: MultiLocation::X1(Junction::GeneralIndex { id: id.into() }), + amount, + }, + amount, + )) + } + + type FungibleTransactAsset = Balances; + type FungiblesTransactAsset = Assets; +} + +// This function basically just builds a genesis storage key/value store according to +// our desired mockup. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = GenesisConfig { ..Default::default() }.build_storage().unwrap(); + sp_tracing::try_init_simple(); + t.into() +} diff --git a/xcm/pallet-xcm-benchmarks/src/mock_shared.rs b/xcm/pallet-xcm-benchmarks/src/mock_shared.rs new file mode 100644 index 000000000000..19e30a9f7a39 --- /dev/null +++ b/xcm/pallet-xcm-benchmarks/src/mock_shared.rs @@ -0,0 +1,70 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use crate::*; +use frame_support::{parameter_types, weights::Weight}; +use xcm::opaque::v0::{prelude::XcmResult, Junction, MultiLocation, Response, Xcm}; + +pub struct YesItShould(sp_std::marker::PhantomData); +impl xcm_executor::traits::ShouldExecute for YesItShould { + fn should_execute( + _: &MultiLocation, + _: bool, + _: &xcm::v0::Xcm, + _: Weight, + _: &mut Weight, + ) -> Result<(), ()> { + Ok(()) + } +} + +// An xcm sender/receiver akin to > /dev/null +pub struct DevNull; +impl xcm::opaque::v0::SendXcm for DevNull { + fn send_xcm(_: MultiLocation, _: Xcm) -> XcmResult { + Ok(()) + } +} + +impl xcm_executor::traits::OnResponse for DevNull { + fn expecting_response(_: &MultiLocation, _: u64) -> bool { + false + } + fn on_response(_: MultiLocation, _: u64, _: Response) -> Weight { + 0 + } +} + +pub struct AccountIdConverter; +impl xcm_executor::traits::Convert for AccountIdConverter { + fn convert(ml: MultiLocation) -> Result { + match ml { + MultiLocation::X1(Junction::AccountId32 { id, .. }) => + Ok(::decode(&mut &*id.to_vec()).unwrap()), + _ => Err(ml), + } + } + + fn reverse(acc: u64) -> Result { + Err(acc) + } +} + +parameter_types! { + pub Ancestry: MultiLocation = MultiLocation::X1(Junction::Parachain(101)); + pub UnitWeightCost: Weight = 10; + pub WeightPrice: (MultiLocation, u128) = (MultiLocation::Null, 1_000_000_000_000); +} diff --git a/xcm/src/v0/mod.rs b/xcm/src/v0/mod.rs index 05aad9581b05..cd34812fe491 100644 --- a/xcm/src/v0/mod.rs +++ b/xcm/src/v0/mod.rs @@ -16,31 +16,37 @@ //! Version 0 of the Cross-Consensus Message format data structures. -use core::{result, convert::TryFrom, fmt::Debug}; -use derivative::Derivative; +use crate::{DoubleEncoded, VersionedMultiAsset, VersionedXcm}; use alloc::vec::Vec; -use parity_scale_codec::{self, Encode, Decode}; -use crate::{VersionedMultiAsset, DoubleEncoded, VersionedXcm}; +use core::{convert::TryFrom, fmt::Debug, result}; +use derivative::Derivative; +use parity_scale_codec::{self, Decode, Encode}; mod junction; mod multi_asset; mod multi_location; mod order; mod traits; -pub use junction::{Junction, NetworkId, BodyId, BodyPart}; -pub use multi_asset::{MultiAsset, AssetInstance}; +pub use junction::{BodyId, BodyPart, Junction, NetworkId}; +pub use multi_asset::{AssetInstance, MultiAsset}; pub use multi_location::MultiLocation; pub use order::Order; -pub use traits::{Error, Result, SendXcm, ExecuteXcm, Outcome}; +pub use traits::{Error, ExecuteXcm, GetWeight, Outcome, Result, SendXcm, Weight, XcmWeightInfo}; /// A prelude for importing all types typically used when interacting with XCM messages. pub mod prelude { - pub use super::junction::{Junction::*, NetworkId, BodyId, BodyPart}; - pub use super::multi_asset::{MultiAsset::{self, *}, AssetInstance::{self, *}}; - pub use super::multi_location::MultiLocation::{self, *}; - pub use super::order::Order::{self, *}; - pub use super::traits::{Error as XcmError, Result as XcmResult, SendXcm, ExecuteXcm, Outcome}; - pub use super::{Xcm::{self, *}, OriginKind}; + pub use super::{ + junction::{BodyId, BodyPart, Junction::*, NetworkId}, + multi_asset::{ + AssetInstance::{self, *}, + MultiAsset::{self, *}, + }, + multi_location::MultiLocation::{self, *}, + order::Order::{self, *}, + traits::{Error as XcmError, ExecuteXcm, Outcome, Result as XcmResult, SendXcm}, + OriginKind, + Xcm::{self, *}, + }; } // TODO: #2841 #XCMENCODE Efficient encodings for Vec, Vec, using initial byte values 128+ to encode @@ -147,7 +153,11 @@ pub enum Xcm { /// /// Errors: #[codec(index = 3)] - QueryResponse { #[codec(compact)] query_id: u64, response: Response }, + QueryResponse { + #[codec(compact)] + query_id: u64, + response: Response, + }, /// Withdraw asset(s) (`assets`) from the ownership of `origin` and place equivalent assets under the /// ownership of `dest` within this consensus system. @@ -209,9 +219,12 @@ pub enum Xcm { /// Kind: *System Notification* #[codec(index = 7)] HrmpNewChannelOpenRequest { - #[codec(compact)] sender: u32, - #[codec(compact)] max_message_size: u32, - #[codec(compact)] max_capacity: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + max_message_size: u32, + #[codec(compact)] + max_capacity: u32, }, /// A message to notify about that a previously sent open channel request has been accepted by @@ -225,7 +238,8 @@ pub enum Xcm { /// Errors: #[codec(index = 8)] HrmpChannelAccepted { - #[codec(compact)] recipient: u32, + #[codec(compact)] + recipient: u32, }, /// A message to notify that the other party in an open channel decided to close it. In particular, @@ -240,9 +254,12 @@ pub enum Xcm { /// Errors: #[codec(index = 9)] HrmpChannelClosing { - #[codec(compact)] initiator: u32, - #[codec(compact)] sender: u32, - #[codec(compact)] recipient: u32, + #[codec(compact)] + initiator: u32, + #[codec(compact)] + sender: u32, + #[codec(compact)] + recipient: u32, }, /// A message to indicate that the embedded XCM is actually arriving on behalf of some consensus @@ -255,10 +272,7 @@ pub enum Xcm { /// /// Errors: #[codec(index = 10)] - RelayedFrom { - who: MultiLocation, - message: alloc::boxed::Box>, - }, + RelayedFrom { who: MultiLocation, message: alloc::boxed::Box> }, } impl From> for VersionedXcm { @@ -277,32 +291,58 @@ impl TryFrom> for Xcm { } impl Xcm { - pub fn into(self) -> Xcm { Xcm::from(self) } + pub fn into(self) -> Xcm { + Xcm::from(self) + } pub fn from(xcm: Xcm) -> Self { use Xcm::*; match xcm { - WithdrawAsset { assets, effects } - => WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - ReserveAssetDeposit { assets, effects } - => ReserveAssetDeposit { assets, effects: effects.into_iter().map(Order::into).collect() }, - TeleportAsset { assets, effects } - => TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, - QueryResponse { query_id: u64, response } - => QueryResponse { query_id: u64, response }, - TransferAsset { assets, dest } - => TransferAsset { assets, dest }, - TransferReserveAsset { assets, dest, effects } - => TransferReserveAsset { assets, dest, effects }, - HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity} - => HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity}, - HrmpChannelAccepted { recipient} - => HrmpChannelAccepted { recipient}, - HrmpChannelClosing { initiator, sender, recipient} - => HrmpChannelClosing { initiator, sender, recipient}, - Transact { origin_type, require_weight_at_most, call} - => Transact { origin_type, require_weight_at_most, call: call.into() }, - RelayedFrom { who, message } - => RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) }, + WithdrawAsset { assets, effects } => + WithdrawAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + ReserveAssetDeposit { assets, effects } => ReserveAssetDeposit { + assets, + effects: effects.into_iter().map(Order::into).collect(), + }, + TeleportAsset { assets, effects } => + TeleportAsset { assets, effects: effects.into_iter().map(Order::into).collect() }, + QueryResponse { query_id: u64, response } => QueryResponse { query_id: u64, response }, + TransferAsset { assets, dest } => TransferAsset { assets, dest }, + TransferReserveAsset { assets, dest, effects } => + TransferReserveAsset { assets, dest, effects }, + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity } => + HrmpNewChannelOpenRequest { sender, max_message_size, max_capacity }, + HrmpChannelAccepted { recipient } => HrmpChannelAccepted { recipient }, + HrmpChannelClosing { initiator, sender, recipient } => + HrmpChannelClosing { initiator, sender, recipient }, + Transact { origin_type, require_weight_at_most, call } => + Transact { origin_type, require_weight_at_most, call: call.into() }, + RelayedFrom { who, message } => + RelayedFrom { who, message: alloc::boxed::Box::new((*message).into()) }, + } + } + pub fn effects(&self) -> &[Order] { + use Xcm::*; + match self { + WithdrawAsset { effects, .. } => effects, + _ => &[], + } + } +} + +impl GetWeight for Xcm { + fn weight(&self) -> Weight { + match self { + Xcm::WithdrawAsset { .. } => W::xcm_withdraw_asset(), + Xcm::ReserveAssetDeposit { .. } => W::xcm_reserve_asset_deposit(), + Xcm::TeleportAsset { .. } => W::xcm_teleport_asset(), + Xcm::QueryResponse { .. } => W::xcm_query_response(), + Xcm::TransferAsset { .. } => W::xcm_transfer_asset(), + Xcm::TransferReserveAsset { .. } => W::xcm_transfer_reserved_asset(), + Xcm::Transact { .. } => W::xcm_transact(), + Xcm::HrmpNewChannelOpenRequest { .. } => W::xcm_hrmp_channel_open_request(), + Xcm::HrmpChannelAccepted { .. } => W::xcm_hrmp_channel_accepted(), + Xcm::HrmpChannelClosing { .. } => W::xcm_hrmp_channel_closing(), + Xcm::RelayedFrom { .. } => W::xcm_relayed_from(), } } } diff --git a/xcm/src/v0/order.rs b/xcm/src/v0/order.rs index 776ac3691c74..ec423dffe64e 100644 --- a/xcm/src/v0/order.rs +++ b/xcm/src/v0/order.rs @@ -16,14 +16,14 @@ //! Version 0 of the Cross-Consensus Message format data structures. +use super::{GetWeight, MultiAsset, MultiLocation, Weight, Xcm, XcmWeightInfo}; use alloc::vec::Vec; use derivative::Derivative; -use parity_scale_codec::{self, Encode, Decode}; -use super::{MultiAsset, MultiLocation, Xcm}; +use parity_scale_codec::{self, Decode, Encode}; /// An instruction to be executed on some or all of the assets in holding, used by asset-related XCM messages. #[derive(Derivative, Encode, Decode)] -#[derivative(Clone(bound=""), Eq(bound=""), PartialEq(bound=""), Debug(bound=""))] +#[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] pub enum Order { @@ -77,7 +77,11 @@ pub enum Order { /// /// Errors: #[codec(index = 4)] - InitiateReserveWithdraw { assets: Vec, reserve: MultiLocation, effects: Vec> }, + InitiateReserveWithdraw { + assets: Vec, + reserve: MultiLocation, + effects: Vec>, + }, /// Remove the asset(s) (`assets`) from holding and send a `TeleportAsset` XCM message to a destination location. /// @@ -99,14 +103,25 @@ pub enum Order { /// /// Errors: #[codec(index = 6)] - QueryHolding { #[codec(compact)] query_id: u64, dest: MultiLocation, assets: Vec }, + QueryHolding { + #[codec(compact)] + query_id: u64, + dest: MultiLocation, + assets: Vec, + }, /// Pay for the execution of some XCM with up to `weight` picoseconds of execution time, paying for this with /// up to `fees` from the holding account. /// /// Errors: #[codec(index = 7)] - BuyExecution { fees: MultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec> }, + BuyExecution { + fees: MultiAsset, + weight: u64, + debt: u64, + halt_on_error: bool, + xcm: Vec>, + }, } pub mod opaque { @@ -114,23 +129,22 @@ pub mod opaque { } impl Order { - pub fn into(self) -> Order { Order::from(self) } + pub fn into(self) -> Order { + Order::from(self) + } pub fn from(order: Order) -> Self { use Order::*; match order { Null => Null, - DepositAsset { assets, dest } - => DepositAsset { assets, dest }, - DepositReserveAsset { assets, dest, effects } - => DepositReserveAsset { assets, dest, effects }, - ExchangeAsset { give, receive } - => ExchangeAsset { give, receive }, - InitiateReserveWithdraw { assets, reserve, effects } - => InitiateReserveWithdraw { assets, reserve, effects }, - InitiateTeleport { assets, dest, effects } - => InitiateTeleport { assets, dest, effects }, - QueryHolding { query_id, dest, assets } - => QueryHolding { query_id, dest, assets }, + DepositAsset { assets, dest } => DepositAsset { assets, dest }, + DepositReserveAsset { assets, dest, effects } => + DepositReserveAsset { assets, dest, effects }, + ExchangeAsset { give, receive } => ExchangeAsset { give, receive }, + InitiateReserveWithdraw { assets, reserve, effects } => + InitiateReserveWithdraw { assets, reserve, effects }, + InitiateTeleport { assets, dest, effects } => + InitiateTeleport { assets, dest, effects }, + QueryHolding { query_id, dest, assets } => QueryHolding { query_id, dest, assets }, BuyExecution { fees, weight, debt, halt_on_error, xcm } => { let xcm = xcm.into_iter().map(Xcm::from).collect(); BuyExecution { fees, weight, debt, halt_on_error, xcm } @@ -138,3 +152,18 @@ impl Order { } } } + +impl GetWeight for Order { + fn weight(&self) -> Weight { + match self { + Order::Null => W::order_null(), + Order::DepositAsset { .. } => W::order_deposit_asset(), + Order::DepositReserveAsset { .. } => W::order_deposit_reserved_asset(), + Order::ExchangeAsset { .. } => W::order_exchange_asset(), + Order::InitiateReserveWithdraw { .. } => W::order_initiate_reserve_withdraw(), + Order::InitiateTeleport { .. } => W::order_initiate_teleport(), + Order::QueryHolding { .. } => W::order_query_holding(), + Order::BuyExecution { .. } => W::order_buy_execution(), + } + } +} diff --git a/xcm/src/v0/traits.rs b/xcm/src/v0/traits.rs index 9a01f227e766..73f14d59ffd7 100644 --- a/xcm/src/v0/traits.rs +++ b/xcm/src/v0/traits.rs @@ -17,7 +17,7 @@ //! Cross-Consensus Message format data structures. use core::result; -use parity_scale_codec::{Encode, Decode}; +use parity_scale_codec::{Decode, Encode}; use super::{MultiLocation, Xcm}; @@ -259,3 +259,33 @@ impl SendXcm for Tuple { Err(Error::CannotReachDestination(destination, message)) } } + +// The info needed to weight an XCM. +pub trait XcmWeightInfo { + fn send_xcm() -> Weight; + fn order_null() -> Weight; + fn order_deposit_asset(assets: Vec, dest: MultiLocation) -> Weight; + fn order_deposit_reserved_asset(assets: Vec, dest: MultiLocation, effects: Vec>) -> Weight; + fn order_exchange_asset(give: Vec, receive: Vec) -> Weight; + fn order_initiate_reserve_withdraw(assets: Vec, reserve: MultiLocation, effects: Vec>) -> Weight; + fn order_initiate_teleport(assets: Vec, dest: MultiLocation, effects: Vec>) -> Weight; + fn order_query_holding(query_id: u64, dest: MultiLocation, assets: Vec) -> Weight; + fn order_buy_execution(fees: MultiAsset, weight: u64, debt: u64, halt_on_error: bool, xcm: Vec>) -> Weight; + fn xcm_withdraw_asset(assets: Vec, effects: Vec>) -> Weight; + fn xcm_reserve_asset_deposit(assets: Vec, effects: Vec>) -> Weight; + fn xcm_teleport_asset(assets: Vec, effects: Vec>) -> Weight; + fn xcm_query_response(query_id: u64, response: Response) -> Weight; + fn xcm_transfer_asset(assets: Vec, dest: MultiLocation) -> Weight; + fn xcm_transfer_reserved_asset(assets: Vec, dest: MultiLocation) -> Weight; + // TODO: Maybe remove call + fn xcm_transact(origin_type: OriginKind, require_weight_at_most: u64, call: DoubleEncoded) -> Weight; + fn xcm_hrmp_channel_open_request(sender: u32, max_message_size: u32, max_capacity: u32) -> Weight; + fn xcm_hrmp_channel_accepted(recipient: u32) -> Weight; + fn xcm_hrmp_channel_closing(initiator: u32, sender: u32, recipient: u32) -> Weight; + fn xcm_relayed_from(who: MultiLocation, message: alloc::boxed::Box) -> Weight; +} + +// A simple trait to get the weight of some object. +pub trait GetWeight { + fn weight(&self) -> Weight; +} diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index 7cda682d4b30..73975fab4928 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -17,16 +17,17 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +log = { version = "0.4.0", default-features = false } # Polkadot dependencies polkadot-parachain = { path = "../../parachain", default-features = false } [features] default = ["std"] -runtime-benchmarks = [] std = [ "parity-scale-codec/std", "xcm/std", + "log/std", "xcm-executor/std", "sp-std/std", "sp-arithmetic/std", @@ -36,3 +37,6 @@ std = [ "polkadot-parachain/std", "pallet-transaction-payment/std", ] +runtime-benchmarks = [ + "xcm-executor/runtime-benchmarks", +] diff --git a/xcm/xcm-builder/src/currency_adapter.rs b/xcm/xcm-builder/src/currency_adapter.rs index 744835f08959..329e8dca2175 100644 --- a/xcm/xcm-builder/src/currency_adapter.rs +++ b/xcm/xcm-builder/src/currency_adapter.rs @@ -85,16 +85,20 @@ pub struct CurrencyAdapter, - AccountIdConverter: Convert, - Currency: frame_support::traits::Currency, - AccountId: Clone, // can't get away without it since Currency is generic over it. - CheckedAccount: Get>, -> TransactAsset for CurrencyAdapter { + Matcher: MatchesFungible, + AccountIdConverter: Convert, + Currency: frame_support::traits::Currency, + AccountId: Clone, // can't get away without it since Currency is generic over it. + CheckedAccount: Get>, + > TransactAsset + for CurrencyAdapter +where + >::Balance: Into, +{ fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result { // Check we handle this asset. - let amount: Currency::Balance = Matcher::matches_fungible(what) - .ok_or(Error::AssetNotFound)?; + let amount: Currency::Balance = + Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?; if let Some(checked_account) = CheckedAccount::get() { let new_balance = Currency::free_balance(&checked_account) .checked_sub(&amount) @@ -123,10 +127,10 @@ impl< } fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result { + log::trace!("xcm::currency_adapter deposit_asset {:?} {:?}", what, who); // Check we handle this asset. - let amount: u128 = Matcher::matches_fungible(&what) - .ok_or(Error::AssetNotFound)? - .saturated_into(); + let amount: u128 = + Matcher::matches_fungible(&what).ok_or(Error::AssetNotFound)?.saturated_into(); let who = AccountIdConverter::convert_ref(who) .map_err(|()| Error::AccountIdConversionFailed)?; let balance_amount = amount @@ -136,14 +140,10 @@ impl< Ok(()) } - fn withdraw_asset( - what: &MultiAsset, - who: &MultiLocation - ) -> result::Result { + fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> result::Result { // Check we handle this asset. - let amount: u128 = Matcher::matches_fungible(what) - .ok_or(Error::AssetNotFound)? - .saturated_into(); + let amount: u128 = + Matcher::matches_fungible(what).ok_or(Error::AssetNotFound)?.saturated_into(); let who = AccountIdConverter::convert_ref(who) .map_err(|()| Error::AccountIdConversionFailed)?; let balance_amount = amount diff --git a/xcm/xcm-builder/src/fungibles_adapter.rs b/xcm/xcm-builder/src/fungibles_adapter.rs index 80cd74c0124d..e591c940be33 100644 --- a/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/xcm/xcm-builder/src/fungibles_adapter.rs @@ -94,14 +94,15 @@ impl< } pub struct FungiblesTransferAdapter( - PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)> + PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>, ); impl< - Assets: fungibles::Transfer, - Matcher: MatchesFungibles, - AccountIdConverter: Convert, - AccountId: Clone, // can't get away without it since Currency is generic over it. -> TransactAsset for FungiblesTransferAdapter { + Assets: fungibles::Transfer, + Matcher: MatchesFungibles, + AccountIdConverter: Convert, + AccountId: Clone, // can't get away without it since Currency is generic over it. + > TransactAsset for FungiblesTransferAdapter +{ fn transfer_asset( what: &MultiAsset, from: &MultiLocation, @@ -119,17 +120,31 @@ impl< } } -pub struct FungiblesMutateAdapter( - PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)> -); +pub struct FungiblesMutateAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, +>(PhantomData<(Assets, Matcher, AccountIdConverter, AccountId, CheckAsset, CheckingAccount)>); impl< - Assets: fungibles::Mutate, - Matcher: MatchesFungibles, - AccountIdConverter: Convert, - AccountId: Clone, // can't get away without it since Currency is generic over it. - CheckAsset: Contains, - CheckingAccount: Get, -> TransactAsset for FungiblesMutateAdapter { + Assets: fungibles::Mutate, + Matcher: MatchesFungibles, + AccountIdConverter: Convert, + AccountId: Clone, // can't get away without it since Currency is generic over it. + CheckAsset: Contains, + CheckingAccount: Get, + > TransactAsset + for FungiblesMutateAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + > +{ fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result { // Check we handle this asset. let (asset_id, amount) = Matcher::matches_fungibles(what)?; @@ -174,7 +189,7 @@ impl< fn withdraw_asset( what: &MultiAsset, - who: &MultiLocation + who: &MultiLocation, ) -> result::Result { // Check we handle this asset. let (asset_id, amount) = Matcher::matches_fungibles(what)?; diff --git a/xcm/xcm-builder/src/mock.rs b/xcm/xcm-builder/src/mock.rs index 56d7d753e49e..0c0228ff54d4 100644 --- a/xcm/xcm-builder/src/mock.rs +++ b/xcm/xcm-builder/src/mock.rs @@ -54,6 +54,7 @@ pub enum TestCall { OnlySigned(Weight, Option, Option), Any(Weight, Option), } + impl Dispatchable for TestCall { type Origin = TestOrigin; type Config = (); @@ -101,9 +102,11 @@ impl GetDispatchInfo for TestCall { thread_local! { pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); } + pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } + pub struct TestSendXcm; impl SendXcm for TestSendXcm { fn send_xcm(dest: MultiLocation, msg: opaque::Xcm) -> XcmResult { diff --git a/xcm/xcm-builder/src/weight.rs b/xcm/xcm-builder/src/weight.rs index e2096afcaa30..3846af2f4705 100644 --- a/xcm/xcm-builder/src/weight.rs +++ b/xcm/xcm-builder/src/weight.rs @@ -14,27 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::{result::Result, marker::PhantomData, convert::TryInto}; +use frame_support::{ + traits::{tokens::currency::Currency as CurrencyT, Get, OnUnbalanced as OnUnbalancedT}, + weights::{GetDispatchInfo, Weight, WeightToFeePolynomial}, +}; use parity_scale_codec::Decode; -use xcm::v0::{Xcm, Order, MultiAsset, MultiLocation, Error}; -use sp_runtime::traits::{Zero, Saturating, SaturatedConversion}; -use frame_support::traits::{Get, OnUnbalanced as OnUnbalancedT, tokens::currency::Currency as CurrencyT}; -use frame_support::weights::{Weight, GetDispatchInfo, WeightToFeePolynomial}; -use xcm_executor::{Assets, traits::{WeightBounds, WeightTrader}}; +use sp_runtime::traits::{SaturatedConversion, Saturating, Zero}; +use sp_std::{convert::TryInto, marker::PhantomData, result::Result}; +use xcm::v0::{Error, MultiAsset, MultiLocation, Order, Xcm}; +use xcm_executor::{ + traits::{WeightBounds, WeightTrader}, + Assets, +}; pub struct FixedWeightBounds(PhantomData<(T, C)>); impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeightBounds { fn shallow(message: &mut Xcm) -> Result { Ok(match message { - Xcm::Transact { call, .. } => { - call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()) - } - Xcm::RelayedFrom { ref mut message, .. } => T::get().saturating_add(Self::shallow(message.as_mut())?), - Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposit { effects, .. } - | Xcm::TeleportAsset { effects, .. } - => { - let inner: Weight = effects.iter_mut() + Xcm::Transact { call, .. } => + call.ensure_decoded()?.get_dispatch_info().weight.saturating_add(T::get()), + + Xcm::RelayedFrom { ref mut message, .. } => + T::get().saturating_add(Self::shallow(message.as_mut())?), + + Xcm::WithdrawAsset { effects, .. } | + Xcm::ReserveAssetDeposit { effects, .. } | + Xcm::TeleportAsset { effects, .. } => { + let inner: Weight = effects + .iter_mut() .map(|effect| match effect { Order::BuyExecution { .. } => { // On success, execution of this will result in more weight being consumed but @@ -45,28 +52,29 @@ impl, C: Decode + GetDispatchInfo> WeightBounds for FixedWeigh T::get() }, _ => T::get(), - }).sum(); + }) + .sum(); T::get().saturating_add(inner) - } + }, _ => T::get(), }) } fn deep(message: &mut Xcm) -> Result { Ok(match message { Xcm::RelayedFrom { ref mut message, .. } => Self::deep(message.as_mut())?, - Xcm::WithdrawAsset { effects, .. } - | Xcm::ReserveAssetDeposit { effects, .. } - | Xcm::TeleportAsset { effects, .. } - => { + Xcm::WithdrawAsset { effects, .. } | + Xcm::ReserveAssetDeposit { effects, .. } | + Xcm::TeleportAsset { effects, .. } => { let mut extra = 0; for effect in effects.iter_mut() { match effect { - Order::BuyExecution { xcm, .. } => { + Order::BuyExecution { xcm, .. } => for message in xcm.iter_mut() { - extra.saturating_accrue(Self::shallow(message)?.saturating_add(Self::deep(message)?)); - } - }, - _ => {} + extra.saturating_accrue( + Self::shallow(message)?.saturating_add(Self::deep(message)?), + ); + }, + _ => {}, } } extra @@ -92,12 +100,17 @@ impl TakeRevenue for () { /// /// The constant `Get` type parameter should be the concrete fungible ID and the amount of it required for /// one second of weight. -pub struct FixedRateOfConcreteFungible< - T: Get<(MultiLocation, u128)>, - R: TakeRevenue, ->(Weight, u128, PhantomData<(T, R)>); -impl, R: TakeRevenue> WeightTrader for FixedRateOfConcreteFungible { - fn new() -> Self { Self(0, 0, PhantomData) } +pub struct FixedRateOfConcreteFungible, R: TakeRevenue>( + Weight, + u128, + PhantomData<(T, R)>, +); +impl, R: TakeRevenue> WeightTrader + for FixedRateOfConcreteFungible +{ + fn new() -> Self { + Self(0, 0, PhantomData) + } fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let (id, units_per_second) = T::get(); @@ -131,20 +144,27 @@ impl, R: TakeRevenue> Drop for FixedRateOfConcrete /// Weight trader which uses the `TransactionPayment` pallet to set the right price for weight and then /// places any weight bought into the right account. pub struct UsingComponents< - WeightToFee: WeightToFeePolynomial, + WeightToFee: WeightToFeePolynomial, AssetId: Get, AccountId, Currency: CurrencyT, OnUnbalanced: OnUnbalancedT, ->(Weight, Currency::Balance, PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>); +>( + Weight, + Currency::Balance, + PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>, +); impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, -> WeightTrader for UsingComponents { - fn new() -> Self { Self(0, Zero::zero(), PhantomData) } + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > WeightTrader for UsingComponents +{ + fn new() -> Self { + Self(0, Zero::zero(), PhantomData) + } fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result { let amount = WeightToFee::calc(&weight); @@ -163,21 +183,19 @@ impl< let amount = WeightToFee::calc(&weight); self.0 -= weight; self.1 = self.1.saturating_sub(amount); - let result = MultiAsset::ConcreteFungible { - amount: amount.saturated_into(), - id: AssetId::get(), - }; + let result = + MultiAsset::ConcreteFungible { amount: amount.saturated_into(), id: AssetId::get() }; result } - } impl< - WeightToFee: WeightToFeePolynomial, - AssetId: Get, - AccountId, - Currency: CurrencyT, - OnUnbalanced: OnUnbalancedT, -> Drop for UsingComponents { + WeightToFee: WeightToFeePolynomial, + AssetId: Get, + AccountId, + Currency: CurrencyT, + OnUnbalanced: OnUnbalancedT, + > Drop for UsingComponents +{ fn drop(&mut self) { OnUnbalanced::on_unbalanced(Currency::issue(self.1)); } diff --git a/xcm/xcm-executor/Cargo.toml b/xcm/xcm-executor/Cargo.toml index 3ff04c530486..5bd6eabd433a 100644 --- a/xcm/xcm-executor/Cargo.toml +++ b/xcm/xcm-executor/Cargo.toml @@ -30,3 +30,4 @@ std = [ "frame-support/std", "log/std", ] +runtime-benchmarks = [] diff --git a/xcm/xcm-executor/src/assets.rs b/xcm/xcm-executor/src/assets.rs index c0d35052482b..f47e1f6d7e8a 100644 --- a/xcm/xcm-executor/src/assets.rs +++ b/xcm/xcm-executor/src/assets.rs @@ -497,7 +497,7 @@ impl Assets { MultiAsset::AbstractFungible { id, amount } => (AssetId::Abstract(id), amount), _ => unreachable!(), }; - // remove the maxmimum possible up to id/amount from self, add the removed onto + // remove the maximum possible up to id/amount from self, add the removed onto // result let maybe_value = self.fungible.get(&id); if let Some(&e) = maybe_value { @@ -516,7 +516,7 @@ impl Assets { MultiAsset::AbstractNonFungible { class, instance } => (AssetId::Abstract(class), instance), _ => unreachable!(), }; - // remove the maxmimum possible up to id/amount from self, add the removed onto + // remove the maximum possible up to id/amount from self, add the removed onto // result if let Some(entry) = self.non_fungible.take(&(class, instance)) { result.non_fungible.insert(entry); diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index 8f8a5c9ee617..0e1f75c927bc 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -106,10 +106,10 @@ impl XcmExecutor { log::trace!( target: "xcm::do_execute_xcm", "origin: {:?}, top_level: {:?}, message: {:?}, weight_credit: {:?}, maybe_shallow_weight: {:?}", - origin, - top_level, - message, - weight_credit, + origin, + top_level, + message, + weight_credit, maybe_shallow_weight, ); // This is the weight of everything that cannot be paid for. This basically means all computation @@ -266,11 +266,11 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest); Config::XcmSender::send_xcm(dest, Xcm::ReserveAssetDeposit { assets, effects })?; }, - Order::InitiateReserveWithdraw { assets, reserve, effects} => { + Order::InitiateReserveWithdraw { assets, reserve, effects } => { let assets = Self::reanchored(holding.saturating_take(assets), &reserve); Config::XcmSender::send_xcm(reserve, Xcm::WithdrawAsset { assets, effects })?; } - Order::InitiateTeleport { assets, dest, effects} => { + Order::InitiateTeleport { assets, dest, effects } => { // We must do this first in order to resolve wildcards. let assets = holding.saturating_take(assets); for asset in assets.assets_iter() { @@ -300,8 +300,21 @@ impl XcmExecutor { } holding.saturating_subsume(trader.refund_weight(remaining_weight)); } + Order::Null => { + // nada + } _ => return Err(XcmError::UnhandledEffect)?, } Ok(total_surplus) } + + #[cfg(feature = "runtime-benchmarks")] + pub fn do_execute_effects( + origin: &MultiLocation, + holding: &mut Assets, + effect: Order, + ) -> Result { + let mut trader = Config::Trader::new(); + Self::execute_effects(origin, holding, effect, &mut trader) + } } diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index a967f1f6909a..cec6bce1c6ff 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -85,7 +85,11 @@ pub trait TransactAsset { /// Move an `asset` `from` one location in `to` another location. /// /// Attempts to use `transfer_asset` and if not available then falls back to using a two-part withdraw/deposit. - fn teleport_asset(asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation) -> Result { + fn teleport_asset( + asset: &MultiAsset, + from: &MultiLocation, + to: &MultiLocation, + ) -> Result { match Self::transfer_asset(asset, from, to) { Err(XcmError::Unimplemented) => { let assets = Self::withdraw_asset(asset, from)?; @@ -244,29 +248,33 @@ mod tests { #[test] fn defaults_to_asset_not_found() { - type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); - - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Err(XcmError::AssetNotFound)); + type MultiTransactor = + (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); + assert_eq!( + MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), + Err(XcmError::AssetNotFound), + ); } #[test] fn unimplemented_and_not_found_continue_iteration() { type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor); - - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(()),); } #[test] fn unexpected_error_stops_iteration() { type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); - - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Err(XcmError::Overflow)); + assert_eq!( + MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), + Err(XcmError::Overflow), + ); } #[test] fn success_stops_iteration() { type MultiTransactor = (SuccessfulTransactor, OverflowTransactor); - assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(())); + assert_eq!(MultiTransactor::deposit_asset(&MultiAsset::All, &MultiLocation::Null), Ok(()),); } } diff --git a/xcm/xcm-executor/src/traits/weight.rs b/xcm/xcm-executor/src/traits/weight.rs index dc9589803af5..824383da508e 100644 --- a/xcm/xcm-executor/src/traits/weight.rs +++ b/xcm/xcm-executor/src/traits/weight.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use sp_std::result::Result; -use xcm::v0::{Xcm, MultiAsset, MultiLocation, Error}; -use frame_support::weights::Weight; use crate::Assets; +use frame_support::weights::Weight; +use sp_std::result::Result; +use xcm::v0::{Error, MultiAsset, MultiLocation, Xcm}; /// Determine the weight of an XCM message. pub trait WeightBounds { @@ -71,11 +71,15 @@ pub trait WeightTrader: Sized { /// purchased using `buy_weight`. /// /// Default implementation refunds nothing. - fn refund_weight(&mut self, _weight: Weight) -> MultiAsset { MultiAsset::None } + fn refund_weight(&mut self, _weight: Weight) -> MultiAsset { + MultiAsset::None + } } impl WeightTrader for () { - fn new() -> Self { () } + fn new() -> Self { + () + } fn buy_weight(&mut self, _: Weight, _: Assets) -> Result { Err(Error::Unimplemented) }