......@@ -313,7 +313,6 @@ parameter_types! {
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxExposurePageSize: u32 = 64;
pub const MaxNominators: u32 = 256;
pub storage OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxAuthorities: u32 = 100_000;
pub const OnChainMaxWinners: u32 = u32::MAX;
// Unbounded number of election targets and voters.
......@@ -349,7 +348,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
......@@ -364,6 +362,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = ();
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}
parameter_types! {
......
......@@ -17,9 +17,5 @@
use substrate_wasm_builder::WasmBuilder;
fn main() {
WasmBuilder::new()
.with_current_project()
.import_memory()
.export_heap_base()
.build()
WasmBuilder::build_using_defaults();
}
......@@ -167,16 +167,11 @@ where
},
]);
let encoded_versioned_xcm =
VersionedXcm::V4(program).encode().try_into().map_err(|error| {
log::error!(target: "runtime::on_reap_identity", "XCM too large, error: {:?}", error);
pallet_xcm::Error::<Runtime>::XcmTooLarge
})?;
// send
let _ = <pallet_xcm::Pallet<Runtime>>::send_blob(
let _ = <pallet_xcm::Pallet<Runtime>>::send(
RawOrigin::Root.into(),
Box::new(VersionedLocation::V4(destination)),
encoded_versioned_xcm,
Box::new(VersionedXcm::V4(program)),
)?;
Ok(())
}
......
......@@ -613,7 +613,6 @@ parameter_types! {
// this is an unbounded number. We just set it to a reasonably high value, 1 full page
// of nominators.
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub const MaxControllersInDeprecationBatch: u32 = 751;
}
......@@ -634,7 +633,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
......@@ -647,6 +645,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = NominationPools;
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}
impl pallet_fast_unstake::Config for Runtime {
......@@ -1189,6 +1188,7 @@ impl parachains_scheduler::Config for Runtime {
parameter_types! {
pub const BrokerId: u32 = BROKER_ID;
pub MaxXcmTransactWeight: Weight = Weight::from_parts(200_000_000, 20_000);
}
impl coretime::Config for Runtime {
......@@ -1198,6 +1198,7 @@ impl coretime::Config for Runtime {
type BrokerId = BrokerId;
type WeightInfo = weights::runtime_parachains_coretime::WeightInfo<Runtime>;
type SendXcm = crate::xcm_config::XcmRouter;
type MaxXcmTransactWeight = MaxXcmTransactWeight;
}
parameter_types! {
......@@ -1647,36 +1648,8 @@ pub mod migrations {
}
}
// We don't have a limit in the Relay Chain.
const IDENTITY_MIGRATION_KEY_LIMIT: u64 = u64::MAX;
/// Unreleased migrations. Add new ones here:
pub type Unreleased = (
parachains_configuration::migration::v7::MigrateToV7<Runtime>,
pallet_staking::migrations::v14::MigrateToV14<Runtime>,
assigned_slots::migration::v1::MigrateToV1<Runtime>,
parachains_scheduler::migration::MigrateV1ToV2<Runtime>,
parachains_configuration::migration::v8::MigrateToV8<Runtime>,
parachains_configuration::migration::v9::MigrateToV9<Runtime>,
paras_registrar::migration::MigrateToV1<Runtime, ()>,
pallet_referenda::migration::v1::MigrateV0ToV1<Runtime, ()>,
pallet_grandpa::migrations::MigrateV4ToV5<Runtime>,
parachains_configuration::migration::v10::MigrateToV10<Runtime>,
pallet_nomination_pools::migration::unversioned::TotalValueLockedSync<Runtime>,
// Migrate Identity pallet for Usernames
pallet_identity::migration::versioned::V0ToV1<Runtime, IDENTITY_MIGRATION_KEY_LIMIT>,
parachains_configuration::migration::v11::MigrateToV11<Runtime>,
parachains_configuration::migration::v12::MigrateToV12<Runtime>,
// permanent
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
// Migrate from legacy lease to coretime. Needs to run after configuration v11
coretime::migration::MigrateToCoretime<
Runtime,
crate::xcm_config::XcmRouter,
GetLegacyLeaseImpl,
>,
parachains_inclusion::migration::MigrateToV1<Runtime>,
);
pub type Unreleased = (pallet_staking::migrations::v15::MigrateV14ToV15<Runtime>,);
}
/// Unchecked extrinsic type as expected by this runtime.
......
......@@ -17,9 +17,9 @@
//! Autogenerated weights for `pallet_xcm`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2024-03-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2024-02-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024
// Executed Command:
......@@ -60,26 +60,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `147`
// Estimated: `3612`
// Minimum execution time: 24_535_000 picoseconds.
Weight::from_parts(25_618_000, 0)
.saturating_add(Weight::from_parts(0, 3612))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
}
/// Storage: `Dmp::DeliveryFeeFactor` (r:1 w:0)
/// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `XcmPallet::SupportedVersion` (r:1 w:0)
/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueues` (r:1 w:1)
/// Proof: `Dmp::DownwardMessageQueues` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Dmp::DownwardMessageQueueHeads` (r:1 w:1)
/// Proof: `Dmp::DownwardMessageQueueHeads` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn send_blob() -> Weight {
// Proof Size summary in bytes:
// Measured: `147`
// Estimated: `3612`
// Minimum execution time: 25_376_000 picoseconds.
Weight::from_parts(26_180_000, 0)
// Minimum execution time: 25_725_000 picoseconds.
Weight::from_parts(26_174_000, 0)
.saturating_add(Weight::from_parts(0, 3612))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(2))
......@@ -98,8 +80,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `250`
// Estimated: `6196`
// Minimum execution time: 108_786_000 picoseconds.
Weight::from_parts(112_208_000, 0)
// Minimum execution time: 113_140_000 picoseconds.
Weight::from_parts(116_204_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(4))
......@@ -118,8 +100,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `6196`
// Minimum execution time: 105_190_000 picoseconds.
Weight::from_parts(107_140_000, 0)
// Minimum execution time: 108_571_000 picoseconds.
Weight::from_parts(110_650_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(4))
......@@ -138,8 +120,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `250`
// Estimated: `6196`
// Minimum execution time: 109_027_000 picoseconds.
Weight::from_parts(111_404_000, 0)
// Minimum execution time: 111_836_000 picoseconds.
Weight::from_parts(114_435_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(4))
......@@ -154,24 +136,14 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
Weight::from_parts(18_446_744_073_709_551_000, 0)
.saturating_add(Weight::from_parts(0, 0))
}
/// Storage: `Benchmark::Override` (r:0 w:0)
/// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn execute_blob() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 18_446_744_073_709_551_000 picoseconds.
Weight::from_parts(18_446_744_073_709_551_000, 0)
.saturating_add(Weight::from_parts(0, 0))
}
/// Storage: `XcmPallet::SupportedVersion` (r:0 w:1)
/// Proof: `XcmPallet::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
fn force_xcm_version() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 6_668_000 picoseconds.
Weight::from_parts(7_013_000, 0)
// Minimum execution time: 7_160_000 picoseconds.
Weight::from_parts(7_477_000, 0)
.saturating_add(Weight::from_parts(0, 0))
.saturating_add(T::DbWeight::get().writes(1))
}
......@@ -179,8 +151,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_740_000 picoseconds.
Weight::from_parts(1_884_000, 0)
// Minimum execution time: 1_934_000 picoseconds.
Weight::from_parts(2_053_000, 0)
.saturating_add(Weight::from_parts(0, 0))
}
/// Storage: `XcmPallet::VersionNotifiers` (r:1 w:1)
......@@ -201,8 +173,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `147`
// Estimated: `3612`
// Minimum execution time: 30_200_000 picoseconds.
Weight::from_parts(30_768_000, 0)
// Minimum execution time: 31_123_000 picoseconds.
Weight::from_parts(31_798_000, 0)
.saturating_add(Weight::from_parts(0, 3612))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(5))
......@@ -223,8 +195,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `327`
// Estimated: `3792`
// Minimum execution time: 33_928_000 picoseconds.
Weight::from_parts(35_551_000, 0)
// Minimum execution time: 35_175_000 picoseconds.
Weight::from_parts(36_098_000, 0)
.saturating_add(Weight::from_parts(0, 3792))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(4))
......@@ -235,8 +207,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_759_000 picoseconds.
Weight::from_parts(1_880_000, 0)
// Minimum execution time: 1_974_000 picoseconds.
Weight::from_parts(2_096_000, 0)
.saturating_add(Weight::from_parts(0, 0))
.saturating_add(T::DbWeight::get().writes(1))
}
......@@ -246,8 +218,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `22`
// Estimated: `13387`
// Minimum execution time: 16_507_000 picoseconds.
Weight::from_parts(17_219_000, 0)
// Minimum execution time: 16_626_000 picoseconds.
Weight::from_parts(17_170_000, 0)
.saturating_add(Weight::from_parts(0, 13387))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
......@@ -258,8 +230,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `26`
// Estimated: `13391`
// Minimum execution time: 16_633_000 picoseconds.
Weight::from_parts(16_889_000, 0)
// Minimum execution time: 16_937_000 picoseconds.
Weight::from_parts(17_447_000, 0)
.saturating_add(Weight::from_parts(0, 13391))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
......@@ -270,8 +242,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `40`
// Estimated: `15880`
// Minimum execution time: 19_297_000 picoseconds.
Weight::from_parts(19_820_000, 0)
// Minimum execution time: 19_157_000 picoseconds.
Weight::from_parts(19_659_000, 0)
.saturating_add(Weight::from_parts(0, 15880))
.saturating_add(T::DbWeight::get().reads(6))
}
......@@ -289,8 +261,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `183`
// Estimated: `6123`
// Minimum execution time: 30_364_000 picoseconds.
Weight::from_parts(31_122_000, 0)
// Minimum execution time: 30_699_000 picoseconds.
Weight::from_parts(31_537_000, 0)
.saturating_add(Weight::from_parts(0, 6123))
.saturating_add(T::DbWeight::get().reads(6))
.saturating_add(T::DbWeight::get().writes(3))
......@@ -301,8 +273,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `69`
// Estimated: `10959`
// Minimum execution time: 11_997_000 picoseconds.
Weight::from_parts(12_392_000, 0)
// Minimum execution time: 12_303_000 picoseconds.
Weight::from_parts(12_670_000, 0)
.saturating_add(Weight::from_parts(0, 10959))
.saturating_add(T::DbWeight::get().reads(4))
}
......@@ -312,8 +284,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `33`
// Estimated: `13398`
// Minimum execution time: 16_894_000 picoseconds.
Weight::from_parts(17_452_000, 0)
// Minimum execution time: 17_129_000 picoseconds.
Weight::from_parts(17_668_000, 0)
.saturating_add(Weight::from_parts(0, 13398))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(2))
......@@ -332,8 +304,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `183`
// Estimated: `13548`
// Minimum execution time: 39_864_000 picoseconds.
Weight::from_parts(40_859_000, 0)
// Minimum execution time: 39_960_000 picoseconds.
Weight::from_parts(41_068_000, 0)
.saturating_add(Weight::from_parts(0, 13548))
.saturating_add(T::DbWeight::get().reads(9))
.saturating_add(T::DbWeight::get().writes(4))
......@@ -346,8 +318,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `1485`
// Minimum execution time: 2_363_000 picoseconds.
Weight::from_parts(2_519_000, 0)
// Minimum execution time: 2_333_000 picoseconds.
Weight::from_parts(2_504_000, 0)
.saturating_add(Weight::from_parts(0, 1485))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(2))
......@@ -358,8 +330,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `7576`
// Estimated: `11041`
// Minimum execution time: 22_409_000 picoseconds.
Weight::from_parts(22_776_000, 0)
// Minimum execution time: 22_932_000 picoseconds.
Weight::from_parts(23_307_000, 0)
.saturating_add(Weight::from_parts(0, 11041))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
......@@ -370,8 +342,8 @@ impl<T: frame_system::Config> pallet_xcm::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `23`
// Estimated: `3488`
// Minimum execution time: 33_551_000 picoseconds.
Weight::from_parts(34_127_000, 0)
// Minimum execution time: 34_558_000 picoseconds.
Weight::from_parts(35_299_000, 0)
.saturating_add(Weight::from_parts(0, 3488))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
......
......@@ -29,7 +29,6 @@ log = { workspace = true, default-features = true }
[dev-dependencies]
pallet-balances = { path = "../../../substrate/frame/balances" }
pallet-assets = { path = "../../../substrate/frame/assets" }
sp-core = { path = "../../../substrate/primitives/core" }
sp-tracing = { path = "../../../substrate/primitives/tracing" }
xcm = { package = "staging-xcm", path = ".." }
# temp
......
......@@ -146,8 +146,6 @@ benchmarks_instance_pallet! {
initiate_reserve_withdraw {
let (sender_account, sender_location) = account_and_location::<T>(1);
let holding = T::worst_case_holding(1);
let assets_filter = AssetFilter::Definite(holding.clone().into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into());
let reserve = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
......@@ -157,15 +155,29 @@ benchmarks_instance_pallet! {
);
let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
// generate holding and add possible required fees
let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
let mut holding = T::worst_case_holding(1 + expected_assets_in_holding.len() as u32);
for a in expected_assets_in_holding.into_inner() {
holding.push(a);
}
holding
} else {
T::worst_case_holding(1)
};
let mut executor = new_executor::<T>(sender_location);
executor.set_holding(holding.into());
executor.set_holding(holding.clone().into());
if let Some(expected_fees_mode) = expected_fees_mode {
executor.set_fees_mode(expected_fees_mode);
}
if let Some(expected_assets_in_holding) = expected_assets_in_holding {
executor.set_holding(expected_assets_in_holding.into());
}
let instruction = Instruction::InitiateReserveWithdraw { assets: assets_filter, reserve, xcm: Xcm(vec![]) };
let instruction = Instruction::InitiateReserveWithdraw {
// Worst case is looking through all holdings for every asset explicitly - respecting the limit `MAX_ITEMS_IN_ASSETS`.
assets: Definite(holding.into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into()),
reserve,
xcm: Xcm(vec![])
};
let xcm = Xcm(vec![instruction]);
}: {
executor.bench_process(xcm)?;
......
......@@ -16,17 +16,16 @@
//! A mock runtime for XCM benchmarking.
use crate::{fungible as xcm_balances_benchmark, mock::*};
use crate::{fungible as xcm_balances_benchmark, generate_holding_assets, mock::*};
use frame_benchmarking::BenchmarkError;
use frame_support::{
derive_impl, parameter_types,
traits::{ConstU32, Everything, Nothing},
weights::Weight,
traits::{Everything, Nothing},
};
use sp_core::H256;
use sp_runtime::traits::{BlakeTwo256, IdentityLookup};
use xcm::latest::prelude::*;
use xcm_builder::{AllowUnpaidExecutionFrom, FrameTransactionalProcessor, MintLocation};
use xcm_builder::{
AllowUnpaidExecutionFrom, EnsureDecodableXcm, FrameTransactionalProcessor, MintLocation,
};
type Block = frame_system::mocking::MockBlock<Test>;
......@@ -40,37 +39,10 @@ frame_support::construct_runtime!(
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Nonce = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}
parameter_types! {
......@@ -121,7 +93,7 @@ parameter_types! {
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = DevNull;
type XcmSender = EnsureDecodableXcm<DevNull>;
type AssetTransactor = AssetTransactor;
type OriginConverter = ();
type IsReserve = TrustedReserves;
......@@ -160,9 +132,8 @@ impl crate::Config for Test {
Ok(valid_destination)
}
fn worst_case_holding(depositable_count: u32) -> Assets {
crate::mock_worst_case_holding(
depositable_count,
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
generate_holding_assets(
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get() - depositable_count,
)
}
}
......
......@@ -19,9 +19,9 @@ use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf};
use codec::Encode;
use frame_benchmarking::{benchmarks, BenchmarkError};
use frame_support::{dispatch::GetDispatchInfo, traits::fungible::Inspect};
use sp_std::vec;
use sp_std::{prelude::*, vec};
use xcm::{
latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight},
latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight, MAX_ITEMS_IN_ASSETS},
DoubleEncoded,
};
use xcm_executor::{
......@@ -32,7 +32,6 @@ use xcm_executor::{
benchmarks! {
report_holding {
let (sender_account, sender_location) = account_and_location::<T>(1);
let holding = T::worst_case_holding(0);
let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
......@@ -42,14 +41,22 @@ benchmarks! {
);
let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
// generate holding and add possible required fees
let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
let mut holding = T::worst_case_holding(expected_assets_in_holding.len() as u32);
for a in expected_assets_in_holding.into_inner() {
holding.push(a);
}
holding
} else {
T::worst_case_holding(0)
};
let mut executor = new_executor::<T>(sender_location);
executor.set_holding(holding.clone().into());
if let Some(expected_fees_mode) = expected_fees_mode {
executor.set_fees_mode(expected_fees_mode);
}
if let Some(expected_assets_in_holding) = expected_assets_in_holding {
executor.set_holding(expected_assets_in_holding.into());
}
let instruction = Instruction::<XcmCallOf<T>>::ReportHolding {
response_info: QueryResponseInfo {
......@@ -57,8 +64,8 @@ benchmarks! {
query_id: Default::default(),
max_weight: Weight::MAX,
},
// Worst case is looking through all holdings for every asset explicitly.
assets: Definite(holding),
// Worst case is looking through all holdings for every asset explicitly - respecting the limit `MAX_ITEMS_IN_ASSETS`.
assets: Definite(holding.into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into()),
};
let xcm = Xcm(vec![instruction]);
......@@ -612,14 +619,19 @@ benchmarks! {
let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap();
let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
// generate holding and add possible required fees
let mut holding: Assets = asset.clone().into();
if let Some(expected_assets_in_holding) = expected_assets_in_holding {
for a in expected_assets_in_holding.into_inner() {
holding.push(a);
}
};
let mut executor = new_executor::<T>(owner);
executor.set_holding(asset.clone().into());
executor.set_holding(holding.into());
if let Some(expected_fees_mode) = expected_fees_mode {
executor.set_fees_mode(expected_fees_mode);
}
if let Some(expected_assets_in_holding) = expected_assets_in_holding {
executor.set_holding(expected_assets_in_holding.into());
}
let instruction = Instruction::LockAsset { asset, unlocker };
let xcm = Xcm(vec![instruction]);
......
......@@ -21,16 +21,15 @@ use codec::Decode;
use frame_support::{
derive_impl, parameter_types,
traits::{Contains, Everything, OriginTrait},
weights::Weight,
};
use sp_core::H256;
use sp_runtime::traits::{BlakeTwo256, IdentityLookup, TrailingZeroInput};
use sp_runtime::traits::TrailingZeroInput;
use xcm_builder::{
test_utils::{
AssetsInHolding, TestAssetExchanger, TestAssetLocker, TestAssetTrap,
TestSubscriptionService, TestUniversalAliases,
},
AliasForeignAccountId32, AllowUnpaidExecutionFrom, FrameTransactionalProcessor,
AliasForeignAccountId32, AllowUnpaidExecutionFrom, EnsureDecodableXcm,
FrameTransactionalProcessor,
};
use xcm_executor::traits::ConvertOrigin;
......@@ -45,37 +44,10 @@ frame_support::construct_runtime!(
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, u64::MAX));
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type Nonce = u64;
type Hash = H256;
type RuntimeCall = RuntimeCall;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
/// The benchmarks in this pallet should never need an asset transactor to begin with.
......@@ -110,7 +82,7 @@ type Aliasers = AliasForeignAccountId32<OnlyParachains>;
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = DevNull;
type XcmSender = EnsureDecodableXcm<DevNull>;
type AssetTransactor = NoAssetTransactor;
type OriginConverter = AlwaysSignedByDefault<RuntimeOrigin>;
type IsReserve = AllAssetLocationsPass;
......@@ -161,9 +133,8 @@ impl crate::Config for Test {
Ok(valid_destination)
}
fn worst_case_holding(depositable_count: u32) -> Assets {
crate::mock_worst_case_holding(
depositable_count,
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get(),
generate_holding_assets(
<XcmConfig as xcm_executor::Config>::MaxAssetsIntoHolding::get() - depositable_count,
)
}
}
......
......@@ -50,6 +50,8 @@ pub trait Config: frame_system::Config {
fn valid_destination() -> Result<Location, BenchmarkError>;
/// Worst case scenario for a holding account in this runtime.
/// - `depositable_count` specifies the count of assets we plan to add to the holding on top of
/// those generated by the `worst_case_holding` implementation.
fn worst_case_holding(depositable_count: u32) -> Assets;
}
......@@ -64,19 +66,22 @@ pub type AssetTransactorOf<T> = <<T as Config>::XcmConfig as XcmConfig>::AssetTr
/// The call type of executor's config. Should eventually resolve to the same overarching call type.
pub type XcmCallOf<T> = <<T as Config>::XcmConfig as XcmConfig>::RuntimeCall;
pub fn mock_worst_case_holding(depositable_count: u32, max_assets: u32) -> Assets {
pub fn generate_holding_assets(max_assets: u32) -> Assets {
let fungibles_amount: u128 = 100;
let holding_fungibles = max_assets / 2 - depositable_count;
let holding_non_fungibles = holding_fungibles;
let holding_fungibles = max_assets / 2;
let holding_non_fungibles = max_assets - holding_fungibles - 1; // -1 because of adding `Here` asset
// add count of `holding_fungibles`
(0..holding_fungibles)
.map(|i| {
Asset {
id: AssetId(GeneralIndex(i as u128).into()),
fun: Fungible(fungibles_amount * i as u128),
fun: Fungible(fungibles_amount * (i + 1) as u128), // non-zero amount
}
.into()
})
// add one more `Here` asset
.chain(core::iter::once(Asset { id: AssetId(Here.into()), fun: Fungible(u128::MAX) }))
// add count of `holding_non_fungibles`
.chain((0..holding_non_fungibles).map(|i| Asset {
id: AssetId(GeneralIndex(i as u128).into()),
fun: NonFungible(asset_instance_from(i)),
......
......@@ -58,7 +58,7 @@ impl xcm_executor::traits::ConvertLocation<u64> for AccountIdConverter {
}
parameter_types! {
pub UniversalLocation: InteriorLocation = Junction::Parachain(101).into();
pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis([1; 32])), Junction::Parachain(101)].into();
pub UnitWeightCost: Weight = Weight::from_parts(10, 10);
pub WeightPrice: (AssetId, u128, u128) = (AssetId(Here.into()), 1_000_000, 1024);
}
......
......@@ -16,7 +16,6 @@
use super::*;
use bounded_collections::{ConstU32, WeakBoundedVec};
use codec::Encode;
use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError, BenchmarkResult};
use frame_support::{assert_ok, weights::Weight};
use frame_system::RawOrigin;
......@@ -101,21 +100,6 @@ benchmarks! {
let versioned_msg = VersionedXcm::from(msg);
}: _<RuntimeOrigin<T>>(send_origin, Box::new(versioned_dest), Box::new(versioned_msg))
send_blob {
let send_origin =
T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let msg = Xcm::<()>(vec![ClearOrigin]);
let versioned_dest: VersionedLocation = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
)?
.into();
let versioned_msg = VersionedXcm::from(msg);
let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap();
}: _<RuntimeOrigin<T>>(send_origin, Box::new(versioned_dest), encoded_versioned_msg)
teleport_assets {
let (asset, destination) = T::teleportable_asset_and_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
......@@ -263,19 +247,6 @@ benchmarks! {
let versioned_msg = VersionedXcm::from(msg);
}: _<RuntimeOrigin<T>>(execute_origin, Box::new(versioned_msg), Weight::MAX)
execute_blob {
let execute_origin =
T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone())
.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
let msg = Xcm(vec![ClearOrigin]);
if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) {
return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))
}
let versioned_msg = VersionedXcm::from(msg);
let encoded_versioned_msg = versioned_msg.encode().try_into().unwrap();
}: _<RuntimeOrigin<T>>(execute_origin, encoded_versioned_msg, Weight::MAX)
force_xcm_version {
let loc = T::reachable_dest().ok_or(
BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)),
......
......@@ -45,13 +45,13 @@ use sp_runtime::{
AccountIdConversion, BadOrigin, BlakeTwo256, BlockNumberProvider, Dispatchable, Hash,
Saturating, Zero,
},
RuntimeDebug,
Either, RuntimeDebug,
};
use sp_std::{boxed::Box, marker::PhantomData, prelude::*, result::Result, vec};
use xcm::{latest::QueryResponseInfo, prelude::*};
use xcm_builder::{
ExecuteController, ExecuteControllerWeightInfo, MaxXcmEncodedSize, QueryController,
QueryControllerWeightInfo, SendController, SendControllerWeightInfo,
ExecuteController, ExecuteControllerWeightInfo, QueryController, QueryControllerWeightInfo,
SendController, SendControllerWeightInfo,
};
use xcm_executor::{
traits::{
......@@ -87,8 +87,6 @@ pub trait WeightInfo {
fn new_query() -> Weight;
fn take_response() -> Weight;
fn claim_assets() -> Weight;
fn execute_blob() -> Weight;
fn send_blob() -> Weight;
}
/// fallback implementation
......@@ -173,14 +171,6 @@ impl WeightInfo for TestWeightInfo {
fn claim_assets() -> Weight {
Weight::from_parts(100_000_000, 0)
}
fn execute_blob() -> Weight {
Weight::from_parts(100_000_000, 0)
}
fn send_blob() -> Weight {
Weight::from_parts(100_000_000, 0)
}
}
#[frame_support::pallet]
......@@ -296,49 +286,76 @@ pub mod pallet {
}
impl<T: Config> ExecuteControllerWeightInfo for Pallet<T> {
fn execute_blob() -> Weight {
T::WeightInfo::execute_blob()
fn execute() -> Weight {
T::WeightInfo::execute()
}
}
impl<T: Config> ExecuteController<OriginFor<T>, <T as Config>::RuntimeCall> for Pallet<T> {
type WeightInfo = Self;
fn execute_blob(
fn execute(
origin: OriginFor<T>,
encoded_message: BoundedVec<u8, MaxXcmEncodedSize>,
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
max_weight: Weight,
) -> Result<Weight, DispatchErrorWithPostInfo> {
log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
let outcome = (|| {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let message =
VersionedXcm::<<T as Config>::RuntimeCall>::decode(&mut &encoded_message[..])
.map_err(|error| {
log::error!(target: "xcm::execute_blob", "Unable to decode XCM, error: {:?}", error);
Error::<T>::UnableToDecode
let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let value = (origin_location, message);
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, message) = value;
Ok(T::XcmExecutor::prepare_and_execute(
origin_location,
message,
&mut hash,
max_weight,
max_weight,
))
})()
.map_err(|e: DispatchError| {
e.with_weight(<Self::WeightInfo as ExecuteControllerWeightInfo>::execute())
})?;
Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
let weight_used = outcome.weight_used();
outcome.ensure_complete().map_err(|error| {
log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
Error::<T>::LocalExecutionIncomplete.with_weight(
weight_used.saturating_add(
<Self::WeightInfo as ExecuteControllerWeightInfo>::execute(),
),
)
})?;
Self::execute_base(origin_location, Box::new(message), max_weight)
Ok(weight_used)
}
}
impl<T: Config> SendControllerWeightInfo for Pallet<T> {
fn send_blob() -> Weight {
T::WeightInfo::send_blob()
fn send() -> Weight {
T::WeightInfo::send()
}
}
impl<T: Config> SendController<OriginFor<T>> for Pallet<T> {
type WeightInfo = Self;
fn send_blob(
fn send(
origin: OriginFor<T>,
dest: Box<VersionedLocation>,
encoded_message: BoundedVec<u8, MaxXcmEncodedSize>,
message: Box<VersionedXcm<()>>,
) -> Result<XcmHash, DispatchError> {
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
let message =
VersionedXcm::<()>::decode(&mut &encoded_message[..]).map_err(|error| {
log::error!(target: "xcm::send_blob", "Unable to decode XCM, error: {:?}", error);
Error::<T>::UnableToDecode
})?;
Self::send_base(origin_location, dest, Box::new(message))
let interior: Junctions =
origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
.map_err(Error::<T>::from)?;
let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
Self::deposit_event(e);
Ok(message_id)
}
}
......@@ -547,13 +564,6 @@ pub mod pallet {
/// Local XCM execution incomplete.
#[codec(index = 24)]
LocalExecutionIncomplete,
/// Could not decode XCM.
#[codec(index = 25)]
UnableToDecode,
/// XCM encoded length is too large.
/// Returned when an XCM encoded length is larger than `MaxXcmEncodedSize`.
#[codec(index = 26)]
XcmTooLarge,
}
impl<T: Config> From<SendError> for Error<T> {
......@@ -890,72 +900,15 @@ pub mod pallet {
}
}
impl<T: Config> Pallet<T> {
/// Underlying logic for both [`execute_blob`] and [`execute`].
fn execute_base(
origin_location: Location,
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
max_weight: Weight,
) -> Result<Weight, DispatchErrorWithPostInfo> {
log::trace!(target: "xcm::pallet_xcm::execute", "message {:?}, max_weight {:?}", message, max_weight);
let outcome = (|| {
let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
let message = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let value = (origin_location, message);
ensure!(T::XcmExecuteFilter::contains(&value), Error::<T>::Filtered);
let (origin_location, message) = value;
Ok(T::XcmExecutor::prepare_and_execute(
origin_location,
message,
&mut hash,
max_weight,
max_weight,
))
})()
.map_err(|e: DispatchError| e.with_weight(T::WeightInfo::execute()))?;
Self::deposit_event(Event::Attempted { outcome: outcome.clone() });
let weight_used = outcome.weight_used();
outcome.ensure_complete().map_err(|error| {
log::error!(target: "xcm::pallet_xcm::execute", "XCM execution failed with error {:?}", error);
Error::<T>::LocalExecutionIncomplete
.with_weight(weight_used.saturating_add(T::WeightInfo::execute()))
})?;
Ok(weight_used)
}
/// Underlying logic for both [`send_blob`] and [`send`].
fn send_base(
origin_location: Location,
dest: Box<VersionedLocation>,
message: Box<VersionedXcm<()>>,
) -> Result<XcmHash, DispatchError> {
let interior: Junctions =
origin_location.clone().try_into().map_err(|_| Error::<T>::InvalidOrigin)?;
let dest = Location::try_from(*dest).map_err(|()| Error::<T>::BadVersion)?;
let message: Xcm<()> = (*message).try_into().map_err(|()| Error::<T>::BadVersion)?;
let message_id = Self::send_xcm(interior, dest.clone(), message.clone())
.map_err(Error::<T>::from)?;
let e = Event::Sent { origin: origin_location, destination: dest, message, message_id };
Self::deposit_event(e);
Ok(message_id)
}
}
#[pallet::call(weight(<T as Config>::WeightInfo))]
impl<T: Config> Pallet<T> {
/// WARNING: DEPRECATED. `send` will be removed after June 2024. Use `send_blob` instead.
#[allow(deprecated)]
#[deprecated(note = "`send` will be removed after June 2024. Use `send_blob` instead.")]
#[pallet::call_index(0)]
pub fn send(
origin: OriginFor<T>,
dest: Box<VersionedLocation>,
message: Box<VersionedXcm<()>>,
) -> DispatchResult {
let origin_location = T::SendXcmOrigin::ensure_origin(origin)?;
Self::send_base(origin_location, dest, message)?;
<Self as SendController<_>>::send(origin, dest, message)?;
Ok(())
}
......@@ -1052,13 +1005,6 @@ pub mod pallet {
/// No more than `max_weight` will be used in its attempted execution. If this is less than
/// the maximum amount of weight that the message could take to be executed, then no
/// execution attempt will be made.
///
/// WARNING: DEPRECATED. `execute` will be removed after June 2024. Use `execute_blob`
/// instead.
#[allow(deprecated)]
#[deprecated(
note = "`execute` will be removed after June 2024. Use `execute_blob` instead."
)]
#[pallet::call_index(3)]
#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute()))]
pub fn execute(
......@@ -1066,8 +1012,8 @@ pub mod pallet {
message: Box<VersionedXcm<<T as Config>::RuntimeCall>>,
max_weight: Weight,
) -> DispatchResultWithPostInfo {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let weight_used = Self::execute_base(origin_location, message, max_weight)?;
let weight_used =
<Self as ExecuteController<_, _>>::execute(origin, message, max_weight)?;
Ok(Some(weight_used.saturating_add(T::WeightInfo::execute())).into())
}
......@@ -1311,7 +1257,7 @@ pub mod pallet {
Self::do_transfer_assets(
origin,
dest,
beneficiary,
Either::Left(beneficiary),
assets,
assets_transfer_type,
fee_asset_item,
......@@ -1362,47 +1308,6 @@ pub mod pallet {
Ok(())
}
/// Execute an XCM from a local, signed, origin.
///
/// An event is deposited indicating whether the message could be executed completely
/// or only partially.
///
/// No more than `max_weight` will be used in its attempted execution. If this is less than
/// the maximum amount of weight that the message could take to be executed, then no
/// execution attempt will be made.
///
/// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`].
#[pallet::call_index(13)]
#[pallet::weight(max_weight.saturating_add(T::WeightInfo::execute_blob()))]
pub fn execute_blob(
origin: OriginFor<T>,
encoded_message: BoundedVec<u8, MaxXcmEncodedSize>,
max_weight: Weight,
) -> DispatchResultWithPostInfo {
let weight_used = <Self as ExecuteController<_, _>>::execute_blob(
origin,
encoded_message,
max_weight,
)?;
Ok(Some(weight_used.saturating_add(T::WeightInfo::execute_blob())).into())
}
/// Send an XCM from a local, signed, origin.
///
/// The destination, `dest`, will receive this message with a `DescendOrigin` instruction
/// that makes the origin of the message be the origin on this system.
///
/// The message is passed in encoded. It needs to be decodable as a [`VersionedXcm`].
#[pallet::call_index(14)]
pub fn send_blob(
origin: OriginFor<T>,
dest: Box<VersionedLocation>,
encoded_message: BoundedVec<u8, MaxXcmEncodedSize>,
) -> DispatchResult {
<Self as SendController<_>>::send_blob(origin, dest, encoded_message)?;
Ok(())
}
/// Transfer assets from the local chain to the destination chain using explicit transfer
/// types for assets and fees.
///
......@@ -1421,50 +1326,60 @@ pub mod pallet {
/// - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to
/// mint/teleport assets and deposit them to `beneficiary`.
///
/// Fee payment on the source, destination and all intermediary hops, is specified through
/// `fees_id`, but make sure enough of the specified `fees_id` asset is included in the
/// given list of `assets`. `fees_id` should be enough to pay for `weight_limit`. If more
/// weight is needed than `weight_limit`, then the operation will fail and the sent assets
/// may be at risk.
/// On the destination chain, as well as any intermediary hops, `BuyExecution` is used to
/// buy execution using transferred `assets` identified by `remote_fees_id`.
/// Make sure enough of the specified `remote_fees_id` asset is included in the given list
/// of `assets`. `remote_fees_id` should be enough to pay for `weight_limit`. If more weight
/// is needed than `weight_limit`, then the operation will fail and the sent assets may be
/// at risk.
///
/// `remote_fees_id` may use different transfer type than rest of `assets` and can be
/// specified through `fees_transfer_type`.
///
/// `fees_id` may use different transfer type than rest of `assets` and can be specified
/// through `fees_transfer_type`.
/// The caller needs to specify what should happen to the transferred assets once they reach
/// the `dest` chain. This is done through the `custom_xcm_on_dest` parameter, which
/// contains the instructions to execute on `dest` as a final step.
/// This is usually as simple as:
/// `Xcm(vec![DepositAsset { assets: Wild(AllCounted(assets.len())), beneficiary }])`,
/// but could be something more exotic like sending the `assets` even further.
///
/// - `origin`: Must be capable of withdrawing the `assets` and executing XCM.
/// - `dest`: Destination context for the assets. Will typically be `[Parent,
/// Parachain(..)]` to send from parachain to parachain, or `[Parachain(..)]` to send from
/// relay to parachain, or `(parents: 2, (GlobalConsensus(..), ..))` to send from
/// parachain across a bridge to another ecosystem destination.
/// - `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will
/// generally be an `AccountId32` value.
/// - `assets`: The assets to be withdrawn. This should include the assets used to pay the
/// fee on the `dest` (and possibly reserve) chains.
/// - `assets_transfer_type`: The XCM `TransferType` used to transfer the `assets`.
/// - `fees_id`: One of the included `assets` to be be used to pay fees.
/// - `remote_fees_id`: One of the included `assets` to be be used to pay fees.
/// - `fees_transfer_type`: The XCM `TransferType` used to transfer the `fees` assets.
/// - `custom_xcm_on_dest`: The XCM to be executed on `dest` chain as the last step of the
/// transfer, which also determines what happens to the assets on the destination chain.
/// - `weight_limit`: The remote-side weight limit, if any, for the XCM fee purchase.
#[pallet::call_index(15)]
#[pallet::call_index(13)]
#[pallet::weight(T::WeightInfo::transfer_assets())]
pub fn transfer_assets_using_type(
pub fn transfer_assets_using_type_and_then(
origin: OriginFor<T>,
dest: Box<VersionedLocation>,
beneficiary: Box<VersionedLocation>,
assets: Box<VersionedAssets>,
assets_transfer_type: Box<TransferType>,
fees_id: Box<VersionedAssetId>,
remote_fees_id: Box<VersionedAssetId>,
fees_transfer_type: Box<TransferType>,
custom_xcm_on_dest: Box<VersionedXcm<()>>,
weight_limit: WeightLimit,
) -> DispatchResult {
let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?;
let dest: Location = (*dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
let beneficiary: Location =
(*beneficiary).try_into().map_err(|()| Error::<T>::BadVersion)?;
let assets: Assets = (*assets).try_into().map_err(|()| Error::<T>::BadVersion)?;
let fees_id: AssetId = (*fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
let fees_id: AssetId =
(*remote_fees_id).try_into().map_err(|()| Error::<T>::BadVersion)?;
let remote_xcm: Xcm<()> =
(*custom_xcm_on_dest).try_into().map_err(|()| Error::<T>::BadVersion)?;
log::debug!(
target: "xcm::pallet_xcm::transfer_assets_using_type",
"origin {:?}, dest {:?}, beneficiary {:?}, assets {:?} through {:?}, fees-id {:?} through {:?}",
origin_location, dest, beneficiary, assets, assets_transfer_type, fees_id, fees_transfer_type,
target: "xcm::pallet_xcm::transfer_assets_using_type_and_then",
"origin {origin_location:?}, dest {dest:?}, assets {assets:?} through {assets_transfer_type:?}, \
remote_fees_id {fees_id:?} through {fees_transfer_type:?}, \
custom_xcm_on_dest {remote_xcm:?}, weight-limit {weight_limit:?}",
);
let assets = assets.into_inner();
......@@ -1475,7 +1390,7 @@ pub mod pallet {
Self::do_transfer_assets(
origin_location,
dest,
beneficiary,
Either::Right(remote_xcm),
assets,
*assets_transfer_type,
fee_asset_index,
......@@ -1650,7 +1565,7 @@ impl<T: Config> Pallet<T> {
let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
origin.clone(),
dest.clone(),
beneficiary,
Either::Left(beneficiary),
assets,
assets_transfer_type,
FeesHandling::Batched { fees },
......@@ -1692,7 +1607,7 @@ impl<T: Config> Pallet<T> {
let (local_xcm, remote_xcm) = Self::build_xcm_transfer_type(
origin_location.clone(),
dest.clone(),
beneficiary,
Either::Left(beneficiary),
assets,
TransferType::Teleport,
FeesHandling::Batched { fees },
......@@ -1704,7 +1619,7 @@ impl<T: Config> Pallet<T> {
fn do_transfer_assets(
origin: Location,
dest: Location,
beneficiary: Location,
beneficiary: Either<Location, Xcm<()>>,
mut assets: Vec<Asset>,
assets_transfer_type: TransferType,
fee_asset_index: usize,
......@@ -1770,7 +1685,7 @@ impl<T: Config> Pallet<T> {
fn build_xcm_transfer_type(
origin: Location,
dest: Location,
beneficiary: Location,
beneficiary: Either<Location, Xcm<()>>,
assets: Vec<Asset>,
transfer_type: TransferType,
fees: FeesHandling<T>,
......@@ -1782,57 +1697,51 @@ impl<T: Config> Pallet<T> {
fees_handling {:?}, weight_limit: {:?}",
origin, dest, beneficiary, assets, transfer_type, fees, weight_limit,
);
Ok(match transfer_type {
TransferType::LocalReserve => {
let (local, remote) = Self::local_reserve_transfer_programs(
match transfer_type {
TransferType::LocalReserve => Self::local_reserve_transfer_programs(
origin.clone(),
dest.clone(),
beneficiary,
assets,
fees,
weight_limit,
)?;
(local, Some(remote))
},
TransferType::DestinationReserve => {
let (local, remote) = Self::destination_reserve_transfer_programs(
)
.map(|(local, remote)| (local, Some(remote))),
TransferType::DestinationReserve => Self::destination_reserve_transfer_programs(
origin.clone(),
dest.clone(),
beneficiary,
assets,
fees,
weight_limit,
)?;
(local, Some(remote))
},
)
.map(|(local, remote)| (local, Some(remote))),
TransferType::RemoteReserve(reserve) => {
let fees = match fees {
FeesHandling::Batched { fees } => fees,
_ => return Err(Error::<T>::InvalidAssetUnsupportedReserve.into()),
};
let local = Self::remote_reserve_transfer_program(
Self::remote_reserve_transfer_program(
origin.clone(),
reserve.try_into().map_err(|()| Error::<T>::BadVersion)?,
dest.clone(),
beneficiary,
dest.clone(),
assets,
fees,
weight_limit,
)?;
(local, None)
)
.map(|local| (local, None))
},
TransferType::Teleport => {
let (local, remote) = Self::teleport_assets_program(
TransferType::Teleport => Self::teleport_assets_program(
origin.clone(),
dest.clone(),
beneficiary,
assets,
fees,
weight_limit,
)?;
(local, Some(remote))
},
})
)
.map(|(local, remote)| (local, Some(remote))),
}
}
fn execute_xcm_transfer(
......@@ -1947,7 +1856,7 @@ impl<T: Config> Pallet<T> {
fn local_reserve_transfer_programs(
origin: Location,
dest: Location,
beneficiary: Location,
beneficiary: Either<Location, Xcm<()>>,
assets: Vec<Asset>,
fees: FeesHandling<T>,
weight_limit: WeightLimit,
......@@ -1980,10 +1889,16 @@ impl<T: Config> Pallet<T> {
]);
// handle fees
Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
let custom_remote_xcm = match beneficiary {
Either::Right(custom_xcm) => custom_xcm,
Either::Left(beneficiary) => {
// deposit all remaining assets in holding to `beneficiary` location
xcm_on_dest
.inner_mut()
.push(DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary });
Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
},
};
xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
Ok((local_execute_xcm, xcm_on_dest))
}
......@@ -2022,7 +1937,7 @@ impl<T: Config> Pallet<T> {
fn destination_reserve_transfer_programs(
origin: Location,
dest: Location,
beneficiary: Location,
beneficiary: Either<Location, Xcm<()>>,
assets: Vec<Asset>,
fees: FeesHandling<T>,
weight_limit: WeightLimit,
......@@ -2058,10 +1973,15 @@ impl<T: Config> Pallet<T> {
// handle fees
Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
let custom_remote_xcm = match beneficiary {
Either::Right(custom_xcm) => custom_xcm,
Either::Left(beneficiary) => {
// deposit all remaining assets in holding to `beneficiary` location
xcm_on_dest
.inner_mut()
.push(DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary });
Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
},
};
xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
Ok((local_execute_xcm, xcm_on_dest))
}
......@@ -2070,8 +1990,8 @@ impl<T: Config> Pallet<T> {
fn remote_reserve_transfer_program(
origin: Location,
reserve: Location,
beneficiary: Either<Location, Xcm<()>>,
dest: Location,
beneficiary: Location,
assets: Vec<Asset>,
fees: Asset,
weight_limit: WeightLimit,
......@@ -2096,10 +2016,17 @@ impl<T: Config> Pallet<T> {
// identifies `dest` as seen by `reserve`
let dest = dest.reanchored(&reserve, &context).map_err(|_| Error::<T>::CannotReanchor)?;
// xcm to be executed at dest
let xcm_on_dest = Xcm(vec![
BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() },
DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary },
]);
let mut xcm_on_dest =
Xcm(vec![BuyExecution { fees: dest_fees, weight_limit: weight_limit.clone() }]);
// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
let custom_xcm_on_dest = match beneficiary {
Either::Right(custom_xcm) => custom_xcm,
Either::Left(beneficiary) => {
// deposit all remaining assets in holding to `beneficiary` location
Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
},
};
xcm_on_dest.0.extend(custom_xcm_on_dest.into_iter());
// xcm to be executed on reserve
let xcm_on_reserve = Xcm(vec![
BuyExecution { fees: reserve_fees, weight_limit },
......@@ -2171,7 +2098,7 @@ impl<T: Config> Pallet<T> {
fn teleport_assets_program(
origin: Location,
dest: Location,
beneficiary: Location,
beneficiary: Either<Location, Xcm<()>>,
assets: Vec<Asset>,
fees: FeesHandling<T>,
weight_limit: WeightLimit,
......@@ -2231,10 +2158,16 @@ impl<T: Config> Pallet<T> {
]);
// handle fees
Self::add_fees_to_xcm(dest, fees, weight_limit, &mut local_execute_xcm, &mut xcm_on_dest)?;
// Use custom XCM on remote chain, or just default to depositing everything to beneficiary.
let custom_remote_xcm = match beneficiary {
Either::Right(custom_xcm) => custom_xcm,
Either::Left(beneficiary) => {
// deposit all remaining assets in holding to `beneficiary` location
xcm_on_dest
.inner_mut()
.push(DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary });
Xcm(vec![DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }])
},
};
xcm_on_dest.0.extend(custom_remote_xcm.into_iter());
Ok((local_execute_xcm, xcm_on_dest))
}
......
......@@ -33,10 +33,11 @@ use xcm::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds,
FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete,
MatchedConvertedConcreteId, NoChecking, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount,
ChildSystemParachainAsSuperuser, DescribeAllTerminal, EnsureDecodableXcm, FixedRateOfFungible,
FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter,
HashedDescription, IsConcrete, MatchedConvertedConcreteId, NoChecking,
SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
XcmFeeManagerFromComponents, XcmFeeToAccount,
};
use xcm_executor::{
traits::{Identity, JustTry},
......@@ -413,7 +414,7 @@ parameter_types! {
)),
};
pub const AnyNetwork: Option<NetworkId> = None;
pub UniversalLocation: InteriorLocation = Here;
pub UniversalLocation: InteriorLocation = GlobalConsensus(ByGenesis([0; 32])).into();
pub UnitWeightCost: u64 = 1_000;
pub CheckingAccount: AccountId = XcmPallet::check_account();
}
......@@ -488,7 +489,8 @@ pub type Barrier = (
AllowSubscriptionsFrom<Everything>,
);
pub type XcmRouter = (TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm);
pub type XcmRouter =
EnsureDecodableXcm<(TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm)>;
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
......
......@@ -20,10 +20,10 @@ pub(crate) mod assets_transfer;
use crate::{
mock::*, pallet::SupportedVersion, AssetTraps, Config, CurrentMigration, Error,
LatestVersionedLocation, Pallet, Queries, QueryStatus, VersionDiscoveryQueue,
VersionMigrationStage, VersionNotifiers, VersionNotifyTargets, WeightInfo,
ExecuteControllerWeightInfo, LatestVersionedLocation, Pallet, Queries, QueryStatus,
VersionDiscoveryQueue, VersionMigrationStage, VersionNotifiers, VersionNotifyTargets,
WeightInfo,
};
use codec::Encode;
use frame_support::{
assert_err_ignore_postinfo, assert_noop, assert_ok,
traits::{Currency, Hooks},
......@@ -305,12 +305,11 @@ fn send_works() {
]);
let versioned_dest = Box::new(RelayLocation::get().into());
let versioned_message = VersionedXcm::from(message.clone());
let encoded_versioned_message = versioned_message.encode().try_into().unwrap();
assert_ok!(XcmPallet::send_blob(
let versioned_message = Box::new(VersionedXcm::from(message.clone()));
assert_ok!(XcmPallet::send(
RuntimeOrigin::signed(ALICE),
versioned_dest,
encoded_versioned_message
versioned_message
));
let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
.into_iter()
......@@ -342,16 +341,16 @@ fn send_fails_when_xcm_router_blocks() {
];
new_test_ext_with_balances(balances).execute_with(|| {
let sender: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
let message = Xcm::<()>(vec![
let message = Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
buy_execution((Parent, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender },
]);
assert_noop!(
XcmPallet::send_blob(
XcmPallet::send(
RuntimeOrigin::signed(ALICE),
Box::new(Location::ancestor(8).into()),
VersionedXcm::from(message.clone()).encode().try_into().unwrap(),
Box::new(VersionedXcm::from(message.clone())),
),
crate::Error::<Test>::SendFailure
);
......@@ -372,16 +371,13 @@ fn execute_withdraw_to_deposit_works() {
let weight = BaseXcmWeight::get() * 3;
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
assert_ok!(XcmPallet::execute_blob(
assert_ok!(XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::from(Xcm::<RuntimeCall>(vec![
Box::new(VersionedXcm::from(Xcm(vec![
WithdrawAsset((Here, SEND_AMOUNT).into()),
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]))
.encode()
.try_into()
.unwrap(),
]))),
weight
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
......@@ -403,21 +399,18 @@ fn trapped_assets_can_be_claimed() {
let weight = BaseXcmWeight::get() * 6;
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
assert_ok!(XcmPallet::execute_blob(
assert_ok!(XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::from(Xcm(vec![
Box::new(VersionedXcm::from(Xcm(vec![
WithdrawAsset((Here, SEND_AMOUNT).into()),
buy_execution((Here, SEND_AMOUNT)),
// Don't propagated the error into the result.
SetErrorHandler(Xcm::<RuntimeCall>(vec![ClearError])),
SetErrorHandler(Xcm(vec![ClearError])),
// This will make an error.
Trap(0),
// This would succeed, but we never get to it.
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() },
]))
.encode()
.try_into()
.unwrap(),
]))),
weight
));
let source: Location = Junction::AccountId32 { network: None, id: ALICE.into() }.into();
......@@ -444,16 +437,13 @@ fn trapped_assets_can_be_claimed() {
assert_eq!(trapped, expected);
let weight = BaseXcmWeight::get() * 3;
assert_ok!(XcmPallet::execute_blob(
assert_ok!(XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::from(Xcm::<RuntimeCall>(vec![
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest.clone() },
]))
.encode()
.try_into()
.unwrap(),
]))),
weight
));
......@@ -463,16 +453,13 @@ fn trapped_assets_can_be_claimed() {
// Can't claim twice.
assert_err_ignore_postinfo!(
XcmPallet::execute_blob(
XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::from(Xcm::<RuntimeCall>(vec![
Box::new(VersionedXcm::from(Xcm(vec![
ClaimAsset { assets: (Here, SEND_AMOUNT).into(), ticket: Here.into() },
buy_execution((Here, SEND_AMOUNT)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]))
.encode()
.try_into()
.unwrap(),
]))),
weight
),
Error::<Test>::LocalExecutionIncomplete
......@@ -489,9 +476,9 @@ fn claim_assets_works() {
let trapping_program =
Xcm::<RuntimeCall>::builder_unsafe().withdraw_asset((Here, SEND_AMOUNT)).build();
// Even though assets are trapped, the extrinsic returns success.
assert_ok!(XcmPallet::execute_blob(
assert_ok!(XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::V4(trapping_program).encode().try_into().unwrap(),
Box::new(VersionedXcm::V4(trapping_program)),
BaseXcmWeight::get() * 2,
));
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE - SEND_AMOUNT);
......@@ -544,9 +531,9 @@ fn incomplete_execute_reverts_side_effects() {
assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE);
let amount_to_send = INITIAL_BALANCE - ExistentialDeposit::get();
let assets: Assets = (Here, amount_to_send).into();
let result = XcmPallet::execute_blob(
let result = XcmPallet::execute(
RuntimeOrigin::signed(ALICE),
VersionedXcm::from(Xcm::<RuntimeCall>(vec![
Box::new(VersionedXcm::from(Xcm(vec![
// Withdraw + BuyExec + Deposit should work
WithdrawAsset(assets.clone()),
buy_execution(assets.inner()[0].clone()),
......@@ -554,10 +541,7 @@ fn incomplete_execute_reverts_side_effects() {
// Withdrawing once more will fail because of InsufficientBalance, and we expect to
// revert the effects of the above instructions as well
WithdrawAsset(assets),
]))
.encode()
.try_into()
.unwrap(),
]))),
weight,
);
// all effects are reverted and balances unchanged for either sender or receiver
......@@ -569,7 +553,7 @@ fn incomplete_execute_reverts_side_effects() {
Err(sp_runtime::DispatchErrorWithPostInfo {
post_info: frame_support::dispatch::PostDispatchInfo {
actual_weight: Some(
<<Test as crate::Config>::WeightInfo>::execute_blob() + weight
<Pallet<Test> as ExecuteControllerWeightInfo>::execute() + weight
),
pays_fee: frame_support::dispatch::Pays::Yes,
},
......
......@@ -25,7 +25,7 @@
extern crate alloc;
use derivative::Derivative;
use parity_scale_codec::{Decode, Encode, Error as CodecError, Input, MaxEncodedLen};
use parity_scale_codec::{Decode, DecodeLimit, Encode, Error as CodecError, Input, MaxEncodedLen};
use scale_info::TypeInfo;
pub mod v2;
......@@ -48,9 +48,6 @@ mod tests;
/// Maximum nesting level for XCM decoding.
pub const MAX_XCM_DECODE_DEPTH: u32 = 8;
/// Maximum encoded size.
/// See `decoding_respects_limit` test for more reasoning behind this value.
pub const MAX_XCM_ENCODED_SIZE: u32 = 12402;
/// A version of XCM.
pub type Version = u32;
......@@ -456,6 +453,23 @@ impl<C> IdentifyVersion for VersionedXcm<C> {
}
}
impl<C> VersionedXcm<C> {
/// Checks that the XCM is decodable with `MAX_XCM_DECODE_DEPTH`. Consequently, it also checks
/// all decode implementations and limits, such as MAX_ITEMS_IN_ASSETS or
/// MAX_INSTRUCTIONS_TO_DECODE.
///
/// Note that this uses the limit of the sender - not the receiver. It is a best effort.
pub fn validate_xcm_nesting(&self) -> Result<(), ()> {
self.using_encoded(|mut enc| {
Self::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut enc).map(|_| ())
})
.map_err(|e| {
log::error!(target: "xcm::validate_xcm_nesting", "Decode error: {e:?} for xcm: {self:?}!");
()
})
}
}
impl<RuntimeCall> From<v2::Xcm<RuntimeCall>> for VersionedXcm<RuntimeCall> {
fn from(x: v2::Xcm<RuntimeCall>) -> Self {
VersionedXcm::V2(x)
......@@ -704,3 +718,77 @@ fn size_limits() {
}
assert!(!test_failed);
}
#[test]
fn validate_xcm_nesting_works() {
use crate::latest::{
prelude::{GeneralIndex, ReserveAssetDeposited, SetAppendix},
Assets, Xcm, MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_ASSETS,
};
// closure generates assets of `count`
let assets = |count| {
let mut assets = Assets::new();
for i in 0..count {
assets.push((GeneralIndex(i as u128), 100).into());
}
assets
};
// closer generates `Xcm` with nested instructions of `depth`
let with_instr = |depth| {
let mut xcm = Xcm::<()>(vec![]);
for _ in 0..depth - 1 {
xcm = Xcm::<()>(vec![SetAppendix(xcm)]);
}
xcm
};
// `MAX_INSTRUCTIONS_TO_DECODE` check
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
(MAX_INSTRUCTIONS_TO_DECODE - 1) as usize
]))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
MAX_INSTRUCTIONS_TO_DECODE as usize
]))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![
ReserveAssetDeposited(assets(1));
(MAX_INSTRUCTIONS_TO_DECODE + 1) as usize
]))
.validate_xcm_nesting()
.is_err());
// `MAX_XCM_DECODE_DEPTH` check
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH - 1))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(with_instr(MAX_XCM_DECODE_DEPTH + 1))
.validate_xcm_nesting()
.is_err());
// `MAX_ITEMS_IN_ASSETS` check
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS
))]))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS - 1
))]))
.validate_xcm_nesting()
.is_ok());
assert!(VersionedXcm::<()>::from(Xcm(vec![ReserveAssetDeposited(assets(
MAX_ITEMS_IN_ASSETS + 1
))]))
.validate_xcm_nesting()
.is_err());
}
......@@ -825,7 +825,9 @@ impl MultiAssets {
/// Prepend a `MultiLocation` to any concrete asset items, giving it a new root location.
pub fn prepend_with(&mut self, prefix: &MultiLocation) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))?;
self.0.sort();
Ok(())
}
/// Mutate the location of the asset identifier if concrete, giving it the same location
......@@ -1213,8 +1215,73 @@ mod tests {
vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
// decoding respects limits and sorting
assert!(assets
.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
.is_ok());
assert!(assets.reanchor(&dest, reanchor_context).is_ok());
assert_eq!(assets, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored].into());
assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]);
// decoding respects limits and sorting
assert!(assets
.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
.is_ok());
}
#[test]
fn prepend_preserves_sorting() {
use super::*;
use alloc::vec;
let prefix = MultiLocation::new(0, X1(Parachain(1000)));
let asset_1: MultiAsset =
(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
let mut asset_1_prepended = asset_1.clone();
assert!(asset_1_prepended.prepend_with(&prefix).is_ok());
// changes interior X2->X3
assert_eq!(
asset_1_prepended,
(MultiLocation::new(0, X3(Parachain(1000), PalletInstance(50), GeneralIndex(1))), 10)
.into()
);
let asset_2: MultiAsset =
(MultiLocation::new(1, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
let mut asset_2_prepended = asset_2.clone();
assert!(asset_2_prepended.prepend_with(&prefix).is_ok());
// changes parent
assert_eq!(
asset_2_prepended,
(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into()
);
let asset_3: MultiAsset =
(MultiLocation::new(2, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
let mut asset_3_prepended = asset_3.clone();
assert!(asset_3_prepended.prepend_with(&prefix).is_ok());
// changes parent
assert_eq!(
asset_3_prepended,
(MultiLocation::new(1, X2(PalletInstance(50), GeneralIndex(1))), 10).into()
);
// `From` impl does sorting.
let mut assets: MultiAssets = vec![asset_1, asset_2, asset_3].into();
// decoding respects limits and sorting
assert!(assets
.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
.is_ok());
// let's do `prepend_with`
assert!(assets.prepend_with(&prefix).is_ok());
assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]);
// decoding respects limits and sorting
assert!(assets
.using_encoded(|mut enc| MultiAssets::decode(&mut enc).map(|_| ()))
.is_ok());
}
#[test]
......
......@@ -723,7 +723,9 @@ impl Assets {
/// Prepend a `Location` to any concrete asset items, giving it a new root location.
pub fn prepend_with(&mut self, prefix: &Location) -> Result<(), ()> {
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))
self.0.iter_mut().try_for_each(|i| i.prepend_with(prefix))?;
self.0.sort();
Ok(())
}
/// Return a reference to an item at a specific index or `None` if it doesn't exist.
......@@ -1035,8 +1037,61 @@ mod tests {
let mut assets: Assets = vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
// decoding respects limits and sorting
assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
assert!(assets.reanchor(&dest, &reanchor_context).is_ok());
assert_eq!(assets, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored].into());
assert_eq!(assets.0, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored]);
// decoding respects limits and sorting
assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
}
#[test]
fn prepend_preserves_sorting() {
use super::*;
use alloc::vec;
let prefix = Location::new(0, [Parachain(1000)]);
let asset_1: Asset = (Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into();
let mut asset_1_prepended = asset_1.clone();
assert!(asset_1_prepended.prepend_with(&prefix).is_ok());
// changes interior X2->X3
assert_eq!(
asset_1_prepended,
(Location::new(0, [Parachain(1000), PalletInstance(50), GeneralIndex(1)]), 10).into()
);
let asset_2: Asset = (Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into();
let mut asset_2_prepended = asset_2.clone();
assert!(asset_2_prepended.prepend_with(&prefix).is_ok());
// changes parent
assert_eq!(
asset_2_prepended,
(Location::new(0, [PalletInstance(50), GeneralIndex(1)]), 10).into()
);
let asset_3: Asset = (Location::new(2, [PalletInstance(50), GeneralIndex(1)]), 10).into();
let mut asset_3_prepended = asset_3.clone();
assert!(asset_3_prepended.prepend_with(&prefix).is_ok());
// changes parent
assert_eq!(
asset_3_prepended,
(Location::new(1, [PalletInstance(50), GeneralIndex(1)]), 10).into()
);
// `From` impl does sorting.
let mut assets: Assets = vec![asset_1, asset_2, asset_3].into();
// decoding respects limits and sorting
assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
// let's do `prepend_with`
assert!(assets.prepend_with(&prefix).is_ok());
assert_eq!(assets.0, vec![asset_2_prepended, asset_1_prepended, asset_3_prepended]);
// decoding respects limits and sorting
assert!(assets.using_encoded(|mut enc| Assets::decode(&mut enc).map(|_| ())).is_ok());
}
#[test]
......
......@@ -1488,21 +1488,7 @@ mod tests {
let encoded = big_xcm.encode();
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
let mut many_assets = Assets::new();
for index in 0..MAX_ITEMS_IN_ASSETS {
many_assets.push((GeneralIndex(index as u128), 1u128).into());
}
let full_xcm_pass =
Xcm::<()>(vec![
TransferAsset { assets: many_assets, beneficiary: Here.into() };
MAX_INSTRUCTIONS_TO_DECODE as usize
]);
let encoded = full_xcm_pass.encode();
assert_eq!(encoded.len(), 12402);
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_ok());
let nested_xcm_fail = Xcm::<()>(vec![
let nested_xcm = Xcm::<()>(vec![
DepositReserveAsset {
assets: All.into(),
dest: Here.into(),
......@@ -1510,10 +1496,10 @@ mod tests {
};
(MAX_INSTRUCTIONS_TO_DECODE / 2) as usize
]);
let encoded = nested_xcm_fail.encode();
let encoded = nested_xcm.encode();
assert!(Xcm::<()>::decode(&mut &encoded[..]).is_err());
let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm_fail); 64]);
let even_more_nested_xcm = Xcm::<()>(vec![SetAppendix(nested_xcm); 64]);
let encoded = even_more_nested_xcm.encode();
assert_eq!(encoded.len(), 342530);
// This should not decode since the limit is 100
......