diff --git a/Cargo.lock b/Cargo.lock index 40b11be0c24e..033219583bd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,6 +798,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "asset-hub-rococo-runtime", + "asset-test-utils", "frame-support", "frame-system", "integration-tests-common", @@ -810,8 +811,10 @@ dependencies = [ "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-parachains", + "rococo-runtime", "sp-runtime", "staging-xcm", + "staging-xcm-executor", "xcm-emulator", ] @@ -871,6 +874,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-runtime-common", "primitive-types", + "rococo-runtime", "rococo-runtime-constants", "scale-info", "smallvec", @@ -899,6 +903,7 @@ version = "1.0.0" dependencies = [ "assert_matches", "asset-hub-westend-runtime", + "asset-test-utils", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "frame-support", @@ -920,6 +925,8 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "westend-runtime", + "westend-runtime-constants", "xcm-emulator", ] @@ -993,6 +1000,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "westend-runtime", "westend-runtime-constants", ] @@ -2000,7 +2008,9 @@ dependencies = [ name = "bridge-hub-rococo-integration-tests" version = "1.0.0" dependencies = [ + "asset-test-utils", "bp-messages", + "bridge-hub-rococo-runtime", "cumulus-pallet-dmp-queue", "cumulus-pallet-xcmp-queue", "frame-support", @@ -2013,6 +2023,7 @@ dependencies = [ "polkadot-parachain-primitives", "polkadot-runtime-parachains", "staging-xcm", + "staging-xcm-executor", "xcm-emulator", ] @@ -2894,6 +2905,7 @@ dependencies = [ "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", + "rococo-runtime-constants", "scale-info", "smallvec", "sp-api", @@ -3617,6 +3629,7 @@ dependencies = [ "log", "parity-scale-codec", "polkadot-parachain-primitives", + "polkadot-runtime-parachains", "sc-client-api", "scale-info", "sp-core", @@ -3701,6 +3714,7 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "polkadot-runtime-common", + "polkadot-runtime-parachains", "rand_chacha 0.3.1", "scale-info", "sp-core", @@ -3797,8 +3811,10 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "log", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-runtime-common", + "polkadot-runtime-parachains", "sp-io", "sp-runtime", "sp-std", @@ -11095,6 +11111,7 @@ dependencies = [ "pallet-authorship", "pallet-balances", "pallet-collator-selection", + "parachain-info", "parity-scale-codec", "polkadot-core-primitives", "polkadot-primitives", @@ -12714,6 +12731,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-treasury", "pallet-vesting", + "pallet-xcm-benchmarks", "parity-scale-codec", "polkadot-primitives", "polkadot-primitives-test-helpers", @@ -12737,6 +12755,7 @@ dependencies = [ "sp-std", "staging-xcm", "staging-xcm-builder", + "staging-xcm-executor", "static_assertions", ] @@ -14084,6 +14103,7 @@ dependencies = [ "parachains-common", "parity-scale-codec", "polkadot-parachain-primitives", + "polkadot-runtime-common", "scale-info", "sp-api", "sp-block-builder", @@ -14216,6 +14236,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-weights", + "staging-xcm", ] [[package]] @@ -20205,6 +20226,7 @@ dependencies = [ "sp-core", "sp-runtime", "sp-weights", + "staging-xcm", ] [[package]] diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index 5470dce47480..64e238ecab69 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -29,6 +29,7 @@ sp-version = { path = "../../../substrate/primitives/version", default-features # Polkadot polkadot-parachain-primitives = { path = "../../../polkadot/parachain", default-features = false, features = [ "wasm-api" ]} +polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false} # Cumulus @@ -63,6 +64,7 @@ std = [ "frame-system/std", "log/std", "polkadot-parachain-primitives/std", + "polkadot-runtime-parachains/std", "scale-info/std", "sp-core/std", "sp-externalities/std", @@ -80,12 +82,14 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index eaf15768e290..369281ccd8ee 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -29,10 +29,10 @@ use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_primitives_core::{ - relay_chain, AbridgedHostConfiguration, ChannelStatus, CollationInfo, DmpMessageHandler, - GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, MessageSendError, - OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender, - XcmpMessageHandler, XcmpMessageSource, + relay_chain, AbridgedHostConfiguration, ChannelInfo, ChannelStatus, CollationInfo, + DmpMessageHandler, GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, + MessageSendError, OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, + UpwardMessageSender, XcmpMessageHandler, XcmpMessageSource, }; use cumulus_primitives_parachain_inherent::{MessageQueueChain, ParachainInherentData}; use frame_support::{ @@ -45,6 +45,7 @@ use frame_support::{ }; use frame_system::{ensure_none, ensure_root, pallet_prelude::HeaderFor}; use polkadot_parachain_primitives::primitives::RelayChainBlockNumber; +use polkadot_runtime_parachains::FeeTracker; use scale_info::TypeInfo; use sp_runtime::{ traits::{Block as BlockT, BlockNumberProvider, Hash}, @@ -52,7 +53,7 @@ use sp_runtime::{ InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity, ValidTransaction, }, - DispatchError, RuntimeDebug, + DispatchError, FixedU128, RuntimeDebug, Saturating, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; use xcm::latest::XcmHash; @@ -177,6 +178,20 @@ where check_version: bool, } +pub mod ump_constants { + use super::FixedU128; + + /// `host_config.max_upward_queue_size / THRESHOLD_FACTOR` is the threshold after which delivery + /// starts getting exponentially more expensive. + /// `2` means the price starts to increase when queue is half full. + pub const THRESHOLD_FACTOR: u32 = 2; + /// The base number the delivery fee factor gets multiplied by every time it is increased. + /// Also the number it gets divided by when decreased. + pub const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 + /// The base number message size in KB is multiplied by before increasing the fee factor. + pub const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -240,6 +255,10 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { + /// Handles actually sending upward messages by moving them from `PendingUpwardMessages` to + /// `UpwardMessages`. Decreases the delivery fee factor if after sending messages, the queue + /// total size is less than the threshold (see [`ump_constants::THRESHOLD_FACTOR`]). + /// Also does the sending for HRMP messages it takes from `OutboundXcmpMessageSource`. fn on_finalize(_: BlockNumberFor) { >::kill(); >::kill(); @@ -326,6 +345,17 @@ pub mod pallet { UpwardMessages::::put(&up[..num as usize]); *up = up.split_off(num as usize); + // If the total size of the pending messages is less than the threshold, + // we decrease the fee factor, since the queue is less congested. + // This makes delivery of new messages cheaper. + let threshold = host_config + .max_upward_queue_size + .saturating_div(ump_constants::THRESHOLD_FACTOR); + let remaining_total_size: usize = up.iter().map(UpwardMessage::len).sum(); + if remaining_total_size <= threshold as usize { + Self::decrease_fee_factor(()); + } + (num, total_size) }); @@ -721,7 +751,7 @@ pub mod pallet { StorageValue<_, Vec>, ValueQuery>; /// Storage field that keeps track of bandwidth used by the unincluded segment along with the - /// latest the latest HRMP watermark. Used for limiting the acceptance of new blocks with + /// latest HRMP watermark. Used for limiting the acceptance of new blocks with /// respect to relay chain constraints. #[pallet::storage] pub(super) type AggregatedUnincludedSegment = @@ -860,6 +890,17 @@ pub mod pallet { pub(super) type PendingUpwardMessages = StorageValue<_, Vec, ValueQuery>; + /// Initialization value for the delivery fee factor for UMP. + #[pallet::type_value] + pub fn UpwardInitialDeliveryFeeFactor() -> FixedU128 { + FixedU128::from_u32(1) + } + + /// The factor to multiply the base delivery fee by for UMP. + #[pallet::storage] + pub(super) type UpwardDeliveryFeeFactor = + StorageValue<_, FixedU128, ValueQuery, UpwardInitialDeliveryFeeFactor>; + /// The number of HRMP messages we observed in `on_initialize` and thus used that number for /// announcing the weight of `on_initialize` and `on_finalize`. #[pallet::storage] @@ -976,6 +1017,31 @@ impl Pallet { } } +impl FeeTracker for Pallet { + type Id = (); + + fn get_fee_factor(_: Self::Id) -> FixedU128 { + UpwardDeliveryFeeFactor::::get() + } + + fn increase_fee_factor(_: Self::Id, message_size_factor: FixedU128) -> FixedU128 { + >::mutate(|f| { + *f = f.saturating_mul( + ump_constants::EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor), + ); + *f + }) + } + + fn decrease_fee_factor(_: Self::Id) -> FixedU128 { + >::mutate(|f| { + *f = + UpwardInitialDeliveryFeeFactor::get().max(*f / ump_constants::EXPONENTIAL_FEE_BASE); + *f + }) + } +} + impl GetChannelInfo for Pallet { fn get_channel_status(id: ParaId) -> ChannelStatus { // Note, that we are using `relevant_messaging_state` which may be from the previous @@ -1019,10 +1085,17 @@ impl GetChannelInfo for Pallet { ChannelStatus::Ready(max_size_now as usize, max_size_ever as usize) } - fn get_channel_max(id: ParaId) -> Option { + fn get_channel_info(id: ParaId) -> Option { let channels = Self::relevant_messaging_state()?.egress_channels; let index = channels.binary_search_by_key(&id, |item| item.0).ok()?; - Some(channels[index].1.max_message_size as usize) + let info = ChannelInfo { + max_capacity: channels[index].1.max_capacity, + max_total_size: channels[index].1.max_total_size, + max_message_size: channels[index].1.max_message_size, + msg_count: channels[index].1.msg_count, + total_size: channels[index].1.total_size, + }; + Some(info) } } @@ -1427,6 +1500,23 @@ impl Pallet { }) } + /// Open HRMP channel for using it in benchmarks or tests. + /// + /// The caller assumes that the pallet will accept regular outbound message to the sibling + /// `target_parachain` after this call. No other assumptions are made. + #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] + pub fn open_custom_outbound_hrmp_channel_for_benchmarks_or_tests( + target_parachain: ParaId, + channel: cumulus_primitives_core::AbridgedHrmpChannel, + ) { + RelevantMessagingState::::put(MessagingStateSnapshot { + dmq_mqc_head: Default::default(), + relay_dispatch_queue_remaining_capacity: Default::default(), + ingress_channels: Default::default(), + egress_channels: vec![(target_parachain, channel)], + }) + } + /// Prepare/insert relevant data for `schedule_code_upgrade` for benchmarks. #[cfg(feature = "runtime-benchmarks")] pub fn initialize_for_set_code_benchmark(max_code_size: u32) { @@ -1468,7 +1558,13 @@ impl frame_system::SetCode for ParachainSetCode { } impl Pallet { + /// Puts a message in the `PendingUpwardMessages` storage item. + /// The message will be later sent in `on_finalize`. + /// Checks host configuration to see if message is too big. + /// Increases the delivery fee factor if the queue is sufficiently (see + /// [`ump_constants::THRESHOLD_FACTOR`]) congested. pub fn send_upward_message(message: UpwardMessage) -> Result<(u32, XcmHash), MessageSendError> { + let message_len = message.len(); // Check if the message fits into the relay-chain constraints. // // Note, that we are using `host_configuration` here which may be from the previous @@ -1482,9 +1578,22 @@ impl Pallet { // // However, changing this setting is expected to be rare. if let Some(cfg) = Self::host_configuration() { - if message.len() > cfg.max_upward_message_size as usize { + if message_len > cfg.max_upward_message_size as usize { return Err(MessageSendError::TooBig) } + let threshold = + cfg.max_upward_queue_size.saturating_div(ump_constants::THRESHOLD_FACTOR); + // We check the threshold against total size and not number of messages since messages + // could be big or small. + >::append(message.clone()); + let pending_messages = PendingUpwardMessages::::get(); + let total_size: usize = pending_messages.iter().map(UpwardMessage::len).sum(); + if total_size > threshold as usize { + // We increase the fee factor by a factor based on the new message's size in KB + let message_size_factor = FixedU128::from((message_len / 1024) as u128) + .saturating_mul(ump_constants::MESSAGE_SIZE_FEE_BASE); + Self::increase_fee_factor((), message_size_factor); + } } else { // This storage field should carry over from the previous block. So if it's None // then it must be that this is an edge-case where a message is attempted to be @@ -1495,8 +1604,8 @@ impl Pallet { // returned back to the sender. // // Thus fall through here. + >::append(message.clone()); }; - >::append(message.clone()); // The relay ump does not use using_encoded // We apply the same this to use the same hash diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 626196790bc9..3f5b4f649e32 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -1496,3 +1496,53 @@ fn deposits_relay_parent_storage_root() { }, ); } + +#[test] +fn ump_fee_factor_increases_and_decreases() { + BlockTests::new() + .with_relay_sproof_builder(|_, _, sproof| { + sproof.host_config.max_upward_queue_size = 100; + sproof.host_config.max_upward_message_num_per_candidate = 1; + }) + .add_with_post_test( + 1, + || { + // Fee factor increases in `send_upward_message` + ParachainSystem::send_upward_message(b"Test".to_vec()).unwrap(); + assert_eq!(UpwardDeliveryFeeFactor::::get(), FixedU128::from_u32(1)); + + ParachainSystem::send_upward_message( + b"This message will be enough to increase the fee factor".to_vec(), + ) + .unwrap(); + assert_eq!( + UpwardDeliveryFeeFactor::::get(), + FixedU128::from_rational(105, 100) + ); + }, + || { + // Factor decreases in `on_finalize`, but only if we are below the threshold + let messages = UpwardMessages::::get(); + assert_eq!(messages, vec![b"Test".to_vec()]); + assert_eq!( + UpwardDeliveryFeeFactor::::get(), + FixedU128::from_rational(105, 100) + ); + }, + ) + .add_with_post_test( + 2, + || { + // We do nothing here + }, + || { + let messages = UpwardMessages::::get(); + assert_eq!( + messages, + vec![b"This message will be enough to increase the fee factor".to_vec(),] + ); + // Now the delivery fee factor is decreased, since we are below the threshold + assert_eq!(UpwardDeliveryFeeFactor::::get(), FixedU128::from_u32(1)); + }, + ); +} diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index 77d0551b5110..034b640a0ed6 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -14,13 +14,15 @@ scale-info = { version = "2.9.0", default-features = false, features = ["derive" frame-support = { path = "../../../substrate/frame/support", default-features = false} frame-system = { path = "../../../substrate/frame/system", default-features = false} sp-io = { path = "../../../substrate/primitives/io", default-features = false} +sp-core = { path = "../../../substrate/primitives/core", default-features = false } sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false} sp-std = { path = "../../../substrate/primitives/std", default-features = false} # Polkadot -polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false} -xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false} -xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false} +polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false } +polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } +xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } # Cumulus cumulus-primitives-core = { path = "../../primitives/core", default-features = false } @@ -54,7 +56,9 @@ std = [ "frame-system/std", "log/std", "polkadot-runtime-common/std", + "polkadot-runtime-parachains/std", "scale-info/std", + "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", @@ -69,6 +73,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -79,6 +84,7 @@ try-runtime = [ "frame-system/try-runtime", "pallet-balances/try-runtime", "polkadot-runtime-common/try-runtime", + "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] bridging = [ "bp-xcm-bridge-hub-router" ] diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index b53eb7cc0e10..b62dc0a47fc5 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -22,6 +22,16 @@ //! Also provides an implementation of `SendXcm` which can be placed in a router tuple for relaying //! XCM over XCMP if the destination is `Parent/Parachain`. It requires an implementation of //! `XcmExecutor` for dispatching incoming XCM messages. +//! +//! To prevent out of memory errors on the `OutboundXcmpMessages` queue, an exponential fee factor +//! (`DeliveryFeeFactor`) is set, much like the one used in DMP. +//! The fee factor increases whenever the total size of messages in a particular channel passes a +//! threshold. This threshold is defined as a percentage of the maximum total size the channel can +//! have. More concretely, the threshold is `max_total_size` / `THRESHOLD_FACTOR`, where: +//! - `max_total_size` is the maximum size, in bytes, of the channel, not number of messages. +//! It is defined in the channel configuration. +//! - `THRESHOLD_FACTOR` just declares which percentage of the max size is the actual threshold. +//! If it's 2, then the threshold is half of the max size, if it's 4, it's a quarter, and so on. #![cfg_attr(not(feature = "std"), no_std)] @@ -49,13 +59,15 @@ use frame_support::{ traits::{EnsureOrigin, Get}, weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight}, }; -use polkadot_runtime_common::xcm_sender::PriceForParachainDelivery; +use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; +use polkadot_runtime_parachains::FeeTracker; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaChaRng, }; use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; +use sp_core::MAX_POSSIBLE_ALLOCATION; +use sp_runtime::{FixedU128, RuntimeDebug, Saturating}; use sp_std::{convert::TryFrom, prelude::*}; use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; @@ -68,6 +80,19 @@ pub type OverweightIndex = u64; const LOG_TARGET: &str = "xcmp_queue"; const DEFAULT_POV_SIZE: u64 = 64 * 1024; // 64 KB +/// Constants related to delivery fee calculation +pub mod delivery_fee_constants { + use super::FixedU128; + + /// Fees will start increasing when queue is half full + pub const THRESHOLD_FACTOR: u32 = 2; + /// The base number the delivery fee factor gets multiplied by every time it is increased. + /// Also, the number it gets divided by when decreased. + pub const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 + /// The contribution of each KB to a fee factor increase + pub const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 +} + // Maximum amount of messages to process per block. This is a temporary measure until we properly // account for proof size weights. const MAX_MESSAGES_PER_BLOCK: u8 = 10; @@ -77,7 +102,7 @@ const MAX_OVERWEIGHT_MESSAGES: u32 = 1000; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, Twox64Concat}; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -109,7 +134,7 @@ pub mod pallet { type ControllerOriginConverter: ConvertOrigin; /// The price for delivering an XCM to a sibling parachain destination. - type PriceForSiblingDelivery: PriceForParachainDelivery; + type PriceForSiblingDelivery: PriceForMessageDelivery; /// The weight information of this pallet. type WeightInfo: WeightInfo; @@ -374,6 +399,17 @@ pub mod pallet { /// Whether or not the XCMP queue is suspended from executing incoming XCMs or not. #[pallet::storage] pub(super) type QueueSuspended = StorageValue<_, bool, ValueQuery>; + + /// Initialization value for the DeliveryFee factor. + #[pallet::type_value] + pub fn InitialFactor() -> FixedU128 { + FixedU128::from_u32(1) + } + + /// The factor to multiply the base delivery fee by. + #[pallet::storage] + pub(super) type DeliveryFeeFactor = + StorageMap<_, Twox64Concat, ParaId, FixedU128, ValueQuery, InitialFactor>; } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo)] @@ -403,7 +439,7 @@ pub struct InboundChannelDetails { } /// Struct containing detailed information about the outbound channel. -#[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] pub struct OutboundChannelDetails { /// The `ParaId` of the parachain that this channel is connected with. recipient: ParaId, @@ -503,56 +539,90 @@ impl Pallet { /// length prefixed and can thus decode each fragment from the aggregate stream. With this, /// we can concatenate them into a single aggregate blob without needing to be concerned /// about encoding fragment boundaries. + /// + /// If successful, returns the number of pages in the outbound queue after enqueuing the new + /// fragment. fn send_fragment( recipient: ParaId, format: XcmpMessageFormat, fragment: Fragment, ) -> Result { - let data = fragment.encode(); + let encoded_fragment = fragment.encode(); - // Optimization note: `max_message_size` could potentially be stored in - // `OutboundXcmpMessages` once known; that way it's only accessed when a new page is needed. - - let max_message_size = - T::ChannelInfo::get_channel_max(recipient).ok_or(MessageSendError::NoChannel)?; - if data.len() > max_message_size { + let channel_info = + T::ChannelInfo::get_channel_info(recipient).ok_or(MessageSendError::NoChannel)?; + let max_message_size = channel_info.max_message_size as usize; + // Max message size refers to aggregates, or pages. Not to individual fragments. + if encoded_fragment.len() > max_message_size { return Err(MessageSendError::TooBig) } - let mut s = >::get(); - let details = if let Some(details) = s.iter_mut().find(|item| item.recipient == recipient) { + let mut all_channels = >::get(); + let channel_details = if let Some(details) = + all_channels.iter_mut().find(|channel| channel.recipient == recipient) + { details } else { - s.push(OutboundChannelDetails::new(recipient)); - s.last_mut().expect("can't be empty; a new element was just pushed; qed") + all_channels.push(OutboundChannelDetails::new(recipient)); + all_channels + .last_mut() + .expect("can't be empty; a new element was just pushed; qed") }; - let have_active = details.last_index > details.first_index; - let appended = have_active && - >::mutate(recipient, details.last_index - 1, |s| { - if XcmpMessageFormat::decode_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut &s[..]) != - Ok(format) - { - return false - } - if s.len() + data.len() > max_message_size { - return false - } - s.extend_from_slice(&data[..]); - true - }); - if appended { - Ok((details.last_index - details.first_index - 1) as u32) + let have_active = channel_details.last_index > channel_details.first_index; + // Try to append fragment to the last page, if there is enough space. + // We return the size of the last page inside of the option, to not calculate it again. + let appended_to_last_page = have_active + .then(|| { + >::mutate( + recipient, + channel_details.last_index - 1, + |page| { + if XcmpMessageFormat::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut &page[..], + ) != Ok(format) + { + return None + } + if page.len() + encoded_fragment.len() > max_message_size { + return None + } + page.extend_from_slice(&encoded_fragment[..]); + Some(page.len()) + }, + ) + }) + .flatten(); + + let (number_of_pages, last_page_size) = if let Some(size) = appended_to_last_page { + let number_of_pages = (channel_details.last_index - channel_details.first_index) as u32; + (number_of_pages, size) } else { // Need to add a new page. - let page_index = details.last_index; - details.last_index += 1; + let page_index = channel_details.last_index; + channel_details.last_index += 1; let mut new_page = format.encode(); - new_page.extend_from_slice(&data[..]); + new_page.extend_from_slice(&encoded_fragment[..]); + let last_page_size = new_page.len(); + let number_of_pages = (channel_details.last_index - channel_details.first_index) as u32; >::insert(recipient, page_index, new_page); - let r = (details.last_index - details.first_index - 1) as u32; - >::put(s); - Ok(r) + >::put(all_channels); + (number_of_pages, last_page_size) + }; + + // We have to count the total size here since `channel_info.total_size` is not updated at + // this point in time. We assume all previous pages are filled, which, in practice, is not + // always the case. + let total_size = + number_of_pages.saturating_sub(1) * max_message_size as u32 + last_page_size as u32; + let threshold = channel_info.max_total_size / delivery_fee_constants::THRESHOLD_FACTOR; + if total_size > threshold { + let message_size_factor = FixedU128::from((encoded_fragment.len() / 1024) as u128) + .saturating_mul(delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); + Self::increase_fee_factor(recipient, message_size_factor); } + + Ok(number_of_pages) } /// Sends a signal to the `dest` chain over XCMP. This is guaranteed to be dispatched on this @@ -1004,9 +1074,8 @@ impl XcmpMessageHandler for Pallet { // Record the fact we received it. match status.binary_search_by_key(&sender, |item| item.sender) { Ok(i) => { - let count = status[i].message_metadata.len(); - if count as u32 >= suspend_threshold && status[i].state == InboundState::Ok - { + let count = status[i].message_metadata.len() as u32; + if count >= suspend_threshold && status[i].state == InboundState::Ok { status[i].state = InboundState::Suspended; let r = Self::send_signal(sender, ChannelSignal::Suspend); if r.is_err() { @@ -1015,7 +1084,7 @@ impl XcmpMessageHandler for Pallet { ); } } - if (count as u32) < drop_threshold { + if count < drop_threshold { status[i].message_metadata.push((sent_at, format)); } else { debug_assert!( @@ -1023,6 +1092,13 @@ impl XcmpMessageHandler for Pallet { "XCMP channel queue full. Silently dropping message" ); } + // Update the delivery fee factor, if applicable. + if count > suspend_threshold { + let message_size_factor = + FixedU128::from((data_ref.len() / 1024) as u128) + .saturating_mul(delivery_fee_constants::MESSAGE_SIZE_FEE_BASE); + Self::increase_fee_factor(sender, message_size_factor); + } }, Err(_) => status.push(InboundChannelDetails { sender, @@ -1120,6 +1196,21 @@ impl XcmpMessageSource for Pallet { result.push((para_id, page)); } + let max_total_size = match T::ChannelInfo::get_channel_info(para_id) { + Some(channel_info) => channel_info.max_total_size, + None => { + log::warn!("calling `get_channel_info` with no RelevantMessagingState?!"); + MAX_POSSIBLE_ALLOCATION // We use this as a fallback in case the messaging state is not present + }, + }; + let threshold = max_total_size.saturating_div(delivery_fee_constants::THRESHOLD_FACTOR); + let remaining_total_size: usize = (first_index..last_index) + .map(|index| OutboundXcmpMessages::::decode_len(para_id, index).unwrap()) + .sum(); + if remaining_total_size <= threshold as usize { + Self::decrease_fee_factor(para_id); + } + *status = OutboundChannelDetails { recipient: para_id, state: outbound_state, @@ -1172,7 +1263,7 @@ impl SendXcm for Pallet { MultiLocation { parents: 1, interior: X1(Parachain(id)) } => { let xcm = msg.take().ok_or(SendError::MissingArgument)?; let id = ParaId::from(*id); - let price = T::PriceForSiblingDelivery::price_for_parachain_delivery(id, &xcm); + let price = T::PriceForSiblingDelivery::price_for_delivery(id, &xcm); let versioned_xcm = T::VersionWrapper::wrap_version(&d, xcm) .map_err(|()| SendError::DestinationUnsupported)?; Ok(((id, versioned_xcm), price)) @@ -1198,3 +1289,27 @@ impl SendXcm for Pallet { } } } + +impl FeeTracker for Pallet { + type Id = ParaId; + + fn get_fee_factor(id: Self::Id) -> FixedU128 { + >::get(id) + } + + fn increase_fee_factor(id: Self::Id, message_size_factor: FixedU128) -> FixedU128 { + >::mutate(id, |f| { + *f = f.saturating_mul( + delivery_fee_constants::EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor), + ); + *f + }) + } + + fn decrease_fee_factor(id: Self::Id) -> FixedU128 { + >::mutate(id, |f| { + *f = InitialFactor::get().max(*f / delivery_fee_constants::EXPONENTIAL_FEE_BASE); + *f + }) + } +} diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index a3f10fa5428c..bc0710e34653 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -85,8 +85,10 @@ parameter_types! { pub const MaxReserves: u32 = 50; } +pub type Balance = u64; + impl pallet_balances::Config for Test { - type Balance = u64; + type Balance = Balance; type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; @@ -196,6 +198,22 @@ impl ConvertOrigin } } +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(RelayChain::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: Balance = 300_000_000; + /// The fee per byte + pub const ByteFee: Balance = 1_000_000; +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + ByteFee, + XcmpQueue, +>; + impl Config for Test { type RuntimeEvent = RuntimeEvent; type XcmExecutor = xcm_executor::XcmExecutor; @@ -205,7 +223,7 @@ impl Config for Test { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = SystemParachainAsSuperuser; type WeightInfo = (); - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs index ba005a5badfe..cf6d947609d2 100644 --- a/cumulus/pallets/xcmp-queue/src/tests.rs +++ b/cumulus/pallets/xcmp-queue/src/tests.rs @@ -324,13 +324,14 @@ fn xcmp_queue_consumes_dest_and_msg_on_ok_validate() { let dest = (Parent, X1(Parachain(5555))); let mut dest_wrapper = Some(dest.into()); let mut msg_wrapper = Some(message.clone()); - assert!(::validate(&mut dest_wrapper, &mut msg_wrapper).is_ok()); - - // check wrapper were consumed - assert_eq!(None, dest_wrapper.take()); - assert_eq!(None, msg_wrapper.take()); new_test_ext().execute_with(|| { + assert!(::validate(&mut dest_wrapper, &mut msg_wrapper).is_ok()); + + // check wrapper were consumed + assert_eq!(None, dest_wrapper.take()); + assert_eq!(None, msg_wrapper.take()); + // another try with router chain with asserting sender assert_eq!( Err(SendError::Transport("NoChannel")), @@ -370,3 +371,74 @@ fn xcmp_queue_send_xcm_works() { .any(|(para_id, _)| para_id == &sibling_para_id)); }) } + +#[test] +fn verify_fee_factor_increase_and_decrease() { + use cumulus_primitives_core::AbridgedHrmpChannel; + use sp_runtime::FixedU128; + + let sibling_para_id = ParaId::from(12345); + let destination = (Parent, Parachain(sibling_para_id.into())).into(); + let xcm = Xcm(vec![ClearOrigin; 100]); + let versioned_xcm = VersionedXcm::from(xcm.clone()); + let mut xcmp_message = XcmpMessageFormat::ConcatenatedVersionedXcm.encode(); + xcmp_message.extend(versioned_xcm.encode()); + + new_test_ext().execute_with(|| { + let initial = InitialFactor::get(); + assert_eq!(DeliveryFeeFactor::::get(sibling_para_id), initial); + + // Open channel so messages can actually be sent + ParachainSystem::open_custom_outbound_hrmp_channel_for_benchmarks_or_tests( + sibling_para_id, + AbridgedHrmpChannel { + max_capacity: 10, + max_total_size: 1000, + max_message_size: 104, + msg_count: 0, + total_size: 0, + mqc_head: None, + }, + ); + + // Fee factor is only increased in `send_fragment`, which is called by `send_xcm`. + // When queue is not congested, fee factor doesn't change. + assert_ok!(send_xcm::(destination, xcm.clone())); // Size 104 + assert_ok!(send_xcm::(destination, xcm.clone())); // Size 208 + assert_ok!(send_xcm::(destination, xcm.clone())); // Size 312 + assert_ok!(send_xcm::(destination, xcm.clone())); // Size 416 + assert_eq!(DeliveryFeeFactor::::get(sibling_para_id), initial); + + // Sending the message right now is cheap + let (_, delivery_fees) = validate_send::(destination, xcm.clone()) + .expect("message can be sent; qed"); + let Fungible(delivery_fee_amount) = delivery_fees.inner()[0].fun else { unreachable!("asset is fungible; qed"); }; + assert_eq!(delivery_fee_amount, 402_000_000); + + let smaller_xcm = Xcm(vec![ClearOrigin; 30]); + + // When we get to half of `max_total_size`, because `THRESHOLD_FACTOR` is 2, + // then the fee factor starts to increase. + assert_ok!(send_xcm::(destination, xcm.clone())); // Size 520 + assert_eq!(DeliveryFeeFactor::::get(sibling_para_id), FixedU128::from_float(1.05)); + + for _ in 0..12 { // We finish at size 929 + assert_ok!(send_xcm::(destination, smaller_xcm.clone())); + } + assert!(DeliveryFeeFactor::::get(sibling_para_id) > FixedU128::from_float(1.88)); + + // Sending the message right now is expensive + let (_, delivery_fees) = validate_send::(destination, xcm.clone()) + .expect("message can be sent; qed"); + let Fungible(delivery_fee_amount) = delivery_fees.inner()[0].fun else { unreachable!("asset is fungible; qed"); }; + assert_eq!(delivery_fee_amount, 758_030_955); + + // Fee factor only decreases in `take_outbound_messages` + for _ in 0..5 { // We take 5 100 byte pages + XcmpQueue::take_outbound_messages(1); + } + assert!(DeliveryFeeFactor::::get(sibling_para_id) < FixedU128::from_float(1.72)); + XcmpQueue::take_outbound_messages(1); + assert!(DeliveryFeeFactor::::get(sibling_para_id) < FixedU128::from_float(1.63)); + }); +} diff --git a/cumulus/parachain-template/runtime/src/lib.rs b/cumulus/parachain-template/runtime/src/lib.rs index b9bf97d7786f..b6bf8419ec46 100644 --- a/cumulus/parachain-template/runtime/src/lib.rs +++ b/cumulus/parachain-template/runtime/src/lib.rs @@ -10,6 +10,8 @@ mod weights; pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use smallvec::smallvec; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; @@ -412,7 +414,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/cumulus/parachains/common/Cargo.toml b/cumulus/parachains/common/Cargo.toml index 29528b169ae2..2bdae91ef4d1 100644 --- a/cumulus/parachains/common/Cargo.toml +++ b/cumulus/parachains/common/Cargo.toml @@ -40,6 +40,7 @@ xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/x pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false } cumulus-primitives-core = { path = "../../primitives/core", default-features = false } cumulus-primitives-utility = { path = "../../primitives/utility", default-features = false } +parachain-info = { path = "../pallets/parachain-info", default-features = false } [dev-dependencies] pallet-authorship = { path = "../../../substrate/frame/authorship", default-features = false} @@ -61,6 +62,7 @@ std = [ "pallet-authorship/std", "pallet-balances/std", "pallet-collator-selection/std", + "parachain-info/std", "polkadot-core-primitives/std", "polkadot-primitives/std", "rococo-runtime-constants/std", diff --git a/cumulus/parachains/common/src/lib.rs b/cumulus/parachains/common/src/lib.rs index 89e74b2f9b74..4ebc2cc6e1e2 100644 --- a/cumulus/parachains/common/src/lib.rs +++ b/cumulus/parachains/common/src/lib.rs @@ -73,7 +73,10 @@ mod types { /// Common constants of parachains. mod constants { use super::types::BlockNumber; - use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}; + use frame_support::{ + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, + PalletId, + }; use sp_runtime::Perbill; /// This determines the average expected block time that we are targeting. Blocks will be /// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by @@ -101,6 +104,9 @@ mod constants { WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), polkadot_primitives::MAX_POV_SIZE as u64, ); + + /// Treasury pallet id of the local chain, used to convert into AccountId + pub const TREASURY_PALLET_ID: PalletId = PalletId(*b"py/trsry"); } /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know diff --git a/cumulus/parachains/common/src/xcm_config.rs b/cumulus/parachains/common/src/xcm_config.rs index 91ad7d902bda..4b0215d672b2 100644 --- a/cumulus/parachains/common/src/xcm_config.rs +++ b/cumulus/parachains/common/src/xcm_config.rs @@ -16,10 +16,9 @@ use crate::impls::AccountIdOf; use cumulus_primitives_core::{IsSystem, ParaId}; use frame_support::{ - traits::{fungibles::Inspect, tokens::ConversionToAssetBalance, ContainsPair}, + traits::{fungibles::Inspect, tokens::ConversionToAssetBalance, Contains, ContainsPair}, weights::Weight, }; -use log; use sp_runtime::traits::Get; use sp_std::marker::PhantomData; use xcm::latest::prelude::*; @@ -80,6 +79,27 @@ impl> ContainsPair } } +pub struct RelayOrOtherSystemParachains< + SystemParachainMatcher: Contains, + Runtime: parachain_info::Config, +> { + _runtime: PhantomData<(SystemParachainMatcher, Runtime)>, +} +impl, Runtime: parachain_info::Config> + Contains for RelayOrOtherSystemParachains +{ + fn contains(l: &MultiLocation) -> bool { + let self_para_id: u32 = parachain_info::Pallet::::get().into(); + if let MultiLocation { parents: 0, interior: X1(Parachain(para_id)) } = l { + if *para_id == self_para_id { + return false + } + } + matches!(l, MultiLocation { parents: 1, interior: Here }) || + SystemParachainMatcher::contains(l) + } +} + /// Accepts an asset if it is a concrete asset from the system (Relay Chain or system parachain). pub struct ConcreteAssetFromSystem(PhantomData); impl> ContainsPair diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/Cargo.toml index 52682c6eefd6..db58d8d33039 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/Cargo.toml @@ -25,8 +25,11 @@ polkadot-parachain-primitives = { path = "../../../../../../polkadot/parachain", polkadot-runtime-parachains = { path = "../../../../../../polkadot/runtime/parachains" } xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", default-features = false} pallet-xcm = { path = "../../../../../../polkadot/xcm/pallet-xcm", default-features = false} +xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../polkadot/xcm/xcm-executor", default-features = false} +rococo-runtime = { path = "../../../../../../polkadot/runtime/rococo", default-features = false } # Cumulus +asset-test-utils = { path = "../../../../runtimes/assets/test-utils", default-features = false } parachains-common = { path = "../../../../common" } asset-hub-rococo-runtime = { path = "../../../../runtimes/assets/asset-hub-rococo" } @@ -47,5 +50,7 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/lib.rs index e0e9dcbdce74..42f54bdf49df 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/lib.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub use asset_test_utils::xcm_helpers; pub use codec::Encode; pub use frame_support::{ assert_err, assert_ok, diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/reserve_transfer.rs index 0c136e2789f5..fb25607c635e 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -14,6 +14,8 @@ // limitations under the License. use crate::*; +use asset_hub_rococo_runtime::xcm_config::XcmConfig as AssetHubRococoXcmConfig; +use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -182,10 +184,16 @@ fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() { test.set_dispatchable::(relay_limited_reserve_transfer_assets); test.assert(); + let delivery_fees = Rococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); assert_eq!(receiver_balance_before, receiver_balance_after); } @@ -244,7 +252,13 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + let delivery_fees = Rococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); assert_eq!(receiver_balance_before, receiver_balance_after); } @@ -306,7 +320,13 @@ fn limited_reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_after = test.sender.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve // transfers } @@ -338,7 +358,13 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_after = test.sender.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve // transfers } diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/teleport.rs index 21afed179184..4b2ea0e160cb 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-rococo/src/tests/teleport.rs @@ -14,6 +14,8 @@ // limitations under the License. use crate::*; +use asset_hub_rococo_runtime::xcm_config::XcmConfig as AssetHubRococoXcmConfig; +use rococo_runtime::xcm_config::XcmConfig as RococoXcmConfig; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -171,11 +173,17 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.set_dispatchable::(relay_limited_teleport_assets); test.assert(); + let delivery_fees = Rococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -212,8 +220,14 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -247,8 +261,14 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance does not change assert_eq!(receiver_balance_after, receiver_balance_before); } @@ -274,11 +294,17 @@ fn teleport_native_assets_from_relay_to_system_para_works() { test.set_dispatchable::(relay_teleport_assets); test.assert(); + let delivery_fees = Rococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -315,8 +341,14 @@ fn teleport_native_assets_back_from_system_para_to_relay_works() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -347,11 +379,17 @@ fn teleport_native_assets_from_system_para_to_relay_fails() { test.set_dispatchable::(system_para_teleport_assets); test.assert(); + let delivery_fees = AssetHubRococo::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance does not change assert_eq!(receiver_balance_after, receiver_balance_before); } @@ -359,11 +397,12 @@ fn teleport_native_assets_from_system_para_to_relay_fails() { #[test] fn teleport_to_other_system_parachains_works() { let amount = ASSET_HUB_ROCOCO_ED * 100; - let native_asset: VersionedMultiAssets = (Parent, amount).into(); + let native_asset: MultiAssets = (Parent, amount).into(); test_parachain_is_trusted_teleporter!( - AssetHubRococo, // Origin - vec![BridgeHubRococo], // Destinations + AssetHubRococo, // Origin + AssetHubRococoXcmConfig, // XCM Configuration + vec![BridgeHubRococo], // Destinations (native_asset, amount) ); } diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml index bf141dafebf2..4b6b8874b6a4 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/Cargo.toml @@ -30,10 +30,13 @@ xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", defaul xcm-builder = { package = "staging-xcm-builder", path = "../../../../../../polkadot/xcm/xcm-builder", default-features = false} xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../polkadot/xcm/xcm-executor", default-features = false} pallet-xcm = { path = "../../../../../../polkadot/xcm/pallet-xcm", default-features = false} +westend-runtime = { path = "../../../../../../polkadot/runtime/westend", default-features = false } +westend-runtime-constants = { path = "../../../../../../polkadot/runtime/westend/constants", default-features = false } # Cumulus parachains-common = { path = "../../../../common" } asset-hub-westend-runtime = { path = "../../../../runtimes/assets/asset-hub-westend" } +asset-test-utils = { path = "../../../../runtimes/assets/test-utils", default-features = false } cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../pallets/dmp-queue" } cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../pallets/parachain-system" } @@ -59,6 +62,7 @@ runtime-benchmarks = [ "polkadot-runtime-common/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "westend-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs index c25cc601fbef..0133cdbed5cc 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/lib.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub use asset_test_utils::xcm_helpers; pub use codec::Encode; pub use frame_support::{ assert_err, assert_ok, diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 8f8b7a7dde77..3b48e6529ff7 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -14,6 +14,8 @@ // limitations under the License. use crate::*; +use asset_hub_westend_runtime::xcm_config::XcmConfig; +use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -179,10 +181,16 @@ fn limited_reserve_transfer_native_asset_from_relay_to_system_para_fails() { test.set_dispatchable::(relay_limited_reserve_transfer_assets); test.assert(); + let delivery_fees = Westend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); assert_eq!(receiver_balance_before, receiver_balance_after); } @@ -238,10 +246,16 @@ fn reserve_transfer_native_asset_from_relay_to_system_para_fails() { test.set_dispatchable::(relay_reserve_transfer_assets); test.assert(); + let delivery_fees = Westend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); assert_eq!(receiver_balance_before, receiver_balance_after); } @@ -303,7 +317,17 @@ fn limited_reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_after = test.sender.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::<::XcmSender>( + test.args.assets.clone(), + 0, + test.args.weight_limit, + test.args.beneficiary, + test.args.dest, + ) + }); + + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve // transfers } @@ -335,7 +359,17 @@ fn reserve_transfer_native_asset_from_system_para_to_para() { let sender_balance_after = test.sender.balance; - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::<::XcmSender>( + test.args.assets.clone(), + 0, + test.args.weight_limit, + test.args.beneficiary, + test.args.dest, + ) + }); + + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // TODO: Check receiver balance when Penpal runtime is improved to propery handle reserve // transfers } diff --git a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs index 81471c401f39..4fe0062dafcd 100644 --- a/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/assets/asset-hub-westend/src/tests/teleport.rs @@ -16,6 +16,8 @@ #![allow(dead_code)] // use crate::*; +use asset_hub_westend_runtime::xcm_config::XcmConfig as AssetHubWestendXcmConfig; +use westend_runtime::xcm_config::XcmConfig as WestendXcmConfig; fn relay_origin_assertions(t: RelayToSystemParaTest) { type RuntimeEvent = ::RuntimeEvent; @@ -173,11 +175,17 @@ fn limited_teleport_native_assets_from_relay_to_system_para_works() { test.set_dispatchable::(relay_limited_teleport_assets); test.assert(); + let delivery_fees = Westend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -214,8 +222,14 @@ fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -249,8 +263,14 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance does not change assert_eq!(receiver_balance_after, receiver_balance_before); } @@ -276,11 +296,17 @@ fn teleport_native_assets_from_relay_to_system_para_works() { test.set_dispatchable::(relay_teleport_assets); test.assert(); + let delivery_fees = Westend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -314,11 +340,17 @@ fn teleport_native_assets_back_from_system_para_to_relay_works() { test.set_dispatchable::(system_para_teleport_assets); test.assert(); + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance is increased assert!(receiver_balance_after > receiver_balance_before); } @@ -349,11 +381,17 @@ fn teleport_native_assets_from_system_para_to_relay_fails() { test.set_dispatchable::(system_para_teleport_assets); test.assert(); + let delivery_fees = AssetHubWestend::execute_with(|| { + xcm_helpers::transfer_assets_delivery_fees::< + ::XcmSender, + >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + }); + let sender_balance_after = test.sender.balance; let receiver_balance_after = test.receiver.balance; // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send, sender_balance_after); + assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); // Receiver's balance does not change assert_eq!(receiver_balance_after, receiver_balance_before); } diff --git a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml index 7ecf87158241..b3ce2a99f70a 100644 --- a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/Cargo.toml @@ -19,13 +19,16 @@ polkadot-parachain-primitives = { path = "../../../../../../polkadot/parachain", polkadot-runtime-parachains = { path = "../../../../../../polkadot/runtime/parachains" } xcm = { package = "staging-xcm", path = "../../../../../../polkadot/xcm", default-features = false} pallet-xcm = { path = "../../../../../../polkadot/xcm/pallet-xcm", default-features = false} +xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../polkadot/xcm/xcm-executor", default-features = false} # Cumulus +asset-test-utils = { path = "../../../../../parachains/runtimes/assets/test-utils", default-features = false } parachains-common = { path = "../../../../common" } cumulus-pallet-xcmp-queue = { path = "../../../../../pallets/xcmp-queue", default-features = false} cumulus-pallet-dmp-queue = { path = "../../../../../pallets/dmp-queue", default-features = false} pallet-bridge-messages = { path = "../../../../../../bridges/modules/messages", default-features = false} bp-messages = { path = "../../../../../../bridges/primitives/messages", default-features = false} +bridge-hub-rococo-runtime = { path = "../../../../../parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } # Local xcm-emulator = { path = "../../../../../xcm/xcm-emulator", default-features = false} diff --git a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/teleport.rs b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/teleport.rs index 4c7e37fab623..f00288a4d8c7 100644 --- a/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/teleport.rs +++ b/cumulus/parachains/integration-tests/emulated/bridges/bridge-hub-rococo/src/tests/teleport.rs @@ -14,14 +14,16 @@ // limitations under the License. use crate::*; +use bridge_hub_rococo_runtime::xcm_config::XcmConfig; #[test] fn teleport_to_other_system_parachains_works() { let amount = BRIDGE_HUB_ROCOCO_ED * 100; - let native_asset: VersionedMultiAssets = (Parent, amount).into(); + let native_asset: MultiAssets = (Parent, amount).into(); test_parachain_is_trusted_teleporter!( BridgeHubRococo, // Origin + XcmConfig, // XCM configuration vec![AssetHubRococo], // Destinations (native_asset, amount) ); diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index e87c361ebeac..5ce940264029 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -15,7 +15,7 @@ #[macro_export] macro_rules! test_parachain_is_trusted_teleporter { - ( $sender_para:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => { + ( $sender_para:ty, $sender_xcm_config:ty, vec![$( $receiver_para:ty ),+], ($assets:expr, $amount:expr) ) => { $crate::paste::paste! { // init Origin variables let sender = [<$sender_para Sender>]::get(); @@ -32,19 +32,22 @@ macro_rules! test_parachain_is_trusted_teleporter { let para_receiver_balance_before = <$receiver_para as $crate::Chain>::account_data_of(receiver.clone()).free; let para_destination = - <$sender_para>::sibling_location_of(<$receiver_para>::para_id()).into(); - let beneficiary = + <$sender_para>::sibling_location_of(<$receiver_para>::para_id()); + let beneficiary: MultiLocation = $crate::AccountId32 { network: None, id: receiver.clone().into() }.into(); + dbg!(&origin); + dbg!(¶_destination); + // Send XCM message from Origin Parachain // We are only testing the limited teleport version, which should be ok since success will // depend only on a proper `XcmConfig` at destination. <$sender_para>::execute_with(|| { assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets( origin.clone(), - bx!(para_destination), - bx!(beneficiary), - bx!($assets.clone()), + bx!(para_destination.into()), + bx!(beneficiary.into()), + bx!($assets.clone().into()), fee_asset_item, weight_limit.clone(), )); @@ -89,8 +92,13 @@ macro_rules! test_parachain_is_trusted_teleporter { <$sender_para as $crate::Chain>::account_data_of(sender.clone()).free; let para_receiver_balance_after = <$receiver_para as $crate::Chain>::account_data_of(receiver.clone()).free; + let delivery_fees = <$sender_para>::execute_with(|| { + asset_test_utils::xcm_helpers::transfer_assets_delivery_fees::< + <$sender_xcm_config as xcm_executor::Config>::XcmSender, + >($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination) + }); - assert_eq!(para_sender_balance_before - $amount, para_sender_balance_after); + assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after); assert!(para_receiver_balance_after > para_receiver_balance_before); // Update sender balance diff --git a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs index 7c218bfbc092..dc6d19d06f45 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/xcm_helpers.rs @@ -14,14 +14,7 @@ // limitations under the License. use parachains_common::AccountId; -use xcm::{ - prelude::{ - AccountId32, All, BuyExecution, DepositAsset, MultiAsset, MultiAssets, MultiLocation, - OriginKind, RefundSurplus, Transact, UnpaidExecution, VersionedXcm, Weight, WeightLimit, - WithdrawAsset, Xcm, X1, - }, - DoubleEncoded, -}; +use xcm::{prelude::*, DoubleEncoded}; /// Helper method to build a XCM with a `Transact` instruction and paying for its execution pub fn xcm_transact_paid_execution( diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs index 40ce122112d2..5c51a3a52324 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs @@ -34,6 +34,8 @@ use assets_common::{ AssetIdForTrustBackedAssetsConvert, MultiLocationForAssetId, }; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -88,7 +90,7 @@ pub use sp_runtime::BuildStorage; // Polkadot imports use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use xcm::latest::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; use crate::xcm_config::{ @@ -645,7 +647,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { >; type ControllerOriginConverter = xcm_config::XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -1211,9 +1213,21 @@ impl_runtime_apis! { use xcm_config::{KsmLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + KsmLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(KsmLocation::get()) } @@ -1269,6 +1283,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs index 57e745d2e5ed..3628641544b5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs @@ -16,9 +16,9 @@ use super::{ AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; -use crate::ForeignAssets; +use crate::{ForeignAssets, CENTS}; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, matching::{FromSiblingParachain, IsForeignConcreteAsset}, @@ -34,6 +34,7 @@ use parachains_common::{ xcm_config::{AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem}, }; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ @@ -532,11 +533,21 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(KsmLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs index d4f7d6ef3616..b58d094deec6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/lib.rs @@ -66,6 +66,8 @@ use assets_common::{ foreign_creators::ForeignCreators, matching::FromSiblingParachain, MultiLocationForAssetId, }; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -118,7 +120,7 @@ pub use sp_runtime::BuildStorage; // Polkadot imports use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use xcm::latest::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; use crate::xcm_config::ForeignCreatorsSovereignAccountOf; @@ -581,7 +583,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { EnsureXcm>, >; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -1090,9 +1092,21 @@ impl_runtime_apis! { use xcm_config::{DotLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + xcm_config::DotLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(DotLocation::get()) } @@ -1148,6 +1162,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs index 8a4b24407b53..91663a75970c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs @@ -16,7 +16,7 @@ use super::{ AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, CENTS, }; use assets_common::matching::{FromSiblingParachain, IsForeignConcreteAsset}; use frame_support::{ @@ -30,6 +30,7 @@ use parachains_common::{ xcm_config::{AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem}, }; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::ConvertInto; use xcm::latest::prelude::*; use xcm_builder::{ @@ -456,11 +457,21 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(DotLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index af0ce6d5814e..ba846a850c84 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -55,6 +55,7 @@ sp-weights = { path = "../../../../../substrate/primitives/weights", default-fea primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "scale-info", "num-traits"] } # Polkadot +rococo-runtime = { path = "../../../../../polkadot/runtime/rococo", default-features = false } rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false} pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false} pallet-xcm-benchmarks = { path = "../../../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false, optional = true } @@ -130,6 +131,7 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "rococo-runtime/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -165,6 +167,7 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", + "rococo-runtime/try-runtime", "sp-runtime/try-runtime", ] std = [ @@ -218,6 +221,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "rococo-runtime-constants/std", + "rococo-runtime/std", "scale-info/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 3328ff0edaf4..a0eef7e43a4b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -93,7 +93,7 @@ pub use sp_runtime::BuildStorage; // Polkadot imports use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use xcm::latest::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; use crate::xcm_config::{ @@ -636,6 +636,20 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(xcm_config::TokenLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; @@ -645,7 +659,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = xcm_config::XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -1315,9 +1329,21 @@ impl_runtime_apis! { use xcm_config::{TokenLocation, MaxAssetsIntoHolding}; use pallet_xcm_benchmarks::asset_instance_from; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(TokenLocation::get()) } @@ -1373,6 +1399,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 6db4a7874057..d25f336f1afe 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -14,10 +14,11 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ForeignAssets, - ForeignAssetsInstance, ParachainInfo, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, - RuntimeCall, RuntimeEvent, RuntimeFlavor, RuntimeOrigin, ToRococoXcmRouter, ToWococoXcmRouter, - TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, BaseDeliveryFee, + FeeAssetId, ForeignAssets, ForeignAssetsInstance, ParachainInfo, ParachainSystem, PolkadotXcm, + PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeFlavor, RuntimeOrigin, + ToRococoXcmRouter, ToWococoXcmRouter, TransactionByteFee, TrustBackedAssetsInstance, + WeightToFee, XcmpQueue, }; use assets_common::{ local_and_foreign_assets::MatchesLocalAndForeignAssetsMultiLocation, @@ -31,10 +32,17 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, - xcm_config::{AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem}, + xcm_config::{ + AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem, + RelayOrOtherSystemParachains, + }, + TREASURY_PALLET_ID, }; use polkadot_parachain_primitives::primitives::Sibling; -use sp_runtime::traits::ConvertInto; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use rococo_runtime::Treasury as RococoTreasury; +use rococo_runtime_constants::system_parachain::SystemParachains; +use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllAssets, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -46,6 +54,7 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeesToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -67,6 +76,7 @@ parameter_types! { PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); pub const GovernanceLocation: MultiLocation = MultiLocation::parent(); + pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } /// Adapter for resolving `NetworkId` based on `pub storage Flavor: RuntimeFlavor`. @@ -521,6 +531,23 @@ pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger = ForeignAssetsInstance, >; +parameter_types! { + pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(::index() as u8)).into(); +} + +pub struct RelayTreasury; +impl Contains for RelayTreasury { + fn contains(location: &MultiLocation) -> bool { + let relay_treasury_location = RelayTreasuryLocation::get(); + *location == relay_treasury_location + } +} + +/// Locations that will not be charged fees in the executor, +/// either execution or delivery. +/// We only waive fees for system functions, which these locations represent. +pub type WaivedLocations = (RelayOrOtherSystemParachains, RelayTreasury); + /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: /// /// - ROC with the parent Relay Chain and sibling system parachains; and @@ -588,8 +615,7 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - // TODO:check-parameter: change and assert in tests when (https://github.com/paritytech/polkadot-sdk/pull/1234) merged - type FeeManager = (); + type FeeManager = XcmFeesToAccount; type MessageExporter = (); type UniversalAliases = (bridging::to_wococo::UniversalAliases, bridging::to_rococo::UniversalAliases); @@ -602,10 +628,13 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +pub type PriceForParentDelivery = + ExponentialPrice; + /// For routing XCM messages which do not cross local consensus boundary. type LocalXcmRouter = ( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index f7f6fdf68e46..cb5fba1684cc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -60,6 +60,7 @@ polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", d polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false} polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false} westend-runtime-constants = { path = "../../../../../polkadot/runtime/westend/constants", default-features = false} +westend-runtime = { path = "../../../../../polkadot/runtime/westend", default-features = false } xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false} xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false} xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false} @@ -116,6 +117,7 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "westend-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] @@ -149,6 +151,7 @@ try-runtime = [ "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", + "westend-runtime/try-runtime", ] std = [ "assets-common/std", @@ -210,6 +213,7 @@ std = [ "sp-version/std", "substrate-wasm-builder", "westend-runtime-constants/std", + "westend-runtime/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 943332087627..a7dc3a84777e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -88,6 +88,7 @@ use assets_common::{ foreign_creators::ForeignCreators, matching::FromSiblingParachain, MultiLocationForAssetId, }; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; use crate::xcm_config::ForeignCreatorsSovereignAccountOf; @@ -604,6 +605,20 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(xcm_config::WestendLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; @@ -613,7 +628,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -1262,9 +1277,21 @@ impl_runtime_apis! { use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; use pallet_xcm_benchmarks::asset_instance_from; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + WestendLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(WestendLocation::get()) } @@ -1320,6 +1347,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { @@ -1421,7 +1449,6 @@ pub mod migrations { }; use parachains_common::impls::AccountIdOf; use sp_runtime::{traits::StaticLookup, Saturating}; - use xcm::latest::prelude::*; /// Temporary migration because of bug with native asset, it can be removed once applied on /// `AssetHubWestend`. Migrates pools with `MultiLocation { parents: 0, interior: Here }` to diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 99f8d5ae5892..1306b00e2f02 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -14,9 +14,10 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, ParachainInfo, - ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TrustBackedAssetsInstance, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Assets, Authorship, Balance, Balances, BaseDeliveryFee, + FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, PoolAssets, Runtime, RuntimeCall, + RuntimeEvent, RuntimeOrigin, TransactionByteFee, TrustBackedAssetsInstance, WeightToFee, + XcmpQueue, }; use crate::ForeignAssets; use assets_common::{ @@ -31,10 +32,17 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{ impls::ToStakingPot, - xcm_config::{AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem}, + xcm_config::{ + AssetFeeAsExistentialDepositMultiplier, ConcreteAssetFromSystem, + RelayOrOtherSystemParachains, + }, + TREASURY_PALLET_ID, }; use polkadot_parachain_primitives::primitives::Sibling; -use sp_runtime::traits::ConvertInto; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use westend_runtime::Treasury as WestendTreasury; +use westend_runtime_constants::system_parachain; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -45,6 +53,7 @@ use xcm_builder::{ SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeesToAccount, }; use xcm_executor::{traits::WithOriginFilter, XcmExecutor}; @@ -65,6 +74,7 @@ parameter_types! { pub PoolAssetsPalletLocation: MultiLocation = PalletInstance(::index() as u8).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -482,6 +492,36 @@ pub type AssetFeeAsExistentialDepositMultiplierFeeCharger = AssetFeeAsExistentia TrustBackedAssetsInstance, >; +match_types! { + pub type SystemParachains: impl Contains = { + MultiLocation { + parents: 1, + interior: X1(Parachain( + system_parachain::ASSET_HUB_ID | + system_parachain::COLLECTIVES_ID | + system_parachain::BRIDGE_HUB_ID + )), + } + }; +} + +parameter_types! { + pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(::index() as u8)).into(); +} + +pub struct RelayTreasury; +impl Contains for RelayTreasury { + fn contains(location: &MultiLocation) -> bool { + let relay_treasury_location = RelayTreasuryLocation::get(); + *location == relay_treasury_location + } +} + +/// Locations that will not be charged fees in the executor, +/// either execution or delivery. +/// We only waive fees for system functions, which these locations represent. +pub type WaivedLocations = (RelayOrOtherSystemParachains, RelayTreasury); + /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: /// /// - WND with the parent Relay Chain and sibling system parachains; and @@ -531,7 +571,7 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = (); + type FeeManager = XcmFeesToAccount; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; @@ -542,11 +582,14 @@ impl xcm_executor::Config for XcmConfig { /// Local origins on this chain are allowed to dispatch XCM sends/executions. pub type LocalOriginToLocation = SignedToAccountId32; +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index 30d3cf90b47f..d8b5ca5c8e59 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -34,11 +34,11 @@ parachain-info = { path = "../../../pallets/parachain-info", default-features = parachains-runtimes-test-utils = { path = "../../test-utils", default-features = false } # Polkadot -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false} -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false} -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false} -pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false} -polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false} +xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } +pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } +polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } # Bridges pallet-xcm-bridge-hub-router = { path = "../../../../../bridges/modules/xcm-bridge-hub-router", default-features = false } diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/lib.rs b/cumulus/parachains/runtimes/assets/test-utils/src/lib.rs index 3c62faf3d5ec..e0f05fa7b0a4 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/lib.rs @@ -18,4 +18,5 @@ pub mod test_cases; pub mod test_cases_over_bridge; +pub mod xcm_helpers; pub use parachains_runtimes_test_utils::*; diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index 62faa3847817..b0616acb1a4d 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -15,10 +15,13 @@ //! Module contains predefined test-case scenarios for `Runtime` with various assets. +use super::xcm_helpers; use codec::Encode; use frame_support::{ assert_noop, assert_ok, - traits::{fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait}, + traits::{ + fungible::Mutate, fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait, + }, weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; @@ -175,6 +178,21 @@ pub fn teleports_for_native_asset_works< target_account_balance_before_teleport - existential_deposit ); + // Mint funds into account to ensure it has enough balance to pay delivery fees + let delivery_fees = + xcm_helpers::transfer_assets_delivery_fees::( + (native_asset_id, native_asset_to_teleport_away.into()).into(), + 0, + Unlimited, + dest_beneficiary, + dest, + ); + >::mint_into( + &target_account, + delivery_fees.into(), + ) + .unwrap(); + assert_ok!(RuntimeHelper::::do_teleport_assets::( RuntimeHelper::::origin_of(target_account.clone()), dest, @@ -184,6 +202,7 @@ pub fn teleports_for_native_asset_works< included_head.clone(), &alice, )); + // check balances assert_eq!( >::free_balance(&target_account), @@ -232,10 +251,21 @@ pub fn teleports_for_native_asset_works< &alice, )); + let delivery_fees = + xcm_helpers::transfer_assets_delivery_fees::( + (native_asset_id, native_asset_to_teleport_away.into()).into(), + 0, + Unlimited, + dest_beneficiary, + dest, + ); + // check balances assert_eq!( >::free_balance(&target_account), - target_account_balance_before_teleport - native_asset_to_teleport_away + target_account_balance_before_teleport - + native_asset_to_teleport_away - + delivery_fees.into() ); assert_eq!( >::free_balance(&CheckingAccount::get()), @@ -370,7 +400,7 @@ pub fn teleports_for_foreign_assets_works< fun: Fungible(buy_execution_fee_amount), }; - let teleported_foreign_asset_amount = 10000000000000; + let teleported_foreign_asset_amount = 10_000_000_000_000; let runtime_para_id = 1000; ExtBuilder::::default() .with_collators(collator_session_keys.collators()) @@ -400,11 +430,11 @@ pub fn teleports_for_foreign_assets_works< >::free_balance(&target_account), existential_deposit ); + // check `CheckingAccount` before assert_eq!( >::free_balance(&CheckingAccount::get()), existential_deposit ); - // check `CheckingAccount` before assert_eq!( >::balance( foreign_asset_id_multilocation.into(), @@ -540,6 +570,21 @@ pub fn teleports_for_foreign_assets_works< .into() ); + // Make sure the target account has enough native asset to pay for delivery fees + let delivery_fees = + xcm_helpers::transfer_assets_delivery_fees::( + (foreign_asset_id_multilocation, asset_to_teleport_away).into(), + 0, + Unlimited, + dest_beneficiary, + dest, + ); + >::mint_into( + &target_account, + delivery_fees.into(), + ) + .unwrap(); + assert_ok!(RuntimeHelper::::do_teleport_assets::( RuntimeHelper::::origin_of(target_account.clone()), dest, diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index a967384fb6dc..9852453d283b 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -20,7 +20,9 @@ use codec::Encode; use cumulus_primitives_core::XcmpMessageSource; use frame_support::{ assert_ok, - traits::{Currency, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError}, + traits::{ + fungible::Mutate, Currency, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError, + }, }; use frame_system::pallet_prelude::BlockNumberFor; use parachains_common::{AccountId, Balance}; @@ -164,6 +166,12 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works< }), }; + // Make sure sender has enough funds for paying delivery fees + // TODO: Get this fee via weighing the corresponding message + let delivery_fees = 1324039894; + >::mint_into(&alice_account, delivery_fees.into()) + .unwrap(); + // do pallet_xcm call reserve transfer assert_ok!(>::limited_reserve_transfer_assets( RuntimeHelper::::origin_of(alice_account.clone()), diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs b/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs new file mode 100644 index 000000000000..0aebe38fef53 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/test-utils/src/xcm_helpers.rs @@ -0,0 +1,108 @@ +// Copyright Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus 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. + +// Cumulus 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 Cumulus. If not, see . + +//! Helpers for calculating XCM delivery fees. + +use xcm::latest::prelude::*; + +/// Returns the delivery fees amount for pallet xcm's `teleport_assets` and +/// `reserve_transfer_assets` extrinsics. +/// Because it returns only a `u128`, it assumes delivery fees are only paid +/// in one asset and that asset is known. +pub fn transfer_assets_delivery_fees( + assets: MultiAssets, + fee_asset_item: u32, + weight_limit: WeightLimit, + beneficiary: MultiLocation, + destination: MultiLocation, +) -> u128 { + let message = teleport_assets_dummy_message(assets, fee_asset_item, weight_limit, beneficiary); + get_fungible_delivery_fees::(destination, message) +} + +/// Returns the delivery fees amount for a query response as a result of the execution +/// of a `ExpectError` instruction with no error. +pub fn query_response_delivery_fees(querier: MultiLocation) -> u128 { + // Message to calculate delivery fees, it's encoded size is what's important. + // This message reports that there was no error, if an error is reported, the encoded size would + // be different. + let message = Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + QueryResponse { + query_id: 0, // Dummy query id + response: Response::ExecutionResult(None), + max_weight: Weight::zero(), + querier: Some(querier), + }, + SetTopic([0u8; 32]), // Dummy topic + ]); + get_fungible_delivery_fees::(querier, message) +} + +/// Returns the delivery fees amount for the execution of `PayOverXcm` +pub fn pay_over_xcm_delivery_fees( + interior: Junctions, + destination: MultiLocation, + beneficiary: MultiLocation, + asset: MultiAsset, +) -> u128 { + // This is a dummy message. + // The encoded size is all that matters for delivery fees. + let message = Xcm(vec![ + DescendOrigin(interior), + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + SetAppendix(Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + ReportError(QueryResponseInfo { destination, query_id: 0, max_weight: Weight::zero() }), + ])), + TransferAsset { beneficiary, assets: vec![asset].into() }, + ]); + get_fungible_delivery_fees::(destination, message) +} + +/// Approximates the actual message sent by the teleport extrinsic. +/// The assets are not reanchored and the topic is a dummy one. +/// However, it should have the same encoded size, which is what matters for delivery fees. +/// Also has same encoded size as the one created by the reserve transfer assets extrinsic. +fn teleport_assets_dummy_message( + assets: MultiAssets, + fee_asset_item: u32, + weight_limit: WeightLimit, + beneficiary: MultiLocation, +) -> Xcm<()> { + Xcm(vec![ + ReceiveTeleportedAsset(assets.clone()), // Same encoded size as `ReserveAssetDeposited` + ClearOrigin, + BuyExecution { fees: assets.get(fee_asset_item as usize).unwrap().clone(), weight_limit }, + DepositAsset { assets: Wild(AllCounted(assets.len() as u32)), beneficiary }, + SetTopic([0u8; 32]), // Dummy topic + ]) +} + +/// Given a message, a sender, and a destination, it returns the delivery fees +fn get_fungible_delivery_fees(destination: MultiLocation, message: Xcm<()>) -> u128 { + let Ok((_, delivery_fees)) = validate_send::(destination, message) else { + unreachable!("message can be sent; qed") + }; + if let Some(delivery_fee) = delivery_fees.inner().first() { + let Fungible(delivery_fee_amount) = delivery_fee.fun else { + unreachable!("asset is fungible; qed"); + }; + delivery_fee_amount + } else { + 0 + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs index 791751e77368..190987b1cfeb 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -26,6 +26,8 @@ mod weights; pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -75,7 +77,7 @@ use parachains_common::{ }; // XCM Imports -use xcm::latest::prelude::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; /// The address format for describing accounts. @@ -314,7 +316,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = RootOrFellows; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -671,9 +673,21 @@ impl_runtime_apis! { use xcm::latest::prelude::*; use xcm_config::KsmRelayLocation; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + xcm_config::KsmRelayLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(KsmRelayLocation::get()) } @@ -714,6 +728,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs index 563115c8938e..85b983a6ab98 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-kusama/src/xcm_config.rs @@ -16,7 +16,8 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, + CENTS, }; use frame_support::{ match_types, parameter_types, @@ -26,6 +27,7 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{impls::ToStakingPot, xcm_config::ConcreteAssetFromSystem}; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -220,11 +222,21 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(KsmRelayLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs index 928b9d091ec5..dc23135f05d7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -26,6 +26,8 @@ mod weights; pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -75,7 +77,7 @@ use parachains_common::{ HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; // XCM Imports -use xcm::latest::prelude::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; /// The address format for describing accounts. @@ -314,7 +316,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = RootOrFellows; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -671,9 +673,21 @@ impl_runtime_apis! { use xcm::latest::prelude::*; use xcm_config::DotRelayLocation; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + xcm_config::DotRelayLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(DotRelayLocation::get()) } @@ -714,6 +728,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs index 6e9d6d586195..7378961f5760 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-polkadot/src/xcm_config.rs @@ -16,7 +16,8 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, + CENTS, }; use frame_support::{ match_types, parameter_types, @@ -26,6 +27,7 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{impls::ToStakingPot, xcm_config::ConcreteAssetFromSystem}; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -224,11 +226,21 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(DotRelayLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 3a8507ccf93e..6b6d84649d76 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -75,6 +75,7 @@ use bp_runtime::HeaderId; pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; +use xcm::latest::prelude::*; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -362,6 +363,20 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(xcm_config::TokenLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; @@ -371,7 +386,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -1014,9 +1029,21 @@ impl_runtime_apis! { use xcm::latest::prelude::*; use xcm_config::TokenLocation; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationToAccountId; + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForParentDelivery, + >; fn valid_destination() -> Result { Ok(TokenLocation::get()) } @@ -1057,6 +1084,7 @@ impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index b35011b095f5..0f6dfb13684e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -15,10 +15,10 @@ // along with Cumulus. If not, see . use super::{ - AccountId, AllPalletsWithSystem, Balances, BridgeGrandpaRococoInstance, - BridgeGrandpaWococoInstance, DeliveryRewardInBalance, ParachainInfo, ParachainSystem, - PolkadotXcm, RequiredStakeForStakeAndSlash, Runtime, RuntimeCall, RuntimeEvent, RuntimeFlavor, - RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, BridgeGrandpaRococoInstance, + BridgeGrandpaWococoInstance, DeliveryRewardInBalance, FeeAssetId, ParachainInfo, + ParachainSystem, PolkadotXcm, RequiredStakeForStakeAndSlash, Runtime, RuntimeCall, + RuntimeEvent, RuntimeFlavor, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, }; use crate::{ bridge_hub_rococo_config::ToBridgeHubWococoHaulBlobExporter, @@ -30,9 +30,16 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; -use parachains_common::{impls::ToStakingPot, xcm_config::ConcreteAssetFromSystem}; +use parachains_common::{ + impls::ToStakingPot, + xcm_config::{ConcreteAssetFromSystem, RelayOrOtherSystemParachains}, + TREASURY_PALLET_ID, +}; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use rococo_runtime_constants::system_parachain::SystemParachains; use sp_core::Get; +use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -41,6 +48,7 @@ use xcm_builder::{ ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeesToAccount, }; use xcm_executor::{ traits::{ExportXcm, WithOriginFilter}, @@ -55,6 +63,7 @@ parameter_types! { X2(GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; + pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } /// Adapter for resolving `NetworkId` based on `pub storage Flavor: RuntimeFlavor`. @@ -253,7 +262,12 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); + type FeeManager = XcmFeesToAccount< + Self, + RelayOrOtherSystemParachains, + AccountId, + TreasuryAccount, + >; type MessageExporter = BridgeHubRococoOrBridgeHubWococoSwitchExporter; type UniversalAliases = Nothing; type CallDispatcher = WithOriginFilter; @@ -261,6 +275,9 @@ impl xcm_executor::Config for XcmConfig { type Aliasers = Nothing; } +pub type PriceForParentDelivery = + ExponentialPrice; + /// Converts a local signed origin into an XCM multilocation. /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; @@ -269,7 +286,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs index d4e5aac34369..e77af189b4fd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases.rs @@ -186,7 +186,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< XcmConfig::AssetTransactor::deposit_asset( &ed, &sibling_parachain_location, - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ) .expect("deposited ed"); } @@ -194,7 +194,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< XcmConfig::AssetTransactor::deposit_asset( &fee, &sibling_parachain_location, - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ) .expect("deposited fee"); diff --git a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs index ff16f93d8f54..cec4152bcc31 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/lib.rs @@ -45,6 +45,7 @@ pub mod fellowship; pub use ambassador::pallet_ambassador_origins; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; use fellowship::{ migration::import_kusama_fellowship, pallet_fellowship_origins, Fellows, FellowshipCollectiveInstance, @@ -98,7 +99,7 @@ pub use sp_runtime::BuildStorage; // Polkadot imports use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; -use xcm::latest::BodyId; +use xcm::latest::prelude::*; use xcm_executor::XcmExecutor; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -401,6 +402,20 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(xcm_config::DotLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; @@ -410,7 +425,8 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EitherOfDiverse, Fellows>; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = weights::cumulus_pallet_xcmp_queue::WeightInfo; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = + polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs index 79d4c53c2ee2..c64d688e5f1b 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-polkadot/src/xcm_config.rs @@ -14,8 +14,9 @@ // limitations under the License. use super::{ - AccountId, AllPalletsWithSystem, Balances, Fellows, ParachainInfo, ParachainSystem, - PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, Fellows, ParachainInfo, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + TransactionByteFee, WeightToFee, XcmpQueue, }; use frame_support::{ match_types, parameter_types, @@ -26,6 +27,7 @@ use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; use parachains_common::{impls::ToStakingPot, xcm_config::ConcreteAssetFromSystem}; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -279,11 +281,14 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index d59fe2fd29e9..448cc4f41603 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -54,13 +54,14 @@ pallet-contracts = { path = "../../../../../substrate/frame/contracts", default- pallet-contracts-primitives = { path = "../../../../../substrate/frame/contracts/primitives", default-features = false} # Polkadot -pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false} -polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false} -polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false} -polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false} -xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false} -xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false} -xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false} +pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } +polkadot-core-primitives = { path = "../../../../../polkadot/core-primitives", default-features = false } +polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", default-features = false } +polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } +rococo-runtime-constants = { path = "../../../../../polkadot/runtime/rococo/constants", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false } # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } @@ -115,6 +116,7 @@ std = [ "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", + "rococo-runtime-constants/std", "scale-info/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 70392c5ecbcc..1ea3eaa2e478 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -224,13 +224,17 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; } +parameter_types! { + pub const TransactionByteFee: Balance = MILLICENTS; +} + impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter>; type WeightToFee = WeightToFee; /// Relay Chain `TransactionByteFee` / 10 - type LengthToFee = ConstantMultiplier>; + type LengthToFee = ConstantMultiplier; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type OperationalFeeMultiplier = ConstU8<5>; } diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs index ff8f46b9d117..71a789e3e25a 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -15,8 +15,9 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, ParachainInfo, ParachainSystem, PolkadotXcm, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmpQueue, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmpQueue, }; +use crate::common::rococo::currency::CENTS; use frame_support::{ match_types, parameter_types, traits::{ConstU32, EitherOfDiverse, Everything, Nothing}, @@ -24,8 +25,14 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; -use parachains_common::xcm_config::ConcreteAssetFromSystem; +use parachains_common::{ + xcm_config::{ConcreteAssetFromSystem, RelayOrOtherSystemParachains}, + TREASURY_PALLET_ID, +}; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::ExponentialPrice; +use rococo_runtime_constants::system_parachain::SystemParachains; +use sp_runtime::traits::AccountIdConversion; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -34,7 +41,7 @@ use xcm_builder::{ NativeAsset, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WithComputedOrigin, WithUniqueTopic, + WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount, }; use xcm_executor::XcmExecutor; @@ -44,7 +51,7 @@ parameter_types! { pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub UniversalLocation: InteriorMultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const ExecutiveBody: BodyId = BodyId::Executive; - pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub TreasuryAccount: Option = Some(TREASURY_PALLET_ID.into_account_truncating()); } /// We allow root and the Relay Chain council to execute privileged collator selection operations. @@ -167,7 +174,12 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = ConstU32<8>; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = (); + type FeeManager = XcmFeesToAccount< + Self, + RelayOrOtherSystemParachains, + AccountId, + TreasuryAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; @@ -179,11 +191,14 @@ impl xcm_executor::Config for XcmConfig { /// Forms the basis for local origins sending/executing XCMs. pub type LocalOriginToLocation = SignedToAccountId32; +pub type PriceForParentDelivery = + ExponentialPrice; + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; @@ -231,6 +246,20 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } +parameter_types! { + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(RelayLocation::get()); + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); +} + +pub type PriceForSiblingParachainDelivery = polkadot_runtime_common::xcm_sender::ExponentialPrice< + FeeAssetId, + BaseDeliveryFee, + TransactionByteFee, + XcmpQueue, +>; + impl cumulus_pallet_xcmp_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; @@ -243,7 +272,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { >; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = PriceForSiblingParachainDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index fe0f19c30632..86389425eb5e 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -33,6 +33,7 @@ mod weights; pub mod xcm_config; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; use frame_support::{ construct_runtime, dispatch::DispatchClass, @@ -50,6 +51,7 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, EnsureSigned, }; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use smallvec::smallvec; use sp_api::impl_runtime_apis; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -488,7 +490,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = (); - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml index 029d5d10f986..a662a5e80665 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/Cargo.toml @@ -42,6 +42,7 @@ polkadot-parachain-primitives = { path = "../../../../../polkadot/parachain", de xcm = { package = "staging-xcm", path = "../../../../../polkadot/xcm", default-features = false} xcm-builder = { package = "staging-xcm-builder", path = "../../../../../polkadot/xcm/xcm-builder", default-features = false} xcm-executor = { package = "staging-xcm-executor", path = "../../../../../polkadot/xcm/xcm-executor", default-features = false} +polkadot-runtime-common = { path = "../../../../../polkadot/runtime/common", default-features = false } # Cumulus cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false } @@ -86,6 +87,7 @@ std = [ "parachain-info/std", "parachains-common/std", "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", "scale-info/std", "sp-api/std", "sp-block-builder/std", @@ -118,6 +120,7 @@ runtime-benchmarks = [ "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 50c5a445c25f..dcea349f3a0e 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -23,6 +23,8 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_primitives_core::ParaId; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_api::impl_runtime_apis; use sp_core::OpaqueMetadata; use sp_runtime::{ @@ -511,7 +513,7 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type ControllerOrigin = EnsureRoot; type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight; - type PriceForSiblingDelivery = (); + type PriceForSiblingDelivery = NoPriceForMessageDelivery; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs index faaef09b26e1..f216af2aaee7 100644 --- a/cumulus/primitives/core/src/lib.rs +++ b/cumulus/primitives/core/src/lib.rs @@ -96,7 +96,7 @@ pub struct ChannelInfo { pub trait GetChannelInfo { fn get_channel_status(id: ParaId) -> ChannelStatus; - fn get_channel_max(id: ParaId) -> Option; + fn get_channel_info(id: ParaId) -> Option; } /// Something that should be called when sending an upward message. diff --git a/cumulus/primitives/utility/Cargo.toml b/cumulus/primitives/utility/Cargo.toml index 9ed1e664ac21..691a4599b2c4 100644 --- a/cumulus/primitives/utility/Cargo.toml +++ b/cumulus/primitives/utility/Cargo.toml @@ -15,11 +15,12 @@ sp-runtime = { path = "../../../substrate/primitives/runtime", default-features sp-std = { path = "../../../substrate/primitives/std", default-features = false} # Polkadot -polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false} -xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false} +polkadot-runtime-common = { path = "../../../polkadot/runtime/common", default-features = false } +polkadot-runtime-parachains = { path = "../../../polkadot/runtime/parachains", default-features = false } +xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false} xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false} - +pallet-xcm-benchmarks = { path = "../../../polkadot/xcm/pallet-xcm-benchmarks", default-features = false } # Cumulus cumulus-primitives-core = { path = "../core", default-features = false } @@ -30,7 +31,9 @@ std = [ "codec/std", "cumulus-primitives-core/std", "frame-support/std", + "pallet-xcm-benchmarks/std", "polkadot-runtime-common/std", + "polkadot-runtime-parachains/std", "sp-io/std", "sp-runtime/std", "sp-std/std", @@ -41,7 +44,9 @@ std = [ runtime-benchmarks = [ "frame-support/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 7d07bc329ed4..c4ce67194855 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -28,29 +28,13 @@ use frame_support::{ }, weights::Weight, }; -use polkadot_runtime_common::xcm_sender::ConstantPrice; +use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; use sp_runtime::{traits::Saturating, SaturatedConversion}; use sp_std::{marker::PhantomData, prelude::*}; use xcm::{latest::prelude::*, WrapVersion}; use xcm_builder::TakeRevenue; use xcm_executor::traits::{MatchesFungibles, TransactAsset, WeightTrader}; -pub trait PriceForParentDelivery { - fn price_for_parent_delivery(message: &Xcm<()>) -> MultiAssets; -} - -impl PriceForParentDelivery for () { - fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { - MultiAssets::new() - } -} - -impl> PriceForParentDelivery for ConstantPrice { - fn price_for_parent_delivery(_: &Xcm<()>) -> MultiAssets { - T::get() - } -} - /// Xcm router which recognises the `Parent` destination and handles it by sending the message into /// the given UMP `UpwardMessageSender` implementation. Thus this essentially adapts an /// `UpwardMessageSender` trait impl into a `SendXcm` trait impl. @@ -63,7 +47,7 @@ impl SendXcm for ParentAsUmp where T: UpwardMessageSender, W: WrapVersion, - P: PriceForParentDelivery, + P: PriceForMessageDelivery, { type Ticket = Vec; @@ -76,7 +60,7 @@ where if d.contains_parents_only(1) { // An upward message for the relay chain. let xcm = msg.take().ok_or(SendError::MissingArgument)?; - let price = P::price_for_parent_delivery(&xcm); + let price = P::price_for_delivery((), &xcm); let versioned_xcm = W::wrap_version(&d, xcm).map_err(|()| SendError::DestinationUnsupported)?; let data = versioned_xcm.encode(); @@ -280,7 +264,7 @@ pub struct XcmFeesTo32ByteAccount, - ReceiverAccount: frame_support::traits::Get>, + ReceiverAccount: Get>, > TakeRevenue for XcmFeesTo32ByteAccount { fn take_revenue(revenue: MultiAsset) { @@ -288,9 +272,7 @@ impl< let ok = FungiblesMutateAdapter::deposit_asset( &revenue, &(X1(AccountId32 { network: None, id: receiver.into() }).into()), - // We aren't able to track the XCM that initiated the fee deposit, so we create a - // fake message hash here - &XcmContext::with_message_id([0; 32]), + None, ) .is_ok(); @@ -542,3 +524,58 @@ mod tests { assert_eq!(trader.buy_weight(weight_to_buy, payment, &ctx), Err(XcmError::NotWithdrawable)); } } + +/// Implementation of `pallet_xcm_benchmarks::EnsureDelivery` which helps to ensure delivery to the +/// parent relay chain. Deposits existential deposit for origin (if needed). +/// Deposits estimated fee to the origin account (if needed). +/// Allows to trigger additional logic for specific `ParaId` (e.g. open HRMP channel) (if neeeded). +#[cfg(feature = "runtime-benchmarks")] +pub struct ToParentDeliveryHelper( + sp_std::marker::PhantomData<(XcmConfig, ExistentialDeposit, PriceForDelivery)>, +); + +#[cfg(feature = "runtime-benchmarks")] +impl< + XcmConfig: xcm_executor::Config, + ExistentialDeposit: Get>, + PriceForDelivery: PriceForMessageDelivery, + > pallet_xcm_benchmarks::EnsureDelivery + for ToParentDeliveryHelper +{ + fn ensure_successful_delivery( + origin_ref: &MultiLocation, + _dest: &MultiLocation, + fee_reason: xcm_executor::traits::FeeReason, + ) -> (Option, Option) { + use xcm::latest::{MAX_INSTRUCTIONS_TO_DECODE, MAX_ITEMS_IN_MULTIASSETS}; + use xcm_executor::{traits::FeeManager, FeesMode}; + + let mut fees_mode = None; + if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { + // if not waived, we need to set up accounts for paying and receiving fees + + // mint ED to origin if needed + if let Some(ed) = ExistentialDeposit::get() { + XcmConfig::AssetTransactor::deposit_asset(&ed, &origin_ref, None).unwrap(); + } + + // overestimate delivery fee + let mut max_assets: Vec = Vec::new(); + for i in 0..MAX_ITEMS_IN_MULTIASSETS { + max_assets.push((GeneralIndex(i as u128), 100u128).into()); + } + let overestimated_xcm = + vec![WithdrawAsset(max_assets.into()); MAX_INSTRUCTIONS_TO_DECODE as usize].into(); + let overestimated_fees = PriceForDelivery::price_for_delivery((), &overestimated_xcm); + + // mint overestimated fee to origin + for fee in overestimated_fees.inner() { + XcmConfig::AssetTransactor::deposit_asset(&fee, &origin_ref, None).unwrap(); + } + + // expected worst case - direct withdraw + fees_mode = Some(FeesMode { jit_withdraw: true }); + } + (fees_mode, None) + } +} diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index caf73ae1e41c..3da7814bec35 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -1335,10 +1335,10 @@ pub struct TestContext { pub args: T, } -/// Struct that help with tests where either dispatchables or assertions need +/// Struct that helps with tests where either dispatchables or assertions need /// to be reused. The struct keeps the test's arguments of your choice in the generic `Args`. -/// These arguments can be easily reused and shared between the assertions functions -/// and dispatchables functions, which are also stored in `Test`. +/// These arguments can be easily reused and shared between the assertion functions +/// and dispatchable functions, which are also stored in `Test`. /// `Origin` corresponds to the chain where the XCM interaction starts with an initial execution. /// `Destination` corresponds to the last chain where an effect of the intial execution is expected /// happen. `Hops` refer all the ordered intermediary chains an initial XCM execution can provoke diff --git a/polkadot/parachain/src/primitives.rs b/polkadot/parachain/src/primitives.rs index 5f77810f5c23..3247e841422e 100644 --- a/polkadot/parachain/src/primitives.rs +++ b/polkadot/parachain/src/primitives.rs @@ -333,7 +333,7 @@ impl DmpMessageHandler for () { } /// The aggregate XCMP message format. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)] pub enum XcmpMessageFormat { /// Encoded `VersionedXcm` messages, all concatenated. ConcatenatedVersionedXcm, diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 89312acc913c..8af7f11f1d7d 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -52,6 +52,9 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } + +pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } [dev-dependencies] @@ -89,6 +92,7 @@ std = [ "pallet-transaction-payment/std", "pallet-treasury/std", "pallet-vesting/std", + "pallet-xcm-benchmarks/std", "parity-scale-codec/std", "primitives/std", "runtime-parachains/std", @@ -105,6 +109,7 @@ std = [ "sp-staking/std", "sp-std/std", "xcm-builder/std", + "xcm-executor/std", "xcm/std", ] runtime-benchmarks = [ @@ -123,11 +128,13 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", "primitives/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] try-runtime = [ "frame-election-provider-support/try-runtime", diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index ff529143c509..4d31c92cdd3a 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -31,38 +31,63 @@ use SendError::*; /// Simple value-bearing trait for determining/expressing the assets required to be paid for a /// messages to be delivered to a parachain. -pub trait PriceForParachainDelivery { +pub trait PriceForMessageDelivery { + /// Type used for charging different prices to different destinations + type Id; /// Return the assets required to deliver `message` to the given `para` destination. - fn price_for_parachain_delivery(para: ParaId, message: &Xcm<()>) -> MultiAssets; + fn price_for_delivery(id: Self::Id, message: &Xcm<()>) -> MultiAssets; } -impl PriceForParachainDelivery for () { - fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { +impl PriceForMessageDelivery for () { + type Id = (); + + fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> MultiAssets { + MultiAssets::new() + } +} + +pub struct NoPriceForMessageDelivery(PhantomData); +impl PriceForMessageDelivery for NoPriceForMessageDelivery { + type Id = Id; + + fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> MultiAssets { MultiAssets::new() } } -/// Implementation of `PriceForParachainDelivery` which returns a fixed price. +/// Implementation of [`PriceForMessageDelivery`] which returns a fixed price. pub struct ConstantPrice(sp_std::marker::PhantomData); -impl> PriceForParachainDelivery for ConstantPrice { - fn price_for_parachain_delivery(_: ParaId, _: &Xcm<()>) -> MultiAssets { +impl> PriceForMessageDelivery for ConstantPrice { + type Id = (); + + fn price_for_delivery(_: Self::Id, _: &Xcm<()>) -> MultiAssets { T::get() } } -/// Implementation of `PriceForParachainDelivery` which returns an exponentially increasing price. -/// The `A` type parameter is used to denote the asset ID that will be used for paying the delivery -/// fee. -/// +/// Implementation of [`PriceForMessageDelivery`] which returns an exponentially increasing price. /// The formula for the fee is based on the sum of a base fee plus a message length fee, multiplied -/// by a specified factor. In mathematical form, it is `F * (B + encoded_msg_len * M)`. +/// by a specified factor. In mathematical form: +/// +/// `F * (B + encoded_msg_len * M)` +/// +/// Thus, if F = 1 and M = 0, this type is equivalent to [`ConstantPrice`]. +/// +/// The type parameters are understood as follows: +/// +/// - `A`: Used to denote the asset ID that will be used for paying the delivery fee. +/// - `B`: The base fee to pay for message delivery. +/// - `M`: The fee to pay for each and every byte of the message after encoding it. +/// - `F`: A fee factor multiplier. It can be understood as the exponent term in the formula. pub struct ExponentialPrice(sp_std::marker::PhantomData<(A, B, M, F)>); -impl, B: Get, M: Get, F: FeeTracker> PriceForParachainDelivery +impl, B: Get, M: Get, F: FeeTracker> PriceForMessageDelivery for ExponentialPrice { - fn price_for_parachain_delivery(para: ParaId, msg: &Xcm<()>) -> MultiAssets { + type Id = F::Id; + + fn price_for_delivery(id: Self::Id, msg: &Xcm<()>) -> MultiAssets { let msg_fee = (msg.encoded_size() as u128).saturating_mul(M::get()); let fee_sum = B::get().saturating_add(msg_fee); - let amount = F::get_fee_factor(para).saturating_mul_int(fee_sum); + let amount = F::get_fee_factor(id).saturating_mul_int(fee_sum); (A::get(), amount).into() } } @@ -70,8 +95,10 @@ impl, B: Get, M: Get, F: FeeTracker> PriceForParacha /// XCM sender for relay chain. It only sends downward message. pub struct ChildParachainRouter(PhantomData<(T, W, P)>); -impl - SendXcm for ChildParachainRouter +impl SendXcm + for ChildParachainRouter +where + P: PriceForMessageDelivery, { type Ticket = (HostConfiguration>, ParaId, Vec); @@ -91,7 +118,7 @@ impl>::config(); let para = id.into(); - let price = P::price_for_parachain_delivery(para, &xcm); + let price = P::price_for_delivery(para, &xcm); let blob = W::wrap_version(&d, xcm).map_err(|()| DestinationUnsupported)?.encode(); >::can_queue_downward_message(&config, ¶, &blob) .map_err(Into::::into)?; @@ -109,6 +136,94 @@ impl( + sp_std::marker::PhantomData<( + XcmConfig, + ExistentialDeposit, + PriceForDelivery, + ParaId, + ToParaIdHelper, + )>, +); + +#[cfg(feature = "runtime-benchmarks")] +impl< + XcmConfig: xcm_executor::Config, + ExistentialDeposit: Get>, + PriceForDelivery: PriceForMessageDelivery, + Parachain: Get, + ToParachainHelper: EnsureForParachain, + > pallet_xcm_benchmarks::EnsureDelivery + for ToParachainDeliveryHelper< + XcmConfig, + ExistentialDeposit, + PriceForDelivery, + Parachain, + ToParachainHelper, + > +{ + fn ensure_successful_delivery( + origin_ref: &MultiLocation, + _dest: &MultiLocation, + fee_reason: xcm_executor::traits::FeeReason, + ) -> (Option, Option) { + use xcm_executor::{ + traits::{FeeManager, TransactAsset}, + FeesMode, + }; + + let mut fees_mode = None; + if !XcmConfig::FeeManager::is_waived(Some(origin_ref), fee_reason) { + // if not waived, we need to set up accounts for paying and receiving fees + + // mint ED to origin if needed + if let Some(ed) = ExistentialDeposit::get() { + XcmConfig::AssetTransactor::deposit_asset(&ed, &origin_ref, None).unwrap(); + } + + // overestimate delivery fee + let overestimated_xcm = vec![ClearOrigin; 128].into(); + let overestimated_fees = + PriceForDelivery::price_for_delivery(Parachain::get(), &overestimated_xcm); + + // mint overestimated fee to origin + for fee in overestimated_fees.inner() { + XcmConfig::AssetTransactor::deposit_asset(&fee, &origin_ref, None).unwrap(); + } + + // allow more initialization for target parachain + ToParachainHelper::ensure(Parachain::get()); + + // expected worst case - direct withdraw + fees_mode = Some(FeesMode { jit_withdraw: true }); + } + (fees_mode, None) + } +} + +/// Ensure more initialization for `ParaId`. (e.g. open HRMP channels, ...) +#[cfg(feature = "runtime-benchmarks")] +pub trait EnsureForParachain { + fn ensure(para_id: ParaId); +} +#[cfg(feature = "runtime-benchmarks")] +impl EnsureForParachain for () { + fn ensure(_: ParaId) { + // doing nothing + } +} + #[cfg(test)] mod tests { use super::*; @@ -124,7 +239,17 @@ mod tests { struct TestFeeTracker; impl FeeTracker for TestFeeTracker { - fn get_fee_factor(_: ParaId) -> FixedU128 { + type Id = ParaId; + + fn get_fee_factor(_: Self::Id) -> FixedU128 { + FixedU128::from_rational(101, 100) + } + + fn increase_fee_factor(_: Self::Id, _: FixedU128) -> FixedU128 { + FixedU128::from_rational(101, 100) + } + + fn decrease_fee_factor(_: Self::Id) -> FixedU128 { FixedU128::from_rational(101, 100) } } @@ -142,21 +267,21 @@ mod tests { // message_length = 1 let result: u128 = TestFeeTracker::get_fee_factor(id).saturating_mul_int(b + m); assert_eq!( - TestExponentialPrice::price_for_parachain_delivery(id, &Xcm(vec![])), + TestExponentialPrice::price_for_delivery(id, &Xcm(vec![])), (FeeAssetId::get(), result).into() ); // message size = 2 let result: u128 = TestFeeTracker::get_fee_factor(id).saturating_mul_int(b + (2 * m)); assert_eq!( - TestExponentialPrice::price_for_parachain_delivery(id, &Xcm(vec![ClearOrigin])), + TestExponentialPrice::price_for_delivery(id, &Xcm(vec![ClearOrigin])), (FeeAssetId::get(), result).into() ); // message size = 4 let result: u128 = TestFeeTracker::get_fee_factor(id).saturating_mul_int(b + (4 * m)); assert_eq!( - TestExponentialPrice::price_for_parachain_delivery( + TestExponentialPrice::price_for_delivery( id, &Xcm(vec![SetAppendix(Xcm(vec![ClearOrigin]))]) ), diff --git a/polkadot/runtime/parachains/src/dmp.rs b/polkadot/runtime/parachains/src/dmp.rs index bc7491a2c61a..15147e9210e2 100644 --- a/polkadot/runtime/parachains/src/dmp.rs +++ b/polkadot/runtime/parachains/src/dmp.rs @@ -17,12 +17,12 @@ //! To prevent Out of Memory errors on the `DownwardMessageQueue`, an //! exponential fee factor (`DeliveryFeeFactor`) is set. The fee factor //! increments exponentially after the number of messages in the -//! `DownwardMessageQueue` pass a threshold. This threshold is set as: +//! `DownwardMessageQueue` passes a threshold. This threshold is set as: //! //! ```ignore //! // Maximum max sized messages that can be send to //! // the DownwardMessageQueue before it runs out of memory -//! max_messsages = MAX_POSSIBLE_ALLOCATION / max_downward_message_size +//! max_messages = MAX_POSSIBLE_ALLOCATION / max_downward_message_size //! threshold = max_messages / THRESHOLD_FACTOR //! ``` //! Based on the THRESHOLD_FACTOR, the threshold is set as a fraction of the @@ -144,7 +144,7 @@ pub mod pallet { FixedU128::from_u32(1) } - /// The number to multiply the base delivery fee by. + /// The factor to multiply the base delivery fee by. #[pallet::storage] pub(crate) type DeliveryFeeFactor = StorageMap<_, Twox64Concat, ParaId, FixedU128, ValueQuery, InitialFactor>; @@ -243,10 +243,9 @@ impl Pallet { let threshold = Self::dmq_max_length(config.max_downward_message_size).saturating_div(THRESHOLD_FACTOR); if q_len > (threshold as usize) { - let message_size_factor = - FixedU128::from_u32(serialized_len.saturating_div(1024) as u32) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - Self::increment_fee_factor(para, message_size_factor); + let message_size_factor = FixedU128::from((serialized_len / 1024) as u128) + .saturating_mul(MESSAGE_SIZE_FEE_BASE); + Self::increase_fee_factor(para, message_size_factor); } Ok(()) @@ -304,7 +303,7 @@ impl Pallet { let threshold = Self::dmq_max_length(config.max_downward_message_size).saturating_div(THRESHOLD_FACTOR); if q_len <= (threshold as usize) { - Self::decrement_fee_factor(para); + Self::decrease_fee_factor(para); } T::DbWeight::get().reads_writes(1, 1) } @@ -337,32 +336,26 @@ impl Pallet { ) -> Vec>> { DownwardMessageQueues::::get(&recipient) } +} - /// Raise the delivery fee factor by a multiplicative factor and stores the resulting value. - /// - /// Returns the new delivery fee factor after the increment. - pub(crate) fn increment_fee_factor(para: ParaId, message_size_factor: FixedU128) -> FixedU128 { - >::mutate(para, |f| { - *f = f.saturating_mul(EXPONENTIAL_FEE_BASE + message_size_factor); +impl FeeTracker for Pallet { + type Id = ParaId; + + fn get_fee_factor(id: Self::Id) -> FixedU128 { + DeliveryFeeFactor::::get(id) + } + + fn increase_fee_factor(id: Self::Id, message_size_factor: FixedU128) -> FixedU128 { + >::mutate(id, |f| { + *f = f.saturating_mul(EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor)); *f }) } - /// Reduce the delivery fee factor by a multiplicative factor and stores the resulting value. - /// - /// Does not reduce the fee factor below the initial value, which is currently set as 1. - /// - /// Returns the new delivery fee factor after the decrement. - pub(crate) fn decrement_fee_factor(para: ParaId) -> FixedU128 { - >::mutate(para, |f| { + fn decrease_fee_factor(id: Self::Id) -> FixedU128 { + >::mutate(id, |f| { *f = InitialFactor::get().max(*f / EXPONENTIAL_FEE_BASE); *f }) } } - -impl FeeTracker for Pallet { - fn get_fee_factor(para: ParaId) -> FixedU128 { - DeliveryFeeFactor::::get(para) - } -} diff --git a/polkadot/runtime/parachains/src/dmp/tests.rs b/polkadot/runtime/parachains/src/dmp/tests.rs index a65984840da5..f9197b156a1e 100644 --- a/polkadot/runtime/parachains/src/dmp/tests.rs +++ b/polkadot/runtime/parachains/src/dmp/tests.rs @@ -233,7 +233,7 @@ fn verify_dmq_mqc_head_is_externally_accessible() { } #[test] -fn verify_fee_increment_and_decrement() { +fn verify_fee_increase_and_decrease() { let a = ParaId::from(123); let mut genesis = default_genesis_config(); genesis.configuration.config.max_downward_message_size = 16777216; diff --git a/polkadot/runtime/parachains/src/lib.rs b/polkadot/runtime/parachains/src/lib.rs index e0ace86d3794..2509edbee3cb 100644 --- a/polkadot/runtime/parachains/src/lib.rs +++ b/polkadot/runtime/parachains/src/lib.rs @@ -59,7 +59,21 @@ use sp_runtime::{DispatchResult, FixedU128}; /// Trait for tracking message delivery fees on a transport protocol. pub trait FeeTracker { - fn get_fee_factor(para: ParaId) -> FixedU128; + /// Type used for assigning different fee factors to different destinations + type Id; + /// Returns the evolving exponential fee factor which will be used to calculate the delivery + /// fees. + fn get_fee_factor(id: Self::Id) -> FixedU128; + /// Increases the delivery fee factor by a factor based on message size and records the result. + /// + /// Returns the new delivery fee factor after the increase. + fn increase_fee_factor(id: Self::Id, message_size_factor: FixedU128) -> FixedU128; + /// Decreases the delivery fee factor by a constant factor and records the result. + /// + /// Does not reduce the fee factor below the initial value, which is currently set as 1. + /// + /// Returns the new delivery fee factor after the decrease. + fn decrease_fee_factor(id: Self::Id) -> FixedU128; } /// Schedule a para to be initialized at the start of the next session with the given genesis data. diff --git a/polkadot/runtime/rococo/constants/Cargo.toml b/polkadot/runtime/rococo/constants/Cargo.toml index a0ef1edf08d1..f99dbd123367 100644 --- a/polkadot/runtime/rococo/constants/Cargo.toml +++ b/polkadot/runtime/rococo/constants/Cargo.toml @@ -16,6 +16,8 @@ sp-runtime = { path = "../../../../substrate/primitives/runtime", default-featur sp-weights = { path = "../../../../substrate/primitives/weights", default-features = false } sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +xcm = { package = "staging-xcm", path = "../../../xcm", default-features = false } + [features] default = [ "std" ] std = [ @@ -25,4 +27,5 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-weights/std", + "xcm/std", ] diff --git a/polkadot/runtime/rococo/constants/src/lib.rs b/polkadot/runtime/rococo/constants/src/lib.rs index 2200f7ddefe1..19225c68151c 100644 --- a/polkadot/runtime/rococo/constants/src/lib.rs +++ b/polkadot/runtime/rococo/constants/src/lib.rs @@ -100,6 +100,26 @@ pub mod fee { } } +/// System Parachains. +pub mod system_parachain { + use xcm::latest::prelude::*; + + /// Network's Asset Hub parachain ID. + pub const ASSET_HUB_ID: u32 = 1000; + /// Contracts parachain ID. + pub const CONTRACTS_ID: u32 = 1002; + /// Encointer parachain ID. + pub const ENCOINTER_ID: u32 = 1003; + /// BridgeHub parachain ID. + pub const BRIDGE_HUB_ID: u32 = 1013; + + frame_support::match_types! { + pub type SystemParachains: impl Contains = { + MultiLocation { parents: 0, interior: X1(Parachain(ASSET_HUB_ID | CONTRACTS_ID | ENCOINTER_ID | BRIDGE_HUB_ID)) } + }; + } +} + #[cfg(test)] mod tests { use super::{ diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 3c08b9b2f94e..f4264ea35336 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2072,14 +2072,29 @@ sp_api::impl_runtime_apis! { use sp_storage::TrackedStorageKey; use xcm::latest::prelude::*; use xcm_config::{ - LocalCheckAccount, LocationConverter, AssetHub, TokenLocation, XcmConfig, + AssetHub, LocalCheckAccount, LocationConverter, TokenLocation, XcmConfig, }; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub ToParachain: ParaId = rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + } + impl frame_system_benchmarking::Config for Runtime {} impl frame_benchmarking::baseline::Config for Runtime {} impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = XcmConfig; type AccountIdConverter = LocationConverter; + type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< + XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForChildParachainDelivery, + ToParachain, + (), + >; fn valid_destination() -> Result { Ok(AssetHub::get()) } @@ -2116,6 +2131,7 @@ sp_api::impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs index a766e2179ea7..fb1653c549e1 100644 --- a/polkadot/runtime/rococo/src/xcm_config.rs +++ b/polkadot/runtime/rococo/src/xcm_config.rs @@ -18,7 +18,7 @@ use super::{ parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, Fellows, ParaId, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmPallet, + RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, Treasury, WeightToFee, XcmPallet, }; use crate::governance::StakingAdmin; @@ -29,7 +29,7 @@ use frame_support::{ weights::Weight, }; use frame_system::EnsureRoot; -use rococo_runtime_constants::currency::CENTS; +use rococo_runtime_constants::{currency::CENTS, system_parachain::*}; use runtime_common::{ xcm_sender::{ChildParachainRouter, ExponentialPrice}, ToAuthor, @@ -43,7 +43,7 @@ use xcm_builder::{ DescribeFamily, FixedWeightBounds, HashedDescription, IsChildSystemParachain, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount, }; use xcm_executor::XcmExecutor; @@ -53,6 +53,7 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + pub TreasuryAccount: Option = Some(Treasury::account_id()); } pub type LocationConverter = ( @@ -100,22 +101,22 @@ parameter_types! { pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } +pub type PriceForChildParachainDelivery = + ExponentialPrice; + /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our /// individual routers. -pub type XcmRouter = WithUniqueTopic<( +pub type XcmRouter = WithUniqueTopic< // Only one router so far - use DMP to communicate with child parachains. - ChildParachainRouter< - Runtime, - XcmPallet, - ExponentialPrice, - >, -)>; + ChildParachainRouter, +>; parameter_types! { pub const Roc: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); - pub const AssetHub: MultiLocation = Parachain(1000).into_location(); - pub const Contracts: MultiLocation = Parachain(1002).into_location(); - pub const Encointer: MultiLocation = Parachain(1003).into_location(); + pub const AssetHub: MultiLocation = Parachain(ASSET_HUB_ID).into_location(); + pub const Contracts: MultiLocation = Parachain(CONTRACTS_ID).into_location(); + pub const Encointer: MultiLocation = Parachain(ENCOINTER_ID).into_location(); + pub const BridgeHub: MultiLocation = Parachain(BRIDGE_HUB_ID).into_location(); pub const Tick: MultiLocation = Parachain(100).into_location(); pub const Trick: MultiLocation = Parachain(110).into_location(); pub const Track: MultiLocation = Parachain(120).into_location(); @@ -125,6 +126,7 @@ parameter_types! { pub const RocForAssetHub: (MultiAssetFilter, MultiLocation) = (Roc::get(), AssetHub::get()); pub const RocForContracts: (MultiAssetFilter, MultiLocation) = (Roc::get(), Contracts::get()); pub const RocForEncointer: (MultiAssetFilter, MultiLocation) = (Roc::get(), Encointer::get()); + pub const RocForBridgeHub: (MultiAssetFilter, MultiLocation) = (Roc::get(), BridgeHub::get()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } @@ -135,6 +137,7 @@ pub type TrustedTeleporters = ( xcm_builder::Case, xcm_builder::Case, xcm_builder::Case, + xcm_builder::Case, ); match_types! { @@ -188,7 +191,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); + type FeeManager = XcmFeesToAccount; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; @@ -206,7 +209,7 @@ parameter_types! { #[cfg(feature = "runtime-benchmarks")] parameter_types! { - pub ReachableDest: Option = Some(Parachain(1000).into()); + pub ReachableDest: Option = Some(Parachain(ASSET_HUB_ID).into()); } /// Type to convert an `Origin` type value into a `MultiLocation` value which represents an interior diff --git a/polkadot/runtime/test-runtime/src/xcm_config.rs b/polkadot/runtime/test-runtime/src/xcm_config.rs index 2113bbae66ad..400658b13863 100644 --- a/polkadot/runtime/test-runtime/src/xcm_config.rs +++ b/polkadot/runtime/test-runtime/src/xcm_config.rs @@ -60,7 +60,11 @@ pub type Barrier = AllowUnpaidExecutionFrom; pub struct DummyAssetTransactor; impl TransactAsset for DummyAssetTransactor { - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult { + fn deposit_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> XcmResult { Ok(()) } diff --git a/polkadot/runtime/westend/constants/Cargo.toml b/polkadot/runtime/westend/constants/Cargo.toml index ea9ff3499f91..2243210975b1 100644 --- a/polkadot/runtime/westend/constants/Cargo.toml +++ b/polkadot/runtime/westend/constants/Cargo.toml @@ -16,6 +16,8 @@ sp-runtime = { path = "../../../../substrate/primitives/runtime", default-featur sp-weights = { path = "../../../../substrate/primitives/weights", default-features = false } sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +xcm = { package = "staging-xcm", path = "../../../xcm", default-features = false } + [features] default = [ "std" ] std = [ @@ -25,4 +27,5 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-weights/std", + "xcm/std", ] diff --git a/polkadot/runtime/westend/constants/src/lib.rs b/polkadot/runtime/westend/constants/src/lib.rs index 0dd64d092c34..4851303b589e 100644 --- a/polkadot/runtime/westend/constants/src/lib.rs +++ b/polkadot/runtime/westend/constants/src/lib.rs @@ -96,6 +96,24 @@ pub mod fee { } } +/// System Parachains. +pub mod system_parachain { + use xcm::latest::prelude::*; + + /// Network's Asset Hub parachain ID. + pub const ASSET_HUB_ID: u32 = 1000; + /// Collectives parachain ID. + pub const COLLECTIVES_ID: u32 = 1001; + /// BridgeHub parachain ID. + pub const BRIDGE_HUB_ID: u32 = 1002; + + frame_support::match_types! { + pub type SystemParachains: impl Contains = { + MultiLocation { parents: 0, interior: X1(Parachain(ASSET_HUB_ID | COLLECTIVES_ID | BRIDGE_HUB_ID ))} + }; + } +} + /// XCM protocol related constants. pub mod xcm { /// Pluralistic bodies existing within the consensus. @@ -108,14 +126,6 @@ pub mod xcm { } } -/// System Parachains. -pub mod system_parachain { - /// Statemint parachain ID. - pub const ASSET_HUB_ID: u32 = 1000; - /// Collectives parachain ID. - pub const COLLECTIVES_ID: u32 = 1001; -} - #[cfg(test)] mod tests { use super::{ diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 0e93b3449fec..a7ddfc52ce6c 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2182,9 +2182,24 @@ sp_api::impl_runtime_apis! { }; use xcm_config::{AssetHub, TokenLocation}; + parameter_types! { + pub ExistentialDepositMultiAsset: Option = Some(( + TokenLocation::get(), + ExistentialDeposit::get() + ).into()); + pub ToParachain: ParaId = westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + } + impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; type AccountIdConverter = xcm_config::LocationConverter; + type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositMultiAsset, + xcm_config::PriceForChildParachainDelivery, + ToParachain, + (), + >; fn valid_destination() -> Result { Ok(AssetHub::get()) } @@ -2221,6 +2236,7 @@ sp_api::impl_runtime_apis! { } impl pallet_xcm_benchmarks::generic::Config for Runtime { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { diff --git a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 87e63fbe3107..9939f16aa29f 100644 --- a/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/polkadot/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-nbnwcyh-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 @@ -55,8 +55,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 24_642_000 picoseconds. - Weight::from_parts(24_973_000, 3593) + // Minimum execution time: 24_815_000 picoseconds. + Weight::from_parts(25_098_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 50_882_000 picoseconds. - Weight::from_parts(51_516_000, 6196) + // Minimum execution time: 51_268_000 picoseconds. + Weight::from_parts(51_857_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -85,8 +85,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `210` // Estimated: `6196` - // Minimum execution time: 73_923_000 picoseconds. - Weight::from_parts(75_454_000, 6196) + // Minimum execution time: 74_113_000 picoseconds. + Weight::from_parts(74_721_000, 6196) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -111,8 +111,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3574` - // Minimum execution time: 29_035_000 picoseconds. - Weight::from_parts(30_086_000, 3574) + // Minimum execution time: 28_919_000 picoseconds. + Weight::from_parts(29_703_000, 3574) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 22_094_000 picoseconds. - Weight::from_parts(22_560_000, 3593) + // Minimum execution time: 21_685_000 picoseconds. + Weight::from_parts(22_528_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -133,8 +133,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 24_771_000 picoseconds. - Weight::from_parts(25_280_000, 3593) + // Minimum execution time: 25_192_000 picoseconds. + Weight::from_parts(25_445_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -152,8 +152,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3593` - // Minimum execution time: 49_777_000 picoseconds. - Weight::from_parts(50_833_000, 3593) + // Minimum execution time: 49_349_000 picoseconds. + Weight::from_parts(50_476_000, 3593) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -171,8 +171,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `109` // Estimated: `3593` - // Minimum execution time: 51_425_000 picoseconds. - Weight::from_parts(52_213_000, 3593) + // Minimum execution time: 51_386_000 picoseconds. + Weight::from_parts(52_141_000, 3593) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs index f025e5f1f3e1..dd6a29885ad6 100644 --- a/polkadot/runtime/westend/src/xcm_config.rs +++ b/polkadot/runtime/westend/src/xcm_config.rs @@ -19,7 +19,7 @@ use super::{ parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, FellowshipAdmin, GeneralAdmin, ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, - TransactionByteFee, WeightToFee, XcmPallet, + TransactionByteFee, Treasury, WeightToFee, XcmPallet, }; use frame_support::{ @@ -44,6 +44,7 @@ use xcm_builder::{ DescribeFamily, HashedDescription, IsConcrete, MintLocation, OriginToPluralityVoice, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeesToAccount, }; use xcm_executor::XcmExecutor; @@ -53,6 +54,7 @@ parameter_types! { pub const UniversalLocation: InteriorMultiLocation = X1(GlobalConsensus(ThisNetwork::get())); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + pub TreasuryAccount: Option = Some(Treasury::account_id()); /// The asset ID for the asset that we use to pay for message delivery fees. pub FeeAssetId: AssetId = Concrete(TokenLocation::get()); /// The base fee for the message delivery fees. @@ -95,29 +97,38 @@ type LocalOriginConverter = ( XcmPassthrough, ); +pub type PriceForChildParachainDelivery = + ExponentialPrice; + /// The XCM router. When we want to send an XCM message, we use this type. It amalgamates all of our /// individual routers. -pub type XcmRouter = WithUniqueTopic<( +pub type XcmRouter = WithUniqueTopic< // Only one router so far - use DMP to communicate with child parachains. - ChildParachainRouter< - Runtime, - XcmPallet, - ExponentialPrice, - >, -)>; + ChildParachainRouter, +>; parameter_types! { - pub const Wnd: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); pub const AssetHub: MultiLocation = Parachain(ASSET_HUB_ID).into_location(); - pub const WndForAssetHub: (MultiAssetFilter, MultiLocation) = (Wnd::get(), AssetHub::get()); pub const Collectives: MultiLocation = Parachain(COLLECTIVES_ID).into_location(); + pub const BridgeHub: MultiLocation = Parachain(BRIDGE_HUB_ID).into_location(); + pub const Wnd: MultiAssetFilter = Wild(AllOf { fun: WildFungible, id: Concrete(TokenLocation::get()) }); + pub const WndForAssetHub: (MultiAssetFilter, MultiLocation) = (Wnd::get(), AssetHub::get()); pub const WndForCollectives: (MultiAssetFilter, MultiLocation) = (Wnd::get(), Collectives::get()); + pub const WndForBridgeHub: (MultiAssetFilter, MultiLocation) = (Wnd::get(), BridgeHub::get()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; } -pub type TrustedTeleporters = - (xcm_builder::Case, xcm_builder::Case); +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + pub ReachableDest: Option = Some(Parachain(ASSET_HUB_ID).into()); +} + +pub type TrustedTeleporters = ( + xcm_builder::Case, + xcm_builder::Case, + xcm_builder::Case, +); match_types! { pub type OnlyParachains: impl Contains = { @@ -174,7 +185,7 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); + type FeeManager = XcmFeesToAccount; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; @@ -191,11 +202,6 @@ parameter_types! { pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX); } -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - pub ReachableDest: Option = Some(Parachain(1000).into()); -} - /// Type to convert the `GeneralAdmin` origin to a Plurality `MultiLocation` value. pub type GeneralAdminToPlurality = OriginToPluralityVoice; diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs index 760fa33b693e..d32eb8d4a52f 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use super::*; -use crate::{account_and_location, new_executor, AssetTransactorOf, XcmCallOf}; +use crate::{account_and_location, new_executor, AssetTransactorOf, EnsureDelivery, XcmCallOf}; use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError, BenchmarkResult}; use frame_support::{ pallet_prelude::Get, @@ -24,8 +24,8 @@ use frame_support::{ }; use sp_runtime::traits::{Bounded, Zero}; use sp_std::{prelude::*, vec}; -use xcm::latest::prelude::*; -use xcm_executor::traits::{ConvertLocation, TransactAsset}; +use xcm::latest::{prelude::*, MAX_ITEMS_IN_MULTIASSETS}; +use xcm_executor::traits::{ConvertLocation, FeeReason, TransactAsset}; benchmarks_instance_pallet! { where_clause { where @@ -45,15 +45,7 @@ benchmarks_instance_pallet! { let worst_case_holding = T::worst_case_holding(0); let asset = T::get_multi_asset(); - >::deposit_asset( - &asset, - &sender_location, - &XcmContext { - origin: Some(sender_location.clone()), - message_id: [0; 32], - topic: None, - }, - ).unwrap(); + >::deposit_asset(&asset, &sender_location, None).unwrap(); // check the assets of origin. assert!(!T::TransactAsset::balance(&sender_account).is_zero()); @@ -78,15 +70,7 @@ benchmarks_instance_pallet! { let dest_location = T::valid_destination()?; let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap(); - >::deposit_asset( - &asset, - &sender_location, - &XcmContext { - origin: Some(sender_location.clone()), - message_id: [0; 32], - topic: None, - }, - ).unwrap(); + >::deposit_asset(&asset, &sender_location, None).unwrap(); assert!(T::TransactAsset::balance(&dest_account).is_zero()); let mut executor = new_executor::(sender_location); @@ -104,20 +88,27 @@ benchmarks_instance_pallet! { let dest_location = T::valid_destination()?; let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap(); - let asset = T::get_multi_asset(); - >::deposit_asset( - &asset, + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( &sender_location, - &XcmContext { - origin: Some(sender_location.clone()), - message_id: [0; 32], - topic: None, - }, - ).unwrap(); + &dest_location, + FeeReason::TransferReserveAsset + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + let asset = T::get_multi_asset(); + >::deposit_asset(&asset, &sender_location, None).unwrap(); + assert!(T::TransactAsset::balance(&sender_account) > sender_account_balance_before); let assets: MultiAssets = vec![ asset ].into(); assert!(T::TransactAsset::balance(&dest_account).is_zero()); let mut executor = new_executor::(sender_location); + 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::TransferReserveAsset { assets, dest: dest_location, @@ -127,7 +118,7 @@ benchmarks_instance_pallet! { }: { executor.bench_process(xcm)?; } verify { - assert!(T::TransactAsset::balance(&sender_account).is_zero()); + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); assert!(!T::TransactAsset::balance(&dest_account).is_zero()); // TODO: Check sender queue is not empty. #4426 } @@ -150,16 +141,33 @@ benchmarks_instance_pallet! { } initiate_reserve_withdraw { + let (sender_account, sender_location) = account_and_location::(1); let holding = T::worst_case_holding(1); - let assets_filter = MultiAssetFilter::Definite(holding.clone()); + let assets_filter = MultiAssetFilter::Definite(holding.clone().into_inner().into_iter().take(MAX_ITEMS_IN_MULTIASSETS).collect::>().into()); let reserve = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; - let mut executor = new_executor::(Default::default()); + + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &sender_location, + &reserve, + FeeReason::InitiateReserveWithdraw, + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + let mut executor = new_executor::(sender_location); 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::InitiateReserveWithdraw { assets: assets_filter, reserve, xcm: Xcm(vec![]) }; let xcm = Xcm(vec![instruction]); }: { executor.bench_process(xcm)?; } verify { + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // The execute completing successfully is as good as we can check. // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs index f5759afc0646..9adc706fc18a 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/fungible/mock.rs @@ -19,7 +19,7 @@ use crate::{fungible as xcm_balances_benchmark, mock::*}; use frame_benchmarking::BenchmarkError; use frame_support::{ - parameter_types, + derive_impl, parameter_types, traits::{ConstU32, Everything, Nothing}, weights::Weight, }; @@ -75,20 +75,10 @@ parameter_types! { pub const ExistentialDeposit: u64 = 7; } +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] impl pallet_balances::Config for Test { - type MaxLocks = (); - type MaxReserves = (); type ReserveIdentifier = [u8; 8]; - type Balance = u64; - type DustRemoval = (); - type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type WeightInfo = (); - type RuntimeHoldReason = RuntimeHoldReason; - type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; - type MaxFreezes = ConstU32<0>; } parameter_types! { @@ -157,6 +147,7 @@ impl xcm_executor::Config for XcmConfig { impl crate::Config for Test { type XcmConfig = XcmConfig; type AccountIdConverter = AccountIdConverter; + type DeliveryHelper = (); fn valid_destination() -> Result { let valid_destination: MultiLocation = X1(AccountId32 { network: None, id: [0u8; 32] }).into(); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index 4583ecdba89f..c6b76e0ffade 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -15,27 +15,45 @@ // along with Polkadot. If not, see . use super::*; -use crate::{new_executor, XcmCallOf}; +use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf}; use codec::Encode; use frame_benchmarking::{benchmarks, BenchmarkError}; -use frame_support::dispatch::GetDispatchInfo; +use frame_support::{dispatch::GetDispatchInfo, traits::fungible::Inspect}; use sp_std::vec; use xcm::{ latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight}, DoubleEncoded, }; -use xcm_executor::{ExecutorError, FeesMode}; +use xcm_executor::{ + traits::{ConvertLocation, FeeReason}, + ExecutorError, FeesMode, +}; benchmarks! { report_holding { + let (sender_account, sender_location) = account_and_location::(1); let holding = T::worst_case_holding(0); + let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; - let mut executor = new_executor::(Default::default()); + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &sender_location, + &destination, + FeeReason::Report, + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + let mut executor = new_executor::(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::>::ReportHolding { response_info: QueryResponseInfo { - destination: T::valid_destination()?, + destination, query_id: Default::default(), max_weight: Weight::MAX, }, @@ -44,11 +62,11 @@ benchmarks! { }; let xcm = Xcm(vec![instruction]); - } : { executor.bench_process(xcm)?; } verify { - // The completion of execution above is enough to validate this is completed. + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); } // This benchmark does not use any additional orders or instructions. This should be managed @@ -182,11 +200,26 @@ benchmarks! { } report_error { - let mut executor = new_executor::(Default::default()); - executor.set_error(Some((0u32, XcmError::Unimplemented))); + let (sender_account, sender_location) = account_and_location::(1); let query_id = Default::default(); - let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; let max_weight = Default::default(); + let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; + + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &sender_location, + &destination, + FeeReason::Report, + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + let mut executor = new_executor::(sender_location); + 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()); + } + executor.set_error(Some((0u32, XcmError::Unimplemented))); let instruction = Instruction::ReportError(QueryResponseInfo { query_id, destination, max_weight @@ -195,7 +228,8 @@ benchmarks! { }: { executor.bench_process(xcm)?; } verify { - // the execution succeeding is all we need to verify this xcm was successful + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); } claim_asset { @@ -360,10 +394,24 @@ benchmarks! { } query_pallet { + let (sender_account, sender_location) = account_and_location::(1); let query_id = Default::default(); let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; let max_weight = Default::default(); - let mut executor = new_executor::(Default::default()); + + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &sender_location, + &destination, + FeeReason::QueryPallet, + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + let mut executor = new_executor::(sender_location); + 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::QueryPallet { module_name: b"frame_system".to_vec(), @@ -373,6 +421,8 @@ benchmarks! { }: { executor.bench_process(xcm)?; } verify { + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -394,11 +444,25 @@ benchmarks! { } report_transact_status { + let (sender_account, sender_location) = account_and_location::(1); let query_id = Default::default(); let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?; let max_weight = Default::default(); - let mut executor = new_executor::(Default::default()); + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &sender_location, + &destination, + FeeReason::Report, + ); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + + let mut executor = new_executor::(sender_location); + 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()); + } executor.set_transact_status(b"MyError".to_vec().into()); let instruction = Instruction::ReportTransactStatus(QueryResponseInfo { @@ -410,6 +474,8 @@ benchmarks! { }: { executor.bench_process(xcm)?; } verify { + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -491,14 +557,30 @@ benchmarks! { let inner_xcm = Xcm(vec![ClearOrigin; x as usize]); // Get `origin`, `network` and `destination` from configured runtime. let (origin, network, destination) = T::export_message_origin_and_destination()?; + + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &origin, + &destination.into(), + FeeReason::Export(network), + ); + let sender_account = T::AccountIdConverter::convert_location(&origin).unwrap(); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + let mut executor = new_executor::(origin); + 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 xcm = Xcm(vec![ExportMessage { network, destination, xcm: inner_xcm, }]); }: { executor.bench_process(xcm)?; } verify { - // The execute completing successfully is as good as we can check. + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -517,14 +599,30 @@ benchmarks! { lock_asset { let (unlocker, owner, asset) = T::unlockable_asset()?; + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &owner, + &unlocker, + FeeReason::LockAsset, + ); + let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap(); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + let mut executor = new_executor::(owner); executor.set_holding(asset.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::LockAsset { asset, unlocker }; let xcm = Xcm(vec![instruction]); }: { executor.bench_process(xcm)?; } verify { + // Check delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -595,13 +693,29 @@ benchmarks! { .enact() .map_err(|_| BenchmarkError::Skip)?; + let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery( + &owner, + &locker, + FeeReason::RequestUnlock, + ); + let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap(); + let sender_account_balance_before = T::TransactAsset::balance(&sender_account); + // ... then request for an unlock with the RequestUnlock instruction. let mut executor = new_executor::(owner); + 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::RequestUnlock { asset, locker }; let xcm = Xcm(vec![instruction]); }: { executor.bench_process(xcm)?; } verify { + // Check we charged the delivery fees + assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before); // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 1b244f316de9..710ff0d80192 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -19,7 +19,7 @@ use crate::{generic, mock::*, *}; use codec::Decode; use frame_support::{ - match_types, parameter_types, + derive_impl, match_types, parameter_types, traits::{Everything, OriginTrait}, weights::Weight, }; @@ -40,6 +40,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, XcmGenericBenchmarks: generic::{Pallet}, } ); @@ -79,7 +80,11 @@ impl frame_system::Config for Test { /// The benchmarks in this pallet should never need an asset transactor to begin with. pub struct NoAssetTransactor; impl xcm_executor::traits::TransactAsset for NoAssetTransactor { - fn deposit_asset(_: &MultiAsset, _: &MultiLocation, _: &XcmContext) -> Result<(), XcmError> { + fn deposit_asset( + _: &MultiAsset, + _: &MultiLocation, + _: Option<&XcmContext>, + ) -> Result<(), XcmError> { unreachable!(); } @@ -133,9 +138,20 @@ impl xcm_executor::Config for XcmConfig { type Aliasers = Aliasers; } +parameter_types! { + pub const ExistentialDeposit: u64 = 7; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Test { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + impl crate::Config for Test { type XcmConfig = XcmConfig; type AccountIdConverter = AccountIdConverter; + type DeliveryHelper = (); fn valid_destination() -> Result { let valid_destination: MultiLocation = Junction::AccountId32 { network: None, id: [0u8; 32] }.into(); @@ -151,6 +167,7 @@ impl crate::Config for Test { } impl generic::Config for Test { + type TransactAsset = Balances; type RuntimeCall = RuntimeCall; fn worst_case_response() -> (u64, Response) { @@ -183,7 +200,7 @@ impl generic::Config for Test { fn unlockable_asset() -> Result<(MultiLocation, MultiLocation, MultiAsset), BenchmarkError> { let assets: MultiAsset = (Concrete(Here.into()), 100).into(); - Ok((Default::default(), Default::default(), assets)) + Ok((Default::default(), account_id_junction::(1).into(), assets)) } fn export_message_origin_and_destination( diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs index f207c238a39f..cbdfa8d0112c 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/generic/mod.rs @@ -38,6 +38,11 @@ pub mod pallet { + From> + Encode; + /// The type of `fungible` that is being used under the hood. + /// + /// This is useful for testing and checking. + type TransactAsset: frame_support::traits::fungible::Mutate; + /// The response which causes the most runtime weight. fn worst_case_response() -> (u64, Response); diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs index c6a963435953..3bf4aea1b25e 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -22,7 +22,10 @@ use codec::Encode; use frame_benchmarking::{account, BenchmarkError}; use sp_std::prelude::*; use xcm::latest::prelude::*; -use xcm_executor::{traits::ConvertLocation, Config as XcmConfig}; +use xcm_executor::{ + traits::{ConvertLocation, FeeReason}, + Config as XcmConfig, FeesMode, +}; pub mod fungible; pub mod generic; @@ -41,6 +44,9 @@ pub trait Config: frame_system::Config { /// A converter between a multi-location to a sovereign account. type AccountIdConverter: ConvertLocation; + /// Helper that ensures successful delivery for XCM instructions which need `SendXcm`. + type DeliveryHelper: EnsureDelivery; + /// Does any necessary setup to create a valid destination for XCM messages. /// Returns that destination's multi-location to be used in benchmarks. fn valid_destination() -> Result; @@ -108,3 +114,29 @@ pub fn account_and_location(index: u32) -> (T::AccountId, MultiLocati (account, location) } + +/// Trait for a type which ensures all requirements for successful delivery with XCM transport +/// layers. +pub trait EnsureDelivery { + /// Prepare all requirements for successful `XcmSender: SendXcm` passing (accounts, balances, + /// channels ...). Returns: + /// - possible `FeesMode` which is expected to be set to executor + /// - possible `MultiAssets` which are expected to be subsume to the Holding Register + fn ensure_successful_delivery( + origin_ref: &MultiLocation, + dest: &MultiLocation, + fee_reason: FeeReason, + ) -> (Option, Option); +} + +/// `()` implementation does nothing which means no special requirements for environment. +impl EnsureDelivery for () { + fn ensure_successful_delivery( + _origin_ref: &MultiLocation, + _dest: &MultiLocation, + _fee_reason: FeeReason, + ) -> (Option, Option) { + // doing nothing + (None, None) + } +} diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index b09bcb80ed67..a85fe0fa2af8 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -16,8 +16,8 @@ use codec::Encode; use frame_support::{ - construct_runtime, parameter_types, - traits::{ConstU32, Everything, Nothing}, + construct_runtime, match_types, parameter_types, + traits::{ConstU32, Everything, EverythingBut, Nothing}, weights::Weight, }; use frame_system::EnsureRoot; @@ -25,14 +25,16 @@ use polkadot_parachain_primitives::primitives::Id as ParaId; use polkadot_runtime_parachains::origin; use sp_core::H256; use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; -pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; +pub use sp_std::{ + cell::RefCell, collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, +}; use xcm::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible, FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, + SovereignSignedViaLocation, TakeWeightCredit, XcmFeesToAccount, }; use xcm_executor::XcmExecutor; @@ -154,7 +156,7 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> { r }) } -/// Sender that never returns error, always sends +/// Sender that never returns error. pub struct TestSendXcm; impl SendXcm for TestSendXcm { type Ticket = (MultiLocation, Xcm<()>); @@ -193,6 +195,38 @@ impl SendXcm for TestSendXcmErrX8 { } } +parameter_types! { + pub Para3000: u32 = 3000; + pub Para3000Location: MultiLocation = Parachain(Para3000::get()).into(); + pub Para3000PaymentAmount: u128 = 1; + pub Para3000PaymentMultiAssets: MultiAssets = MultiAssets::from(MultiAsset::from((Here, Para3000PaymentAmount::get()))); +} +/// Sender only sends to `Parachain(3000)` destination requiring payment. +pub struct TestPaidForPara3000SendXcm; +impl SendXcm for TestPaidForPara3000SendXcm { + type Ticket = (MultiLocation, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(MultiLocation, Xcm<()>)> { + if let Some(dest) = dest.as_ref() { + if !dest.eq(&Para3000Location::get()) { + return Err(SendError::NotApplicable) + } + } else { + return Err(SendError::NotApplicable) + } + + let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, Para3000PaymentMultiAssets::get())) + } + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + parameter_types! { pub const BlockHashCount: u64 = 250; } @@ -271,6 +305,14 @@ parameter_types! { pub TrustedAssets: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; + pub XcmFeesTargetAccount: AccountId = AccountId::new([167u8; 32]); +} + +pub const XCM_FEES_NOT_WAIVED_USER_ACCOUNT: [u8; 32] = [37u8; 32]; +match_types! { + pub type XcmFeesNotWaivedLocations: impl Contains = { + MultiLocation { parents: 0, interior: X1(Junction::AccountId32 {network: None, id: XCM_FEES_NOT_WAIVED_USER_ACCOUNT})} + }; } pub type Barrier = ( @@ -283,7 +325,7 @@ pub type Barrier = ( pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; - type XcmSender = TestSendXcm; + type XcmSender = (TestPaidForPara3000SendXcm, TestSendXcm); type AssetTransactor = LocalAssetTransactor; type OriginConverter = LocalOriginConverter; type IsReserve = (); @@ -300,7 +342,12 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = XcmPallet; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = (); + type FeeManager = XcmFeesToAccount< + Self, + EverythingBut, + AccountId, + XcmFeesTargetAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; @@ -322,7 +369,7 @@ parameter_types! { impl pallet_xcm::Config for Test { type RuntimeEvent = RuntimeEvent; type SendXcmOrigin = xcm_builder::EnsureXcmOrigin; - type XcmRouter = (TestSendXcmErrX8, TestSendXcm); + type XcmRouter = (TestSendXcmErrX8, TestPaidForPara3000SendXcm, TestSendXcm); type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; type XcmExecuteFilter = Everything; type XcmExecutor = XcmExecutor; diff --git a/polkadot/xcm/pallet-xcm/src/tests.rs b/polkadot/xcm/pallet-xcm/src/tests.rs index 5d8aee8d665f..d267eece2c04 100644 --- a/polkadot/xcm/pallet-xcm/src/tests.rs +++ b/polkadot/xcm/pallet-xcm/src/tests.rs @@ -522,6 +522,74 @@ fn reserve_transfer_assets_works() { }); } +/// Test `reserve_transfer_assets_with_paid_router_works` +/// +/// Asserts that the sender's balance is decreased and the beneficiary's balance +/// is increased. Verifies the correct message is sent and event is emitted. +/// Verifies that XCM router fees (`SendXcm::validate` -> `MultiAssets`) are withdrawn from correct +/// user account and deposited to a correct target account (`XcmFeesTargetAccount`). +#[test] +fn reserve_transfer_assets_with_paid_router_works() { + let user_account = AccountId::from(XCM_FEES_NOT_WAIVED_USER_ACCOUNT); + let paid_para_id = Para3000::get(); + let balances = vec![ + (user_account.clone(), INITIAL_BALANCE), + (ParaId::from(paid_para_id).into_account_truncating(), INITIAL_BALANCE), + (XcmFeesTargetAccount::get(), INITIAL_BALANCE), + ]; + new_test_ext_with_balances(balances).execute_with(|| { + let xcm_router_fee_amount = Para3000PaymentAmount::get(); + let weight = BaseXcmWeight::get() * 2; + let dest: MultiLocation = + Junction::AccountId32 { network: None, id: user_account.clone().into() }.into(); + assert_eq!(Balances::total_balance(&user_account), INITIAL_BALANCE); + assert_ok!(XcmPallet::reserve_transfer_assets( + RuntimeOrigin::signed(user_account.clone()), + Box::new(Parachain(paid_para_id).into()), + Box::new(dest.into()), + Box::new((Here, SEND_AMOUNT).into()), + 0, + )); + // check event + assert_eq!( + last_event(), + RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) }) + ); + + // XCM_FEES_NOT_WAIVED_USER_ACCOUNT spent amount + assert_eq!( + Balances::free_balance(user_account), + INITIAL_BALANCE - SEND_AMOUNT - xcm_router_fee_amount + ); + // Destination account (parachain account) has amount + let para_acc: AccountId = ParaId::from(paid_para_id).into_account_truncating(); + assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT); + // XcmFeesTargetAccount where should lend xcm_router_fee_amount + assert_eq!( + Balances::free_balance(XcmFeesTargetAccount::get()), + INITIAL_BALANCE + xcm_router_fee_amount + ); + assert_eq!( + sent_xcm(), + vec![( + Parachain(paid_para_id).into(), + Xcm(vec![ + ReserveAssetDeposited((Parent, SEND_AMOUNT).into()), + ClearOrigin, + buy_execution((Parent, SEND_AMOUNT)), + DepositAsset { assets: AllCounted(1).into(), beneficiary: dest }, + ]), + )] + ); + let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1); + let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap(); + assert_eq!( + last_event(), + RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) }) + ); + }); +} + /// Test `limited_reserve_transfer_assets` /// /// Asserts that the sender's balance is decreased and the beneficiary's balance diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index a428f1277a5f..a3490108e8b4 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -45,7 +45,7 @@ pub use junction::{BodyId, BodyPart, Junction, NetworkId}; pub use junctions::Junctions; pub use multiasset::{ AssetId, AssetInstance, Fungibility, MultiAsset, MultiAssetFilter, MultiAssets, - WildFungibility, WildMultiAsset, + WildFungibility, WildMultiAsset, MAX_ITEMS_IN_MULTIASSETS, }; pub use multilocation::{ Ancestor, AncestorThen, InteriorMultiLocation, MultiLocation, Parent, ParentThen, @@ -70,7 +70,7 @@ pub type QueryId = u64; #[scale_info(bounds(), skip_type_params(Call))] pub struct Xcm(pub Vec>); -const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; +pub const MAX_INSTRUCTIONS_TO_DECODE: u8 = 100; environmental::environmental!(instructions_count: u8); @@ -932,7 +932,7 @@ pub enum Instruction { /// should be sent on arrival. /// - `xcm`: The message to be exported. /// - /// As an example, to export a message for execution on Statemine (parachain #1000 in the + /// As an example, to export a message for execution on Asset Hub (parachain #1000 in the /// Kusama network), you would call with `network: NetworkId::Kusama` and /// `destination: X1(Parachain(1000))`. Alternatively, to export a message for execution on /// Polkadot, you would call with `network: NetworkId:: Polkadot` and `destination: Here`. diff --git a/polkadot/xcm/xcm-builder/src/currency_adapter.rs b/polkadot/xcm/xcm-builder/src/currency_adapter.rs index 23dc7958fe15..8ecf1dee72db 100644 --- a/polkadot/xcm/xcm-builder/src/currency_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/currency_adapter.rs @@ -191,7 +191,11 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> Result { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> Result { log::trace!(target: "xcm::currency_adapter", "deposit_asset what: {:?}, who: {:?}", what, who); // Check we handle this asset. let amount = Matcher::matches_fungible(&what).ok_or(Error::AssetNotHandled)?; diff --git a/polkadot/xcm/xcm-builder/src/fee_handling.rs b/polkadot/xcm/xcm-builder/src/fee_handling.rs new file mode 100644 index 000000000000..1386747c9778 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/fee_handling.rs @@ -0,0 +1,58 @@ +// Copyright 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 core::marker::PhantomData; +use frame_support::traits::{Contains, Get}; +use xcm::prelude::*; +use xcm_executor::traits::{FeeManager, FeeReason, TransactAsset}; + +/// A `FeeManager` implementation that simply deposits the fees handled into a specific on-chain +/// `ReceiverAccount`. +/// +/// It reuses the `AssetTransactor` configured on the XCM executor to deposit fee assets, and also +/// permits specifying `WaivedLocations` for locations that are privileged to not pay for fees. If +/// the `AssetTransactor` returns an error while calling `deposit_asset`, then a warning will be +/// logged. +pub struct XcmFeesToAccount( + PhantomData<(XcmConfig, WaivedLocations, AccountId, ReceiverAccount)>, +); +impl< + XcmConfig: xcm_executor::Config, + WaivedLocations: Contains, + AccountId: Clone + Into<[u8; 32]>, + ReceiverAccount: Get>, + > FeeManager for XcmFeesToAccount +{ + fn is_waived(origin: Option<&MultiLocation>, _: FeeReason) -> bool { + let Some(loc) = origin else { return false }; + WaivedLocations::contains(loc) + } + + fn handle_fee(fees: MultiAssets, context: Option<&XcmContext>) { + if let Some(receiver) = ReceiverAccount::get() { + let dest = AccountId32 { network: None, id: receiver.into() }.into(); + for asset in fees.into_inner() { + if let Err(e) = XcmConfig::AssetTransactor::deposit_asset(&asset, &dest, context) { + log::trace!( + target: "xcm::fees", + "`AssetTransactor::deposit_asset` returned error: {:?}, burning fees: {:?}", + e, asset, + ); + } + } + } + } +} diff --git a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs index 2179421b3ebc..b2802c908092 100644 --- a/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/fungibles_adapter.rs @@ -274,7 +274,11 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> XcmResult { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", "deposit_asset what: {:?}, who: {:?}", @@ -371,7 +375,11 @@ impl< >::check_out(dest, what, context) } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + context: Option<&XcmContext>, + ) -> XcmResult { FungiblesMutateAdapter::< Assets, Matcher, diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 21ada3244a30..34371398cdc3 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -67,6 +67,9 @@ pub use process_xcm_message::ProcessXcmMessage; mod currency_adapter; pub use currency_adapter::CurrencyAdapter; +mod fee_handling; +pub use fee_handling::XcmFeesToAccount; + mod fungibles_adapter; pub use fungibles_adapter::{ AssetChecking, DualMint, FungiblesAdapter, FungiblesMutateAdapter, FungiblesTransferAdapter, diff --git a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs index 4aebb496d497..357dc534a5f1 100644 --- a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs @@ -207,7 +207,11 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + context: Option<&XcmContext>, + ) -> XcmResult { log::trace!( target: LOG_TARGET, "deposit_asset what: {:?}, who: {:?}, context: {:?}", @@ -307,7 +311,11 @@ impl< >::check_out(dest, what, context) } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + context: Option<&XcmContext>, + ) -> XcmResult { NonFungiblesMutateAdapter::< Assets, Matcher, diff --git a/polkadot/xcm/xcm-builder/src/pay.rs b/polkadot/xcm/xcm-builder/src/pay.rs index 0f3a622f4ece..4c9b9a6088de 100644 --- a/polkadot/xcm/xcm-builder/src/pay.rs +++ b/polkadot/xcm/xcm-builder/src/pay.rs @@ -110,11 +110,14 @@ impl< let message = Xcm(vec![ DescendOrigin(Interior::get()), UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { - destination, - query_id, - max_weight: Weight::zero(), - })])), + SetAppendix(Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + ReportError(QueryResponseInfo { + destination, + query_id, + max_weight: Weight::zero(), + }), + ])), TransferAsset { beneficiary, assets: vec![MultiAsset { id: asset_id, fun: Fungibility::Fungible(amount) }] diff --git a/polkadot/xcm/xcm-builder/src/tests/mock.rs b/polkadot/xcm/xcm-builder/src/tests/mock.rs index e73b62f00731..7a7c8837fc1c 100644 --- a/polkadot/xcm/xcm-builder/src/tests/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/mock.rs @@ -253,7 +253,7 @@ impl TransactAsset for TestAssetTransactor { fn deposit_asset( what: &MultiAsset, who: &MultiLocation, - _context: &XcmContext, + _context: Option<&XcmContext>, ) -> Result<(), XcmError> { add_asset(*who, what.clone()); Ok(()) @@ -526,7 +526,7 @@ impl FeeManager for TestFeeManager { fn is_waived(_: Option<&MultiLocation>, r: FeeReason) -> bool { IS_WAIVED.with(|l| l.borrow().contains(&r)) } - fn handle_fee(_: MultiAssets) {} + fn handle_fee(_: MultiAssets, _: Option<&XcmContext>) {} } #[derive(Clone, Eq, PartialEq, Debug)] diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/pay.rs b/polkadot/xcm/xcm-builder/src/tests/pay/pay.rs index 491a2bcef7a0..178b93842736 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/pay.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/pay.rs @@ -71,11 +71,14 @@ fn pay_over_xcm_works() { let expected_message = Xcm(vec![ DescendOrigin(AccountId32 { id: SenderAccount::get().into(), network: None }.into()), UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { - destination: (Parent, Parachain(42)).into(), - query_id: 1, - max_weight: Weight::zero(), - })])), + SetAppendix(Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + ReportError(QueryResponseInfo { + destination: (Parent, Parachain(42)).into(), + query_id: 1, + max_weight: Weight::zero(), + }), + ])), TransferAsset { assets: (Here, amount).into(), beneficiary: AccountId32 { id: recipient.clone().into(), network: None }.into(), @@ -130,11 +133,14 @@ fn pay_over_xcm_governance_body() { let expected_message = Xcm(vec![ DescendOrigin(Plurality { id: BodyId::Treasury, part: BodyPart::Voice }.into()), UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { - destination: (Parent, Parachain(42)).into(), - query_id: 1, - max_weight: Weight::zero(), - })])), + SetAppendix(Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + ReportError(QueryResponseInfo { + destination: (Parent, Parachain(42)).into(), + query_id: 1, + max_weight: Weight::zero(), + }), + ])), TransferAsset { assets: (Parent, amount).into(), beneficiary: AccountId32 { id: recipient.clone().into(), network: None }.into(), diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/salary.rs b/polkadot/xcm/xcm-builder/src/tests/pay/salary.rs index 1d40345f302a..e490fe326b37 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/salary.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/salary.rs @@ -148,11 +148,14 @@ fn salary_pay_over_xcm_works() { let expected_message: Xcm = Xcm::(vec![ DescendOrigin(Plurality { id: BodyId::Treasury, part: BodyPart::Voice }.into()), UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - SetAppendix(Xcm(vec![ReportError(QueryResponseInfo { - destination: (Parent, Parachain(42)).into(), - query_id: 1, - max_weight: Weight::zero(), - })])), + SetAppendix(Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + ReportError(QueryResponseInfo { + destination: (Parent, Parachain(42)).into(), + query_id: 1, + max_weight: Weight::zero(), + }), + ])), TransferAsset { assets: (AssetHubAssetId::get(), FixedSalaryAmount::get()).into(), beneficiary: AccountId32 { id: recipient.clone().into(), network: None }.into(), diff --git a/polkadot/xcm/xcm-builder/tests/mock/mod.rs b/polkadot/xcm/xcm-builder/tests/mock/mod.rs index 363748940ca6..19ced5919517 100644 --- a/polkadot/xcm/xcm-builder/tests/mock/mod.rs +++ b/polkadot/xcm/xcm-builder/tests/mock/mod.rs @@ -171,13 +171,13 @@ pub type Barrier = ( ); parameter_types! { - pub KusamaForStatemine: (MultiAssetFilter, MultiLocation) = + pub KusamaForAssetHub: (MultiAssetFilter, MultiLocation) = (Wild(AllOf { id: Concrete(Here.into()), fun: WildFungible }), Parachain(1000).into()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 4; } -pub type TrustedTeleporters = (xcm_builder::Case,); +pub type TrustedTeleporters = (xcm_builder::Case,); pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { diff --git a/polkadot/xcm/xcm-builder/tests/scenarios.rs b/polkadot/xcm/xcm-builder/tests/scenarios.rs index 426efcaccdba..36780b9f0078 100644 --- a/polkadot/xcm/xcm-builder/tests/scenarios.rs +++ b/polkadot/xcm/xcm-builder/tests/scenarios.rs @@ -174,7 +174,7 @@ fn report_holding_works() { } /// Scenario: -/// A parachain wants to move KSM from Kusama to Statemine. +/// A parachain wants to move KSM from Kusama to Asset Hub. /// The parachain sends an XCM to withdraw funds combined with a teleport to the destination. /// /// This way of moving funds from a relay to a parachain will only work for trusted chains. @@ -182,12 +182,12 @@ fn report_holding_works() { /// /// Asserts that the balances are updated accordingly and the correct XCM is sent. #[test] -fn teleport_to_statemine_works() { +fn teleport_to_asset_hub_works() { use xcm::opaque::latest::prelude::*; let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating(); let balances = vec![(ALICE, INITIAL_BALANCE), (para_acc.clone(), INITIAL_BALANCE)]; kusama_like_with_balances(balances).execute_with(|| { - let statemine_id = 1000; + let asset_hub_id = 1000; let other_para_id = 3000; let amount = REGISTER_AMOUNT; let teleport_effects = vec![ @@ -222,13 +222,13 @@ fn teleport_to_statemine_works() { vec![(Parachain(other_para_id).into(), expected_msg, expected_hash,)] ); - // teleports are allowed from statemine to kusama. + // teleports are allowed from asset hub to kusama. let message = Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), InitiateTeleport { assets: All.into(), - dest: Parachain(statemine_id).into(), + dest: Parachain(asset_hub_id).into(), xcm: Xcm(teleport_effects.clone()), }, ]); @@ -246,7 +246,7 @@ fn teleport_to_statemine_works() { mock::sent_xcm(), vec![ (Parachain(other_para_id).into(), expected_msg.clone(), expected_hash,), - (Parachain(statemine_id).into(), expected_msg, expected_hash,) + (Parachain(asset_hub_id).into(), expected_msg, expected_hash,) ] ); }); diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index a48cd3259d67..e11ec2630e43 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -248,7 +248,7 @@ impl ExecuteXcm for XcmExecutor XcmExecutor { reason: FeeReason, ) -> Result { let (ticket, fee) = validate_send::(dest, msg)?; - if !Config::FeeManager::is_waived(self.origin_ref(), reason) { - let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; - Config::FeeManager::handle_fee(paid.into()); - } + self.take_fee(fee, reason)?; Config::XcmSender::deliver(ticket).map_err(Into::into) } @@ -618,14 +615,18 @@ impl XcmExecutor { DepositAsset { assets, beneficiary } => { let deposited = self.holding.saturating_take(assets); for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &beneficiary, &self.context)?; + Config::AssetTransactor::deposit_asset( + &asset, + &beneficiary, + Some(&self.context), + )?; } Ok(()) }, DepositReserveAsset { assets, dest, xcm } => { let deposited = self.holding.saturating_take(assets); for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest, &self.context)?; + Config::AssetTransactor::deposit_asset(&asset, &dest, Some(&self.context))?; } // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot // be reanchored because we have already called `deposit_asset` on all assets. @@ -944,6 +945,14 @@ impl XcmExecutor { if Config::FeeManager::is_waived(self.origin_ref(), reason) { return Ok(()) } + log::trace!( + target: "xcm::fees", + "taking fee: {:?} from origin_ref: {:?} in fees_mode: {:?} for a reason: {:?}", + fee, + self.origin_ref(), + self.fees_mode, + reason, + ); let paid = if self.fees_mode.jit_withdraw { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; for asset in fee.inner() { @@ -953,7 +962,7 @@ impl XcmExecutor { } else { self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into() }; - Config::FeeManager::handle_fee(paid); + Config::FeeManager::handle_fee(paid, Some(&self.context)); Ok(()) } @@ -985,12 +994,7 @@ impl XcmExecutor { let QueryResponseInfo { destination, query_id, max_weight } = info; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); - let (ticket, fee) = validate_send::(destination, message)?; - if !Config::FeeManager::is_waived(self.origin_ref(), fee_reason) { - let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; - Config::FeeManager::handle_fee(paid.into()); - } - Config::XcmSender::deliver(ticket).map_err(Into::into) + self.send(destination, message, fee_reason) } fn try_reanchor( diff --git a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs index ad2e108e7dee..2b2f21927f2e 100644 --- a/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs +++ b/polkadot/xcm/xcm-executor/src/traits/fee_manager.rs @@ -23,11 +23,11 @@ pub trait FeeManager { /// Do something with the fee which has been paid. Doing nothing here silently burns the /// fees. - fn handle_fee(fee: MultiAssets); + fn handle_fee(fee: MultiAssets, context: Option<&XcmContext>); } /// Context under which a fee is paid. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum FeeReason { /// When a reporting instruction is called. Report, @@ -53,7 +53,7 @@ pub enum FeeReason { impl FeeManager for () { fn is_waived(_: Option<&MultiLocation>, _: FeeReason) -> bool { - true + false } - fn handle_fee(_: MultiAssets) {} + fn handle_fee(_: MultiAssets, _: Option<&XcmContext>) {} } diff --git a/polkadot/xcm/xcm-executor/src/traits/transact_asset.rs b/polkadot/xcm/xcm-executor/src/traits/transact_asset.rs index 34cdb0c71413..c51befff88a6 100644 --- a/polkadot/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/polkadot/xcm/xcm-executor/src/traits/transact_asset.rs @@ -87,7 +87,11 @@ pub trait TransactAsset { /// Deposit the `what` asset into the account of `who`. /// /// Implementations should return `XcmError::FailedToTransactAsset` if deposit failed. - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult { + fn deposit_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> XcmResult { Err(XcmError::Unimplemented) } @@ -139,7 +143,7 @@ pub trait TransactAsset { Err(XcmError::AssetNotFound | XcmError::Unimplemented) => { let assets = Self::withdraw_asset(asset, from, Some(context))?; // Not a very forgiving attitude; once we implement roll-backs then it'll be nicer. - Self::deposit_asset(asset, to, context)?; + Self::deposit_asset(asset, to, Some(context))?; Ok(assets) }, result => result, @@ -195,7 +199,11 @@ impl TransactAsset for Tuple { )* ); } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + context: Option<&XcmContext>, + ) -> XcmResult { for_tuples!( #( match Tuple::deposit_asset(what, who, context) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), @@ -286,7 +294,7 @@ mod tests { fn deposit_asset( _what: &MultiAsset, _who: &MultiLocation, - _context: &XcmContext, + _context: Option<&XcmContext>, ) -> XcmResult { Err(XcmError::AssetNotFound) } @@ -330,7 +338,7 @@ mod tests { fn deposit_asset( _what: &MultiAsset, _who: &MultiLocation, - _context: &XcmContext, + _context: Option<&XcmContext>, ) -> XcmResult { Err(XcmError::Overflow) } @@ -374,7 +382,7 @@ mod tests { fn deposit_asset( _what: &MultiAsset, _who: &MultiLocation, - _context: &XcmContext, + _context: Option<&XcmContext>, ) -> XcmResult { Ok(()) } @@ -406,7 +414,7 @@ mod tests { MultiTransactor::deposit_asset( &(Here, 1u128).into(), &Here.into(), - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ), Err(XcmError::AssetNotFound) ); @@ -420,7 +428,7 @@ mod tests { MultiTransactor::deposit_asset( &(Here, 1u128).into(), &Here.into(), - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ), Ok(()) ); @@ -434,7 +442,7 @@ mod tests { MultiTransactor::deposit_asset( &(Here, 1u128).into(), &Here.into(), - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ), Err(XcmError::Overflow) ); @@ -448,7 +456,7 @@ mod tests { MultiTransactor::deposit_asset( &(Here, 1u128).into(), &Here.into(), - &XcmContext::with_message_id([0; 32]), + Some(&XcmContext::with_message_id([0; 32])), ), Ok(()), ); diff --git a/prdoc/pr_1234.prdoc b/prdoc/pr_1234.prdoc new file mode 100644 index 000000000000..cc22a02d88b9 --- /dev/null +++ b/prdoc/pr_1234.prdoc @@ -0,0 +1,20 @@ +# Schema: Parity PR Documentation Schema (prdoc) +# See doc at https://github.com/paritytech/prdoc + +title: Introduce XcmFeesToAccount fee manager + +doc: + - audience: Builder + description: | + Now all XCM sending, unless done by the system for the system, will be charged delivery fees. + All runtimes are now configured to send these delivery fees to a treasury account. + The fee formula is `delivery_fee_factor * (base_fee + encoded_msg_len * per_byte_fee)`. + +migrations: + db: [] + + runtime: [] + +crates: [] + +host_functions: [] diff --git a/substrate/frame/support/procedural/src/pallet/expand/warnings.rs b/substrate/frame/support/procedural/src/pallet/expand/warnings.rs index 110c4fa51987..6ce2097c2684 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/warnings.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/warnings.rs @@ -33,8 +33,9 @@ pub(crate) fn weight_witness_warning( if dev_mode { return } - - let CallWeightDef::Immediate(w) = &method.weight else { return }; + let CallWeightDef::Immediate(w) = &method.weight else { + return + }; let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") .old("not check weight witness data") @@ -65,8 +66,9 @@ pub(crate) fn weight_constant_warning( if dev_mode { return } - - let syn::Expr::Lit(lit) = weight else { return }; + let syn::Expr::Lit(lit) = weight else { + return + }; let warning = Warning::new_deprecated("ConstantWeight") .index(warnings.len())