mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 13:01:07 +00:00
Rework Transaction Priority calculation (#9834)
* Add transaction validity docs. * Re-work priority calculation. * Fix tests. * Update frame/transaction-payment/src/lib.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * cargo +nightly fmt --all * Fix an obvious mistake :) * Re-work again. * Fix test. * cargo +nightly fmt --all * Make VirtualTip dependent on the transaction size. * cargo +nightly fmt --all * Update frame/transaction-payment/src/lib.rs Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> * Fix compilation. * Update bin/node/runtime/src/lib.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Alexander Popiak <alexander.popiak@parity.io> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Generated
+4
-4
@@ -2473,9 +2473,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.13.21"
|
||||
version = "0.13.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "659cd14835e75b64d9dba5b660463506763cf0aa6cb640aeeb0e98d841093490"
|
||||
checksum = "9c1cbbfc9a1996c6af82c2b4caf828d2c653af4fcdbb0e5674cc966eee5a4197"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -3294,9 +3294,9 @@ checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.12.22+1.1.0"
|
||||
version = "0.12.23+1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89c53ac117c44f7042ad8d8f5681378dfbc6010e49ec2c0d1f11dfedc7a4a1c3"
|
||||
checksum = "29730a445bae719db3107078b46808cc45a5b7a6bae3f31272923af969453356"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
||||
@@ -258,11 +258,13 @@ impl pallet_balances::Config for Runtime {
|
||||
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: Balance = 1;
|
||||
pub OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<Balance>;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
@@ -413,6 +413,7 @@ impl pallet_balances::Config for Runtime {
|
||||
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: Balance = 10 * MILLICENTS;
|
||||
pub const OperationalFeeMultiplier: u8 = 5;
|
||||
pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
|
||||
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 100_000);
|
||||
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128);
|
||||
@@ -421,6 +422,7 @@ parameter_types! {
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<Balance>;
|
||||
type FeeMultiplierUpdate =
|
||||
TargetedFeeAdjustment<Self, TargetBlockFullness, AdjustmentVariable, MinimumMultiplier>;
|
||||
|
||||
@@ -76,10 +76,12 @@ impl frame_system::Config for Test {
|
||||
}
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: u64 = 1;
|
||||
pub const OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
impl pallet_transaction_payment::Config for Test {
|
||||
type OnChargeTransaction = CurrencyAdapter<Pallet<Test>, ()>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<u64>;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
@@ -78,10 +78,12 @@ impl frame_system::Config for Test {
|
||||
}
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: u64 = 1;
|
||||
pub const OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
impl pallet_transaction_payment::Config for Test {
|
||||
type OnChargeTransaction = CurrencyAdapter<Pallet<Test>, ()>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<u64>;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
@@ -80,10 +80,12 @@ impl frame_system::Config for Test {
|
||||
}
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: u64 = 1;
|
||||
pub const OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
impl pallet_transaction_payment::Config for Test {
|
||||
type OnChargeTransaction = CurrencyAdapter<Pallet<Test>, ()>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<u64>;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
@@ -800,10 +800,12 @@ mod tests {
|
||||
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: Balance = 0;
|
||||
pub const OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
impl pallet_transaction_payment::Config for Runtime {
|
||||
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = IdentityFee<Balance>;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
@@ -1110,8 +1112,6 @@ mod tests {
|
||||
let invalid = TestXt::new(Call::Custom(custom::Call::unallowed_unsigned {}), None);
|
||||
let mut t = new_test_ext(1);
|
||||
|
||||
let mut default_with_prio_3 = ValidTransaction::default();
|
||||
default_with_prio_3.priority = 3;
|
||||
t.execute_with(|| {
|
||||
assert_eq!(
|
||||
Executive::validate_transaction(
|
||||
@@ -1119,7 +1119,7 @@ mod tests {
|
||||
valid.clone(),
|
||||
Default::default(),
|
||||
),
|
||||
Ok(default_with_prio_3),
|
||||
Ok(ValidTransaction::default()),
|
||||
);
|
||||
assert_eq!(
|
||||
Executive::validate_transaction(
|
||||
|
||||
@@ -287,30 +287,6 @@ impl<'a> OneOrMany<DispatchClass> for &'a [DispatchClass] {
|
||||
}
|
||||
}
|
||||
|
||||
/// Primitives related to priority management of Frame.
|
||||
pub mod priority {
|
||||
/// The starting point of all Operational transactions. 3/4 of u64::MAX.
|
||||
pub const LIMIT: u64 = 13_835_058_055_282_163_711_u64;
|
||||
|
||||
/// Wrapper for priority of different dispatch classes.
|
||||
///
|
||||
/// This only makes sure that any value created for the operational dispatch class is
|
||||
/// incremented by [`LIMIT`].
|
||||
pub enum FrameTransactionPriority {
|
||||
Normal(u64),
|
||||
Operational(u64),
|
||||
}
|
||||
|
||||
impl From<FrameTransactionPriority> for u64 {
|
||||
fn from(priority: FrameTransactionPriority) -> Self {
|
||||
match priority {
|
||||
FrameTransactionPriority::Normal(inner) => inner,
|
||||
FrameTransactionPriority::Operational(inner) => inner.saturating_add(LIMIT),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bundle of static information collected from the `#[weight = $x]` attributes.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo)]
|
||||
pub struct DispatchInfo {
|
||||
|
||||
@@ -24,6 +24,11 @@ use sp_runtime::{
|
||||
};
|
||||
|
||||
/// Genesis hash check to provide replay protection between different networks.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// Note that while a transaction with invalid `genesis_hash` will fail to be decoded,
|
||||
/// the extension does not affect any other fields of `TransactionValidity` directly.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckGenesis<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
@@ -27,6 +27,10 @@ use sp_runtime::{
|
||||
};
|
||||
|
||||
/// Check for transaction mortality.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// The extension affects `longevity` of the transaction according to the [`Era`] definition.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckMortality<T: Config + Send + Sync>(Era, sp_std::marker::PhantomData<T>);
|
||||
|
||||
@@ -30,8 +30,11 @@ use sp_std::vec;
|
||||
|
||||
/// Nonce check and increment to give replay protection for transactions.
|
||||
///
|
||||
/// Note that this does not set any priority by default. Make sure that AT LEAST one of the signed
|
||||
/// extension sets some kind of priority upon validating transactions.
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// This extension affects `requires` and `provides` tags of validity, but DOES NOT
|
||||
/// set the `priority` field. Make sure that AT LEAST one of the signed extension sets
|
||||
/// some kind of priority upon validating transactions.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Index);
|
||||
|
||||
@@ -21,6 +21,11 @@ use scale_info::TypeInfo;
|
||||
use sp_runtime::{traits::SignedExtension, transaction_validity::TransactionValidityError};
|
||||
|
||||
/// Ensure the runtime version registered in the transaction is the same as at present.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// The transaction with incorrect `spec_version` are considered invalid. The validity
|
||||
/// is not affected in any other way.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckSpecVersion<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
@@ -21,6 +21,11 @@ use scale_info::TypeInfo;
|
||||
use sp_runtime::{traits::SignedExtension, transaction_validity::TransactionValidityError};
|
||||
|
||||
/// Ensure the transaction version registered in the transaction is the same as at present.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// The transaction with incorrect `transaction_version` are considered invalid. The validity
|
||||
/// is not affected in any other way.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckTxVersion<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||
|
||||
@@ -19,19 +19,21 @@ use crate::{limits::BlockWeights, Config, Pallet};
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{
|
||||
traits::Get,
|
||||
weights::{priority::FrameTransactionPriority, DispatchClass, DispatchInfo, PostDispatchInfo},
|
||||
weights::{DispatchClass, DispatchInfo, PostDispatchInfo},
|
||||
};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{
|
||||
traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension},
|
||||
transaction_validity::{
|
||||
InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,
|
||||
ValidTransaction,
|
||||
},
|
||||
transaction_validity::{InvalidTransaction, TransactionValidity, TransactionValidityError},
|
||||
DispatchResult,
|
||||
};
|
||||
|
||||
/// Block resource (weight) limit check.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// This extension does not influence any fields of `TransactionValidity` in case the
|
||||
/// transaction is valid.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct CheckWeight<T: Config + Send + Sync>(sp_std::marker::PhantomData<T>);
|
||||
@@ -81,23 +83,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the priority of an extrinsic denoted by `info`.
|
||||
///
|
||||
/// Operational transaction will be given a fixed initial amount to be fairly distinguished from
|
||||
/// the normal ones.
|
||||
fn get_priority(info: &DispatchInfoOf<T::Call>) -> TransactionPriority {
|
||||
match info.class {
|
||||
// Normal transaction.
|
||||
DispatchClass::Normal => FrameTransactionPriority::Normal(info.weight.into()).into(),
|
||||
// Don't use up the whole priority space, to allow things like `tip` to be taken into
|
||||
// account as well.
|
||||
DispatchClass::Operational =>
|
||||
FrameTransactionPriority::Operational(info.weight.into()).into(),
|
||||
// Mandatory extrinsics are only for inherents; never transactions.
|
||||
DispatchClass::Mandatory => TransactionPriority::min_value(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `SignedExtension` to check weight of the extrinsic.
|
||||
pub fn new() -> Self {
|
||||
Self(Default::default())
|
||||
@@ -130,7 +115,7 @@ where
|
||||
// consumption from causing false negatives.
|
||||
Self::check_extrinsic_weight(info)?;
|
||||
|
||||
Ok(ValidTransaction { priority: Self::get_priority(info), ..Default::default() })
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,13 +353,7 @@ mod tests {
|
||||
};
|
||||
let len = 0_usize;
|
||||
|
||||
assert_eq!(
|
||||
CheckWeight::<Test>::do_validate(&okay, len),
|
||||
Ok(ValidTransaction {
|
||||
priority: CheckWeight::<Test>::get_priority(&okay),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
assert_eq!(CheckWeight::<Test>::do_validate(&okay, len), Ok(Default::default()));
|
||||
assert_err!(
|
||||
CheckWeight::<Test>::do_validate(&max, len),
|
||||
InvalidTransaction::ExhaustsResources
|
||||
@@ -506,30 +485,6 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_ext_check_weight_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let normal =
|
||||
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
|
||||
let op = DispatchInfo {
|
||||
weight: 100,
|
||||
class: DispatchClass::Operational,
|
||||
pays_fee: Pays::Yes,
|
||||
};
|
||||
let len = 0_usize;
|
||||
|
||||
let priority = CheckWeight::<Test>(PhantomData)
|
||||
.validate(&1, CALL, &normal, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority, 100);
|
||||
|
||||
let priority =
|
||||
CheckWeight::<Test>(PhantomData).validate(&1, CALL, &op, len).unwrap().priority;
|
||||
assert_eq!(priority, frame_support::weights::priority::LIMIT + 100);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_ext_check_weight_block_size_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
|
||||
@@ -264,6 +264,30 @@ pub mod pallet {
|
||||
#[pallet::constant]
|
||||
type TransactionByteFee: Get<BalanceOf<Self>>;
|
||||
|
||||
/// A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their
|
||||
/// `priority`
|
||||
///
|
||||
/// This value is multipled by the `final_fee` to obtain a "virtual tip" that is later
|
||||
/// added to a tip component in regular `priority` calculations.
|
||||
/// It means that a `Normal` transaction can front-run a similarly-sized `Operational`
|
||||
/// extrinsic (with no tip), by including a tip value greater than the virtual tip.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // For `Normal`
|
||||
/// let priority = priority_calc(tip);
|
||||
///
|
||||
/// // For `Operational`
|
||||
/// let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;
|
||||
/// let priority = priority_calc(tip + virtual_tip);
|
||||
/// ```
|
||||
///
|
||||
/// Note that since we use `final_fee` the multiplier applies also to the regular `tip`
|
||||
/// sent with the transaction. So, not only does the transaction get a priority bump based
|
||||
/// on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`
|
||||
/// transactions.
|
||||
#[pallet::constant]
|
||||
type OperationalFeeMultiplier: Get<u8>;
|
||||
|
||||
/// Convert a weight value into a deductible fee based on the currency type.
|
||||
type WeightToFee: WeightToFeePolynomial<Balance = BalanceOf<Self>>;
|
||||
|
||||
@@ -525,6 +549,14 @@ where
|
||||
|
||||
/// Require the transactor pay for themselves and maybe include a tip to gain additional priority
|
||||
/// in the queue.
|
||||
///
|
||||
/// # Transaction Validity
|
||||
///
|
||||
/// This extension sets the `priority` field of `TransactionValidity` depending on the amount
|
||||
/// of tip being paid per weight unit.
|
||||
///
|
||||
/// Operational transactions will receive an additional priority bump, so that they are normally
|
||||
/// considered before regular transactions.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);
|
||||
@@ -566,27 +598,73 @@ where
|
||||
.map(|i| (fee, i))
|
||||
}
|
||||
|
||||
/// Get an appropriate priority for a transaction with the given length and info.
|
||||
/// Get an appropriate priority for a transaction with the given `DispatchInfo`, encoded length
|
||||
/// and user-included tip.
|
||||
///
|
||||
/// This will try and optimise the `fee/weight` `fee/length`, whichever is consuming more of the
|
||||
/// maximum corresponding limit.
|
||||
/// The priority is based on the amount of `tip` the user is willing to pay per unit of either
|
||||
/// `weight` or `length`, depending which one is more limitting. For `Operational` extrinsics
|
||||
/// we add a "virtual tip" to the calculations.
|
||||
///
|
||||
/// For example, if a transaction consumed 1/4th of the block length and half of the weight, its
|
||||
/// final priority is `fee * min(2, 4) = fee * 2`. If it consumed `1/4th` of the block length
|
||||
/// and the entire block weight `(1/1)`, its priority is `fee * min(1, 4) = fee * 1`. This means
|
||||
/// that the transaction which consumes more resources (either length or weight) with the same
|
||||
/// `fee` ends up having lower priority.
|
||||
fn get_priority(
|
||||
len: usize,
|
||||
/// The formula should simply be `tip / bounded_{weight|length}`, but since we are using
|
||||
/// integer division, we have no guarantees it's going to give results in any reasonable
|
||||
/// range (might simply end up being zero). Hence we use a scaling factor:
|
||||
/// `tip * (max_block_{weight|length} / bounded_{weight|length})`, since given current
|
||||
/// state of-the-art blockchains, number of per-block transactions is expected to be in a
|
||||
/// range reasonable enough to not saturate the `Balance` type while multiplying by the tip.
|
||||
pub fn get_priority(
|
||||
info: &DispatchInfoOf<T::Call>,
|
||||
len: usize,
|
||||
tip: BalanceOf<T>,
|
||||
final_fee: BalanceOf<T>,
|
||||
) -> TransactionPriority {
|
||||
let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);
|
||||
let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);
|
||||
let len_saturation = max_block_length as u64 / (len as u64).max(1);
|
||||
let coefficient: BalanceOf<T> =
|
||||
weight_saturation.min(len_saturation).saturated_into::<BalanceOf<T>>();
|
||||
final_fee.saturating_mul(coefficient).saturated_into::<TransactionPriority>()
|
||||
// Calculate how many such extrinsics we could fit into an empty block and take
|
||||
// the limitting factor.
|
||||
let max_block_weight = T::BlockWeights::get().max_block;
|
||||
let max_block_length = *T::BlockLength::get().max.get(info.class) as u64;
|
||||
|
||||
let bounded_weight = info.weight.max(1).min(max_block_weight);
|
||||
let bounded_length = (len as u64).max(1).min(max_block_length);
|
||||
|
||||
let max_tx_per_block_weight = max_block_weight / bounded_weight;
|
||||
let max_tx_per_block_length = max_block_length / bounded_length;
|
||||
// Given our current knowledge this value is going to be in a reasonable range - i.e.
|
||||
// less than 10^9 (2^30), so multiplying by the `tip` value is unlikely to overflow the
|
||||
// balance type. We still use saturating ops obviously, but the point is to end up with some
|
||||
// `priority` distribution instead of having all transactions saturate the priority.
|
||||
let max_tx_per_block = max_tx_per_block_length
|
||||
.min(max_tx_per_block_weight)
|
||||
.saturated_into::<BalanceOf<T>>();
|
||||
let max_reward = |val: BalanceOf<T>| val.saturating_mul(max_tx_per_block);
|
||||
|
||||
// To distribute no-tip transactions a little bit, we set the minimal tip as `1`.
|
||||
// This means that given two transactions without a tip, smaller one will be preferred.
|
||||
let tip = tip.max(1.saturated_into());
|
||||
let scaled_tip = max_reward(tip);
|
||||
|
||||
match info.class {
|
||||
DispatchClass::Normal => {
|
||||
// For normal class we simply take the `tip_per_weight`.
|
||||
scaled_tip
|
||||
},
|
||||
DispatchClass::Mandatory => {
|
||||
// Mandatory extrinsics should be prohibited (e.g. by the [`CheckWeight`]
|
||||
// extensions), but just to be safe let's return the same priority as `Normal` here.
|
||||
scaled_tip
|
||||
},
|
||||
DispatchClass::Operational => {
|
||||
// A "virtual tip" value added to an `Operational` extrinsic.
|
||||
// This value should be kept high enough to allow `Operational` extrinsics
|
||||
// to get in even during congestion period, but at the same time low
|
||||
// enough to prevent a possible spam attack by sending invalid operational
|
||||
// extrinsics which push away regular transactions from the pool.
|
||||
let fee_multiplier = T::OperationalFeeMultiplier::get().saturated_into();
|
||||
let virtual_tip = final_fee.saturating_mul(fee_multiplier);
|
||||
let scaled_virtual_tip = max_reward(virtual_tip);
|
||||
|
||||
scaled_tip.saturating_add(scaled_virtual_tip)
|
||||
},
|
||||
}
|
||||
.saturated_into::<TransactionPriority>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,8 +707,12 @@ where
|
||||
info: &DispatchInfoOf<Self::Call>,
|
||||
len: usize,
|
||||
) -> TransactionValidity {
|
||||
let (fee, _) = self.withdraw_fee(who, call, info, len)?;
|
||||
Ok(ValidTransaction { priority: Self::get_priority(len, info, fee), ..Default::default() })
|
||||
let (final_fee, _) = self.withdraw_fee(who, call, info, len)?;
|
||||
let tip = self.0;
|
||||
Ok(ValidTransaction {
|
||||
priority: Self::get_priority(info, len, tip, final_fee),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
fn pre_dispatch(
|
||||
@@ -743,6 +825,7 @@ mod tests {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub static TransactionByteFee: u64 = 1;
|
||||
pub static WeightToFee: u64 = 1;
|
||||
pub static OperationalFeeMultiplier: u8 = 5;
|
||||
}
|
||||
|
||||
impl frame_system::Config for Runtime {
|
||||
@@ -822,6 +905,7 @@ mod tests {
|
||||
impl Config for Runtime {
|
||||
type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees>;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type OperationalFeeMultiplier = OperationalFeeMultiplier;
|
||||
type WeightToFee = WeightToFee;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
@@ -1335,6 +1419,79 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_alter_operational_priority() {
|
||||
let tip = 5;
|
||||
let len = 10;
|
||||
|
||||
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
|
||||
let normal =
|
||||
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
|
||||
let priority = ChargeTransactionPayment::<Runtime>(tip)
|
||||
.validate(&2, CALL, &normal, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
|
||||
assert_eq!(priority, 50);
|
||||
|
||||
let priority = ChargeTransactionPayment::<Runtime>(2 * tip)
|
||||
.validate(&2, CALL, &normal, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
|
||||
assert_eq!(priority, 100);
|
||||
});
|
||||
|
||||
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
|
||||
let op = DispatchInfo {
|
||||
weight: 100,
|
||||
class: DispatchClass::Operational,
|
||||
pays_fee: Pays::Yes,
|
||||
};
|
||||
let priority = ChargeTransactionPayment::<Runtime>(tip)
|
||||
.validate(&2, CALL, &op, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority, 5800);
|
||||
|
||||
let priority = ChargeTransactionPayment::<Runtime>(2 * tip)
|
||||
.validate(&2, CALL, &op, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority, 6100);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_tip_has_some_priority() {
|
||||
let tip = 0;
|
||||
let len = 10;
|
||||
|
||||
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
|
||||
let normal =
|
||||
DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes };
|
||||
let priority = ChargeTransactionPayment::<Runtime>(tip)
|
||||
.validate(&2, CALL, &normal, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
|
||||
assert_eq!(priority, 10);
|
||||
});
|
||||
|
||||
ExtBuilder::default().balance_factor(100).build().execute_with(|| {
|
||||
let op = DispatchInfo {
|
||||
weight: 100,
|
||||
class: DispatchClass::Operational,
|
||||
pays_fee: Pays::Yes,
|
||||
};
|
||||
let priority = ChargeTransactionPayment::<Runtime>(tip)
|
||||
.validate(&2, CALL, &op, len)
|
||||
.unwrap()
|
||||
.priority;
|
||||
assert_eq!(priority, 5510);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_info_can_change_pays_fee() {
|
||||
ExtBuilder::default()
|
||||
|
||||
Reference in New Issue
Block a user