diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index 97c6cfe368d..ad5313ee9ac 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -53,6 +53,7 @@ use sp_runtime::{ InvalidTransaction, TransactionLongevity, TransactionSource, TransactionValidity, ValidTransaction, }, + FixedU128, Saturating, }; use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; use xcm::latest::XcmHash; @@ -91,6 +92,8 @@ pub use relay_state_snapshot::{MessagingStateSnapshot, RelayChainStateProof}; pub use pallet::*; +pub const MULTIPLICATIVE_FEE_FACTOR_UMP: FixedU128 = FixedU128::from_rational(101, 100); // 1.01 + /// Something that can check the associated relay block number. /// /// Each Parachain block is built in the context of a relay chain block, this trait allows us @@ -261,6 +264,9 @@ pub mod pallet { UpwardMessages::::put(&up[..num]); *up = up.split_off(num); + if num > 0 { + Self::decrement_ump_fee_factor(); + } }); // Sending HRMP messages is a little bit more involved. There are the following @@ -691,6 +697,16 @@ pub mod pallet { #[pallet::storage] pub(super) type CustomValidationHeadData = StorageValue<_, Vec, OptionQuery>; + frame_support::parameter_types! { + pub InitialFactor: FixedU128 = FixedU128::from_u32(1); + } + + /// The number to multiply the base delivery fee by. + #[pallet::storage] + #[pallet::getter(fn delivery_fee_factor_ump)] + pub(crate) type DeliveryFeeFactorUmp = + StorageValue<_, FixedU128, ValueQuery, InitialFactor>; + #[pallet::inherent] impl ProvideInherent for Pallet { type Call = Call; @@ -1069,7 +1085,7 @@ impl Pallet { match Self::host_configuration() { Some(cfg) => if message.len() > cfg.max_upward_message_size as usize { - return Err(MessageSendError::TooBig) + Self::increment_ump_fee_factor(); }, None => { // This storage field should carry over from the previous block. So if it's None @@ -1091,6 +1107,28 @@ impl Pallet { Self::deposit_event(Event::UpwardMessageSent { message_hash: Some(hash) }); Ok((0, hash)) } + + /// 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_ump_fee_factor() -> FixedU128 { + >::mutate(|f| { + *f = f.saturating_mul(MULTIPLICATIVE_FEE_FACTOR_UMP); + *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_ump_fee_factor() -> FixedU128 { + >::mutate(|f| { + *f = InitialFactor::get().max(*f / MULTIPLICATIVE_FEE_FACTOR_UMP); + *f + }) + } } impl UpwardMessageSender for Pallet { diff --git a/pallets/xcmp-queue/src/lib.rs b/pallets/xcmp-queue/src/lib.rs index df2b78ebc20..5cb2db0f094 100644 --- a/pallets/xcmp-queue/src/lib.rs +++ b/pallets/xcmp-queue/src/lib.rs @@ -53,7 +53,7 @@ use rand_chacha::{ ChaChaRng, }; use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; +use sp_runtime::{FixedPointNumber, FixedU128, RuntimeDebug, Saturating}; use sp_std::{convert::TryFrom, prelude::*}; use xcm::{latest::prelude::*, VersionedXcm, WrapVersion, MAX_XCM_DECODE_DEPTH}; use xcm_executor::traits::ConvertOrigin; @@ -72,6 +72,8 @@ const MAX_MESSAGES_PER_BLOCK: u8 = 10; // Maximum amount of messages that can exist in the overweight queue at any given time. const MAX_OVERWEIGHT_MESSAGES: u32 = 1000; +pub const MULTIPLICATIVE_FEE_FACTOR_XCMP: FixedU128 = FixedU128::from_rational(101, 100); // 1.01 + #[frame_support::pallet] pub mod pallet { use super::*; @@ -374,6 +376,16 @@ 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>; + + frame_support::parameter_types! { + pub InitialFactor: FixedU128 = FixedU128::from_u32(1); + } + + /// The number to multiply the base delivery fee by. + #[pallet::storage] + #[pallet::getter(fn delivery_fee_factor_xcmp)] + pub(crate) type DeliveryFeeFactor = + StorageValue<_, FixedU128, ValueQuery, InitialFactor>; } #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, TypeInfo)] @@ -516,7 +528,7 @@ impl Pallet { let max_message_size = T::ChannelInfo::get_channel_max(recipient).ok_or(MessageSendError::NoChannel)?; if data.len() > max_message_size { - return Err(MessageSendError::TooBig) + Self::increment_xcmp_fee_factor(); } let mut s = >::get(); @@ -943,6 +955,28 @@ impl Pallet { } }); } + + /// 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_xcmp_fee_factor() -> FixedU128 { + >::mutate(|f| { + *f = f.saturating_mul(MULTIPLICATIVE_FEE_FACTOR_XCMP); + *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_xcmp_fee_factor() -> FixedU128 { + >::mutate(|f| { + *f = InitialFactor::get().max(*f / MULTIPLICATIVE_FEE_FACTOR_XCMP); + *f + }) + } } impl XcmpMessageHandler for Pallet { @@ -1129,6 +1163,9 @@ impl XcmpMessageSource for Pallet { >::put(statuses); + if !result.is_empty() { + Self::decrement_xcmp_fee_factor(); + } result } } @@ -1190,3 +1227,21 @@ impl SendXcm for Pallet { } } } + +/// Implementation of `PriceForSiblingDelivery` 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 + msg_len * M)`. +pub struct SiblingExponentialPrice(sp_std::marker::PhantomData<(A, B, M, F)>); +impl, B: Get, M: Get, F: Get> PriceForSiblingDelivery + for SiblingExponentialPrice +{ + fn price_for_sibling_delivery(_para_id: 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().saturating_mul_int(fee_sum); + (A::get(), amount).into() + } +} diff --git a/parachains/common/src/xcm_config.rs b/parachains/common/src/xcm_config.rs index 753aa97c6fe..8855642abf0 100644 --- a/parachains/common/src/xcm_config.rs +++ b/parachains/common/src/xcm_config.rs @@ -1,11 +1,13 @@ use crate::impls::AccountIdOf; use core::{marker::PhantomData, ops::ControlFlow}; +use cumulus_primitives_utility::PriceForParentDelivery; use frame_support::{ log, traits::{fungibles::Inspect, tokens::BalanceConversion, ContainsPair}, weights::{Weight, WeightToFee, WeightToFeePolynomial}, }; -use sp_runtime::traits::Get; +use sp_core::Encode; +use sp_runtime::{traits::Get, FixedPointNumber, FixedU128}; use xcm::{latest::prelude::*, CreateMatcher, MatchXcm}; use xcm_executor::traits::ShouldExecute; @@ -134,3 +136,22 @@ impl> ContainsPair matches!(asset.id, Concrete(ref id) if id == origin && origin == &Location::get()) } } + +//TODO: just reference this from polkadot once https://github.com/paritytech/polkadot/pull/6843 merges +/// Implementation of `PriceForParentDelivery` 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 + msg_len * M)`. +pub struct ExponentialPrice(sp_std::marker::PhantomData<(A, B, M, F)>); +impl, B: Get, M: Get, F: Get> PriceForParentDelivery + for ExponentialPrice +{ + fn price_for_parent_delivery(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().saturating_mul_int(fee_sum); + (A::get(), amount).into() + } +} diff --git a/parachains/runtimes/assets/statemine/src/lib.rs b/parachains/runtimes/assets/statemine/src/lib.rs index b937e7c1397..97bf2b33149 100644 --- a/parachains/runtimes/assets/statemine/src/lib.rs +++ b/parachains/runtimes/assets/statemine/src/lib.rs @@ -28,7 +28,9 @@ pub mod constants; mod weights; pub mod xcm_config; +use crate::xcm_config::{BaseDeliveryFee, FeeAssetId, XcmpFeeFactor}; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; +use cumulus_pallet_xcmp_queue::SiblingExponentialPrice; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -475,7 +477,8 @@ 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 = + SiblingExponentialPrice; } impl cumulus_pallet_dmp_queue::Config for Runtime { diff --git a/parachains/runtimes/assets/statemine/src/xcm_config.rs b/parachains/runtimes/assets/statemine/src/xcm_config.rs index 6711a278509..d366aef981f 100644 --- a/parachains/runtimes/assets/statemine/src/xcm_config.rs +++ b/parachains/runtimes/assets/statemine/src/xcm_config.rs @@ -18,6 +18,7 @@ use super::{ ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, TrustBackedAssetsInstance, WeightToFee, XcmpQueue, }; +use crate::{TransactionByteFee, CENTS}; use frame_support::{ match_types, parameter_types, traits::{ConstU32, Contains, Everything, Nothing, PalletInfoAccess}, @@ -27,10 +28,11 @@ use parachains_common::{ impls::ToStakingPot, xcm_config::{ AssetFeeAsExistentialDepositMultiplier, DenyReserveTransferToRelayChain, DenyThenTry, + ExponentialPrice, }, }; use polkadot_parachain::primitives::Sibling; -use sp_runtime::traits::ConvertInto; +use sp_runtime::{traits::ConvertInto, FixedU128}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, @@ -129,6 +131,19 @@ pub type XcmOriginToTransactDispatchOrigin = ( parameter_types! { pub const MaxInstructions: u32 = 100; + + /// The asset ID for the asset that we use to pay for message delivery fees. + pub FeeAssetId: AssetId = Concrete(Here.into()); + + /// The base fee for the message delivery fees. + pub const BaseDeliveryFee: u128 = CENTS.saturating_mul(3); + + /// The factor to multiply by for the message delivery fees. + pub UmpFeeFactor: FixedU128 = ParachainSystem::delivery_fee_factor_ump(); + + /// The factor to multiply by for the message delivery fees. + pub XcmpFeeFactor: FixedU128 = XcmpQueue::delivery_fee_factor_xcmp(); + pub const MaxAssetsIntoHolding: u32 = 64; pub XcmAssetFeesReceiver: Option = Authorship::author(); } @@ -326,7 +341,11 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp< + ParachainSystem, + PolkadotXcm, + ExponentialPrice, + >, // ..and XCMP to communicate with the sibling chains. XcmpQueue, ); diff --git a/primitives/utility/src/lib.rs b/primitives/utility/src/lib.rs index 75ad7f417eb..325b16c99ff 100644 --- a/primitives/utility/src/lib.rs +++ b/primitives/utility/src/lib.rs @@ -54,10 +54,6 @@ impl> PriceForParentDelivery for ConstantPrice { /// 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. -/// -/// NOTE: This is a pretty dumb "just send it" router; we will probably want to introduce queuing -/// to UMP eventually and when we do, the pallet which implements the queuing will be responsible -/// for the `SendXcm` implementation. pub struct ParentAsUmp(PhantomData<(T, W, P)>); impl SendXcm for ParentAsUmp where