diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index 03120ccf704a..3654c44010e9 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -21,8 +21,9 @@ use parity_scale_codec::Encode; use primitives::Id as ParaId; use runtime_parachains::{ configuration::{self, HostConfiguration}, - dmp, + dmp, FeeTracker, }; +use sp_runtime::FixedPointNumber; use sp_std::{marker::PhantomData, prelude::*}; use xcm::prelude::*; use SendError::*; @@ -47,6 +48,24 @@ impl> PriceForParachainDelivery for ConstantPrice { } } +/// 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. +/// +/// 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)`. +pub struct ExponentialPrice(sp_std::marker::PhantomData<(A, B, M, F)>); +impl, B: Get, M: Get, F: FeeTracker> PriceForParachainDelivery + for ExponentialPrice +{ + fn price_for_parachain_delivery(para: ParaId, 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); + (A::get(), amount).into() + } +} + /// XCM sender for relay chain. It only sends downward message. pub struct ChildParachainRouter(PhantomData<(T, W, P)>); @@ -88,3 +107,61 @@ impl FixedU128 { + FixedU128::from_rational(101, 100) + } + } + + type TestExponentialPrice = + ExponentialPrice; + + #[test] + fn exponential_price_correct_price_calculation() { + let id: ParaId = 123.into(); + let b: u128 = BaseDeliveryFee::get(); + let m: u128 = TransactionByteFee::get(); + + // F * (B + msg_length * M) + // message_length = 1 + let result: u128 = TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + m); + assert_eq!( + TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![])), + (FeeAssetId::get(), result).into() + ); + + // message size = 2 + let result: u128 = + TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (2 * m)); + assert_eq!( + TestExponentialPrice::price_for_parachain_delivery(id.clone(), &Xcm(vec![ClearOrigin])), + (FeeAssetId::get(), result).into() + ); + + // message size = 4 + let result: u128 = + TestFeeTracker::get_fee_factor(id.clone()).saturating_mul_int(b + (4 * m)); + assert_eq!( + TestExponentialPrice::price_for_parachain_delivery( + id.clone(), + &Xcm(vec![SetAppendix(Xcm(vec![ClearOrigin]))]) + ), + (FeeAssetId::get(), result).into() + ); + } +} diff --git a/runtime/kusama/Cargo.toml b/runtime/kusama/Cargo.toml index 668cf04dae4d..4e082eb5acd7 100644 --- a/runtime/kusama/Cargo.toml +++ b/runtime/kusama/Cargo.toml @@ -24,7 +24,7 @@ sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", d inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-arithmetic = { package = "sp-arithmetic", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -128,6 +128,7 @@ std = [ "parity-scale-codec/std", "scale-info/std", "inherents/std", + "sp-arithmetic/std", "sp-core/std", "sp-api/std", "tx-pool-api/std", diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index b62f72cc4224..79a5ce75f7ae 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -1410,7 +1410,7 @@ construct_runtime! { ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, - Dmp: parachains_dmp::{Pallet, Call, Storage} = 58, + Dmp: parachains_dmp::{Pallet, Storage} = 58, Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, diff --git a/runtime/kusama/src/xcm_config.rs b/runtime/kusama/src/xcm_config.rs index 289ea118d7bc..b3da8f4d2a9a 100644 --- a/runtime/kusama/src/xcm_config.rs +++ b/runtime/kusama/src/xcm_config.rs @@ -17,8 +17,9 @@ //! XCM configurations for the Kusama runtime. use super::{ - parachains_origin, AccountId, AllPalletsWithSystem, Balances, Fellows, ParaId, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, WeightToFee, XcmPallet, + parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, Fellows, ParaId, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, TransactionByteFee, WeightToFee, + XcmPallet, }; use frame_support::{ match_types, parameter_types, @@ -26,7 +27,12 @@ use frame_support::{ weights::Weight, }; use frame_system::EnsureRoot; -use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor}; +use kusama_runtime_constants::currency::CENTS; +use runtime_common::{ + crowdloan, paras_registrar, + xcm_sender::{ChildParachainRouter, ExponentialPrice}, + ToAuthor, +}; use sp_core::ConstU32; use xcm::latest::prelude::*; use xcm_builder::{ @@ -101,13 +107,21 @@ parameter_types! { /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; + /// 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. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } /// 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 = ( // Only one router so far - use DMP to communicate with child parachains. - xcm_sender::ChildParachainRouter, + ChildParachainRouter< + Runtime, + XcmPallet, + ExponentialPrice, + >, ); parameter_types! { diff --git a/runtime/parachains/src/dmp.rs b/runtime/parachains/src/dmp.rs index e5ccac2647fe..5244406fcce4 100644 --- a/runtime/parachains/src/dmp.rs +++ b/runtime/parachains/src/dmp.rs @@ -14,13 +14,45 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! 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: +//! +//! ```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 +//! threshold = max_messages / THRESHOLD_FACTOR +//! ``` +//! Based on the THRESHOLD_FACTOR, the threshold is set as a fraction of the +//! total messages. The `DeliveryFeeFactor` increases for a message over the +//! threshold by: +//! +//! `DeliveryFeeFactor = DeliveryFeeFactor * +//! (EXPONENTIAL_FEE_BASE + MESSAGE_SIZE_FEE_BASE * encoded_message_size_in_KB)` +//! +//! And decreases when the number of messages in the `DownwardMessageQueue` fall +//! below the threshold by: +//! +//! `DeliveryFeeFactor = DeliveryFeeFactor / EXPONENTIAL_FEE_BASE` +//! +//! As an extra defensive measure, a `max_messages` hard +//! limit is set to the number of messages in the DownwardMessageQueue. Messages +//! that would increase the number of messages in the queue above this hard +//! limit are dropped. + use crate::{ configuration::{self, HostConfiguration}, - initializer, + initializer, FeeTracker, }; use frame_support::pallet_prelude::*; use primitives::{DownwardMessage, Hash, Id as ParaId, InboundDownwardMessage}; -use sp_runtime::traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}; +use sp_core::MAX_POSSIBLE_ALLOCATION; +use sp_runtime::{ + traits::{BlakeTwo256, Hash as HashT, SaturatedConversion}, + FixedU128, Saturating, +}; use sp_std::{fmt, prelude::*}; use xcm::latest::SendError; @@ -29,7 +61,9 @@ pub use pallet::*; #[cfg(test)] mod tests; -pub const MAX_MESSAGE_QUEUE_SIZE: usize = 1024; +const THRESHOLD_FACTOR: u32 = 2; +const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 +const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 /// An error sending a downward message. #[cfg_attr(test, derive(Debug))] @@ -102,10 +136,17 @@ pub mod pallet { pub(crate) type DownwardMessageQueueHeads = StorageMap<_, Twox64Concat, ParaId, Hash, ValueQuery>; - #[pallet::call] - impl Pallet {} -} + /// Initialization value for the DeliveryFee factor. + #[pallet::type_value] + pub fn InitialFactor() -> FixedU128 { + FixedU128::from_u32(1) + } + /// The number to multiply the base delivery fee by. + #[pallet::storage] + pub(crate) type DeliveryFeeFactor = + StorageMap<_, Twox64Concat, ParaId, FixedU128, ValueQuery, InitialFactor>; +} /// Routines and getters related to downward message passing. impl Pallet { /// Block initialization logic, called by initializer. @@ -151,7 +192,8 @@ impl Pallet { return Err(QueueDownwardMessageError::ExceedsMaxMessageSize) } - if DownwardMessageQueues::::decode_len(para).unwrap_or(0) > MAX_MESSAGE_QUEUE_SIZE { + // Hard limit on Queue size + if Self::dmq_length(*para) > Self::dmq_max_length(config.max_downward_message_size) { return Err(QueueDownwardMessageError::ExceedsMaxMessageSize) } @@ -176,7 +218,8 @@ impl Pallet { return Err(QueueDownwardMessageError::ExceedsMaxMessageSize) } - if DownwardMessageQueues::::decode_len(para).unwrap_or(0) > MAX_MESSAGE_QUEUE_SIZE { + // Hard limit on Queue size + if Self::dmq_length(para) > Self::dmq_max_length(config.max_downward_message_size) { return Err(QueueDownwardMessageError::ExceedsMaxMessageSize) } @@ -190,10 +233,20 @@ impl Pallet { *head = new_head; }); - DownwardMessageQueues::::mutate(para, |v| { + let q_len = DownwardMessageQueues::::mutate(para, |v| { v.push(inbound); + v.len() }); + 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); + } + Ok(()) } @@ -219,7 +272,7 @@ impl Pallet { /// Prunes the specified number of messages from the downward message queue of the given para. pub(crate) fn prune_dmq(para: ParaId, processed_downward_messages: u32) -> Weight { - DownwardMessageQueues::::mutate(para, |q| { + let q_len = DownwardMessageQueues::::mutate(para, |q| { let processed_downward_messages = processed_downward_messages as usize; if processed_downward_messages > q.len() { // reaching this branch is unexpected due to the constraint established by @@ -228,7 +281,15 @@ impl Pallet { } else { *q = q.split_off(processed_downward_messages); } + q.len() }); + + let config = configuration::ActiveConfig::::get(); + 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); + } T::DbWeight::get().reads_writes(1, 1) } @@ -248,10 +309,42 @@ impl Pallet { .saturated_into::() } + fn dmq_max_length(max_downward_message_size: u32) -> u32 { + MAX_POSSIBLE_ALLOCATION.checked_div(max_downward_message_size).unwrap_or(0) + } + /// Returns the downward message queue contents for the given para. /// /// The most recent messages are the latest in the vector. pub(crate) fn dmq_contents(recipient: ParaId) -> 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); + *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| { + *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/runtime/parachains/src/dmp/tests.rs b/runtime/parachains/src/dmp/tests.rs index 6e05f6c735ed..234f5f7e43c7 100644 --- a/runtime/parachains/src/dmp/tests.rs +++ b/runtime/parachains/src/dmp/tests.rs @@ -15,7 +15,11 @@ // along with Polkadot. If not, see . use super::*; -use crate::mock::{new_test_ext, Configuration, Dmp, MockGenesisConfig, Paras, System, Test}; +use crate::{ + configuration::ActiveConfig, + mock::{new_test_ext, Configuration, Dmp, MockGenesisConfig, Paras, System, Test}, +}; +use frame_support::assert_ok; use hex_literal::hex; use parity_scale_codec::Encode; use primitives::BlockNumber; @@ -205,3 +209,69 @@ fn verify_dmq_mqc_head_is_externally_accessible() { ); }); } + +#[test] +fn verify_fee_increment_and_decrement() { + let a = ParaId::from(123); + let mut genesis = default_genesis_config(); + genesis.configuration.config.max_downward_message_size = 16777216; + new_test_ext(genesis).execute_with(|| { + let initial = InitialFactor::get(); + assert_eq!(DeliveryFeeFactor::::get(a), initial); + + // Under fee limit + queue_downward_message(a, vec![1]).unwrap(); + assert_eq!(DeliveryFeeFactor::::get(a), initial); + + // Limit reached so fee is increased + queue_downward_message(a, vec![1]).unwrap(); + let result = InitialFactor::get().saturating_mul(EXPONENTIAL_FEE_BASE); + assert_eq!(DeliveryFeeFactor::::get(a), result); + + Dmp::prune_dmq(a, 1); + assert_eq!(DeliveryFeeFactor::::get(a), initial); + + // 10 Kb message adds additional 0.001 per KB fee factor + let big_message = [0; 10240].to_vec(); + let msg_len_in_kb = big_message.len().saturating_div(1024) as u32; + let result = initial.saturating_mul( + EXPONENTIAL_FEE_BASE + + MESSAGE_SIZE_FEE_BASE.saturating_mul(FixedU128::from_u32(msg_len_in_kb)), + ); + queue_downward_message(a, big_message).unwrap(); + assert_eq!(DeliveryFeeFactor::::get(a), result); + + queue_downward_message(a, vec![1]).unwrap(); + let result = result.saturating_mul(EXPONENTIAL_FEE_BASE); + assert_eq!(DeliveryFeeFactor::::get(a), result); + + Dmp::prune_dmq(a, 3); + let result = result / EXPONENTIAL_FEE_BASE; + assert_eq!(DeliveryFeeFactor::::get(a), result); + assert_eq!(Dmp::dmq_length(a), 0); + + // Messages under limit will keep decreasing fee factor until base fee factor is reached + queue_downward_message(a, vec![1]).unwrap(); + Dmp::prune_dmq(a, 1); + queue_downward_message(a, vec![1]).unwrap(); + Dmp::prune_dmq(a, 1); + assert_eq!(DeliveryFeeFactor::::get(a), initial); + }); +} + +#[test] +fn verify_fee_factor_reaches_high_value() { + let a = ParaId::from(123); + let mut genesis = default_genesis_config(); + genesis.configuration.config.max_downward_message_size = 51200; + new_test_ext(genesis).execute_with(|| { + let max_messages = + Dmp::dmq_max_length(ActiveConfig::::get().max_downward_message_size); + let mut total_fee_factor = FixedU128::from_float(1.0); + for _ in 1..max_messages { + assert_ok!(queue_downward_message(a, vec![])); + total_fee_factor = total_fee_factor + (DeliveryFeeFactor::::get(a)); + } + assert!(total_fee_factor > FixedU128::from_u32(100_000_000)); + }); +} diff --git a/runtime/parachains/src/lib.rs b/runtime/parachains/src/lib.rs index ad884802ff19..9b2a49d34986 100644 --- a/runtime/parachains/src/lib.rs +++ b/runtime/parachains/src/lib.rs @@ -51,7 +51,12 @@ mod mock; pub use origin::{ensure_parachain, Origin}; pub use paras::ParaLifecycle; use primitives::{HeadData, Id as ParaId, ValidationCode}; -use sp_runtime::DispatchResult; +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; +} /// Schedule a para to be initialized at the start of the next session with the given genesis data. /// diff --git a/runtime/polkadot/Cargo.toml b/runtime/polkadot/Cargo.toml index 6163ef12e390..56f835ea5a47 100644 --- a/runtime/polkadot/Cargo.toml +++ b/runtime/polkadot/Cargo.toml @@ -23,11 +23,11 @@ block-builder-api = { package = "sp-block-builder", git = "https://github.com/pa inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } tx-pool-api = { package = "sp-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -124,6 +124,7 @@ std = [ "parity-scale-codec/std", "scale-info/std", "inherents/std", + "sp-arithmetic/std", "sp-core/std", "sp-api/std", "tx-pool-api/std", diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index fe5cafd0ee15..ec68400dca86 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -1367,7 +1367,7 @@ construct_runtime! { ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, - Dmp: parachains_dmp::{Pallet, Call, Storage} = 58, + Dmp: parachains_dmp::{Pallet, Storage} = 58, Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, diff --git a/runtime/polkadot/src/xcm_config.rs b/runtime/polkadot/src/xcm_config.rs index 5aa841564bf5..573798a873d2 100644 --- a/runtime/polkadot/src/xcm_config.rs +++ b/runtime/polkadot/src/xcm_config.rs @@ -17,9 +17,9 @@ //! XCM configuration for Polkadot. use super::{ - parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, + parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, Dmp, FellowshipAdmin, ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin, - WeightToFee, XcmPallet, + TransactionByteFee, WeightToFee, XcmPallet, }; use frame_support::{ match_types, parameter_types, @@ -28,8 +28,14 @@ use frame_support::{ }; use frame_system::EnsureRoot; use pallet_xcm::XcmPassthrough; -use polkadot_runtime_constants::{system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX}; -use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor}; +use polkadot_runtime_constants::{ + currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX, +}; +use runtime_common::{ + crowdloan, paras_registrar, + xcm_sender::{ChildParachainRouter, ExponentialPrice}, + ToAuthor, +}; use sp_core::ConstU32; use xcm::latest::prelude::*; use xcm_builder::{ @@ -106,13 +112,21 @@ parameter_types! { /// Maximum number of instructions in a single XCM fragment. A sanity check against weight /// calculations getting too crazy. pub const MaxInstructions: u32 = 100; + /// 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. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } /// 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 = ( // Only one router so far - use DMP to communicate with child parachains. - xcm_sender::ChildParachainRouter, + ChildParachainRouter< + Runtime, + XcmPallet, + ExponentialPrice, + >, ); parameter_types! { diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 522cee6bec8e..ec5f62fdb712 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -1409,7 +1409,7 @@ construct_runtime! { ParaScheduler: parachains_scheduler::{Pallet, Storage} = 55, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 56, Initializer: parachains_initializer::{Pallet, Call, Storage} = 57, - Dmp: parachains_dmp::{Pallet, Call, Storage} = 58, + Dmp: parachains_dmp::{Pallet, Storage} = 58, Ump: parachains_ump::{Pallet, Call, Storage, Event} = 59, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 60, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 61, diff --git a/runtime/rococo/src/xcm_config.rs b/runtime/rococo/src/xcm_config.rs index 872bbbe1a035..de3bdfde4c0b 100644 --- a/runtime/rococo/src/xcm_config.rs +++ b/runtime/rococo/src/xcm_config.rs @@ -17,8 +17,8 @@ //! XCM configuration for Rococo. use super::{ - parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, ParaId, - Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet, + parachains_origin, AccountId, AllPalletsWithSystem, Balances, CouncilCollective, Dmp, ParaId, + Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmPallet, }; use frame_support::{ match_types, parameter_types, @@ -26,7 +26,12 @@ use frame_support::{ weights::Weight, }; use frame_system::EnsureRoot; -use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor}; +use rococo_runtime_constants::currency::CENTS; +use runtime_common::{ + crowdloan, paras_registrar, + xcm_sender::{ChildParachainRouter, ExponentialPrice}, + ToAuthor, +}; use sp_core::ConstU32; use xcm::latest::prelude::*; use xcm_builder::{ @@ -82,12 +87,21 @@ type LocalOriginConverter = ( parameter_types! { /// The amount of weight an XCM operation takes. This is a safe overestimate. pub const BaseXcmWeight: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); + /// 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. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } + /// 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 = ( // Only one router so far - use DMP to communicate with child parachains. - xcm_sender::ChildParachainRouter, + ChildParachainRouter< + Runtime, + XcmPallet, + ExponentialPrice, + >, ); parameter_types! { diff --git a/runtime/test-runtime/src/lib.rs b/runtime/test-runtime/src/lib.rs index 616f1c955dc9..fb114df2ee46 100644 --- a/runtime/test-runtime/src/lib.rs +++ b/runtime/test-runtime/src/lib.rs @@ -671,7 +671,7 @@ construct_runtime! { ParaSessionInfo: parachains_session_info::{Pallet, Storage}, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event}, Ump: parachains_ump::{Pallet, Call, Storage, Event}, - Dmp: parachains_dmp::{Pallet, Call, Storage}, + Dmp: parachains_dmp::{Pallet, Storage}, Xcm: pallet_xcm::{Pallet, Call, Event, Origin}, ParasDisputes: parachains_disputes::{Pallet, Storage, Event}, diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs index 9374d5a8c8ef..e3d917a43a43 100644 --- a/runtime/westend/src/lib.rs +++ b/runtime/westend/src/lib.rs @@ -1160,7 +1160,7 @@ construct_runtime! { ParaScheduler: parachains_scheduler::{Pallet, Storage} = 46, Paras: parachains_paras::{Pallet, Call, Storage, Event, Config, ValidateUnsigned} = 47, Initializer: parachains_initializer::{Pallet, Call, Storage} = 48, - Dmp: parachains_dmp::{Pallet, Call, Storage} = 49, + Dmp: parachains_dmp::{Pallet, Storage} = 49, Ump: parachains_ump::{Pallet, Call, Storage, Event} = 50, Hrmp: parachains_hrmp::{Pallet, Call, Storage, Event, Config} = 51, ParaSessionInfo: parachains_session_info::{Pallet, Storage} = 52, diff --git a/runtime/westend/src/xcm_config.rs b/runtime/westend/src/xcm_config.rs index 24b161e42a01..148405c7e4e7 100644 --- a/runtime/westend/src/xcm_config.rs +++ b/runtime/westend/src/xcm_config.rs @@ -17,16 +17,21 @@ //! XCM configurations for Westend. use super::{ - parachains_origin, weights, AccountId, AllPalletsWithSystem, Balances, ParaId, Runtime, - RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, XcmPallet, + parachains_origin, weights, AccountId, AllPalletsWithSystem, Balances, Dmp, ParaId, Runtime, + RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, WeightToFee, XcmPallet, }; use frame_support::{ parameter_types, traits::{Contains, Everything, Nothing}, }; use frame_system::EnsureRoot; -use runtime_common::{crowdloan, paras_registrar, xcm_sender, ToAuthor}; +use runtime_common::{ + crowdloan, paras_registrar, + xcm_sender::{ChildParachainRouter, ExponentialPrice}, + ToAuthor, +}; use sp_core::ConstU32; +use westend_runtime_constants::currency::CENTS; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -44,6 +49,10 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = ThisNetwork::get().into(); pub CheckAccount: AccountId = XcmPallet::check_account(); pub LocalCheckAccount: (AccountId, MintLocation) = (CheckAccount::get(), MintLocation::Local); + /// 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. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); } pub type LocationConverter = @@ -73,7 +82,11 @@ type LocalOriginConverter = ( /// individual routers. pub type XcmRouter = ( // Only one router so far - use DMP to communicate with child parachains. - xcm_sender::ChildParachainRouter, + ChildParachainRouter< + Runtime, + XcmPallet, + ExponentialPrice, + >, ); parameter_types! { diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index 5af903ca37b3..cd6fb6321a8e 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -1146,7 +1146,10 @@ impl Pallet { BuyExecution { fees, weight_limit }, DepositAsset { assets: Wild(AllCounted(max_assets)), beneficiary }, ]); - let mut message = Xcm(vec![TransferReserveAsset { assets, dest, xcm }]); + let mut message = Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + TransferReserveAsset { assets, dest, xcm }, + ]); let weight = T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; let hash = message.using_encoded(sp_io::hashing::blake2_256); @@ -1205,6 +1208,7 @@ impl Pallet { ]); let mut message = Xcm(vec![ WithdrawAsset(assets), + SetFeesMode { jit_withdraw: true }, InitiateTeleport { assets: Wild(AllCounted(max_assets)), dest, xcm }, ]); let weight = diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 919eede0fe4a..ae359116e023 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -349,7 +349,7 @@ fn teleport_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get() * 2; + let weight = BaseXcmWeight::get() * 3; assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::teleport_assets( @@ -392,7 +392,7 @@ fn limited_teleport_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get() * 2; + let weight = BaseXcmWeight::get() * 3; assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::limited_teleport_assets( @@ -436,7 +436,7 @@ fn unlimited_teleport_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get() * 2; + let weight = BaseXcmWeight::get() * 3; assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); let dest: MultiLocation = AccountId32 { network: None, id: BOB.into() }.into(); assert_ok!(XcmPallet::limited_teleport_assets( @@ -478,7 +478,7 @@ fn reserve_transfer_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get(); + let weight = BaseXcmWeight::get() * 2; let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::reserve_transfer_assets( @@ -525,7 +525,7 @@ fn limited_reserve_transfer_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get(); + let weight = BaseXcmWeight::get() * 2; let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( @@ -573,7 +573,7 @@ fn unlimited_reserve_transfer_assets_works() { (ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE), ]; new_test_ext_with_balances(balances).execute_with(|| { - let weight = BaseXcmWeight::get(); + let weight = BaseXcmWeight::get() * 2; let dest: MultiLocation = Junction::AccountId32 { network: None, id: ALICE.into() }.into(); assert_eq!(Balances::total_balance(&ALICE), INITIAL_BALANCE); assert_ok!(XcmPallet::limited_reserve_transfer_assets( diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 39a7ae32ee1e..ff94c1392fea 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -925,7 +925,7 @@ pub enum Instruction { /// asset to be transferred. /// /// - `asset`: The asset to be unlocked. - /// - `owner`: The owner of the asset on the local chain. + /// - `target`: The owner of the asset on the local chain. /// /// Safety: No concerns. ///