From 5606b556708c4bd331917f255af4c57dd428b1f6 Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Wed, 8 Dec 2021 08:37:45 +0800 Subject: [PATCH 1/5] update transaction-payment --- modules/transaction-payment/src/lib.rs | 116 +++++++++++++++++---- modules/transaction-payment/src/mock.rs | 2 + modules/transaction-payment/src/tests.rs | 123 ++++++++++++++++++++--- runtime/acala/src/lib.rs | 2 + runtime/common/src/lib.rs | 5 +- runtime/common/src/precompile/mock.rs | 2 + runtime/karura/src/lib.rs | 2 + runtime/mandala/src/lib.rs | 2 + 8 files changed, 217 insertions(+), 37 deletions(-) diff --git a/modules/transaction-payment/src/lib.rs b/modules/transaction-payment/src/lib.rs index 383d35b1b2..0bed15962f 100644 --- a/modules/transaction-payment/src/lib.rs +++ b/modules/transaction-payment/src/lib.rs @@ -248,6 +248,30 @@ pub mod module { #[pallet::constant] type TransactionByteFee: Get>; + /// 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; + /// Convert a weight value into a deductible fee based on the currency /// type. type WeightToFee: WeightToFeePolynomial>; @@ -652,6 +676,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(#[codec(compact)] pub PalletBalanceOf); @@ -707,32 +739,73 @@ where } } - /// 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. + /// 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. fn get_priority( - len: usize, info: &DispatchInfoOf<::Call>, + len: usize, + tip: PalletBalanceOf, final_fee: PalletBalanceOf, ) -> 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: PalletBalanceOf = weight_saturation - .min(len_saturation) + // 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::>(); - final_fee - .saturating_mul(coefficient) - .saturated_into::() + let max_reward = |val: PalletBalanceOf| val.saturating_mul(max_tx_per_block); + + // To distribute no-tip transactions a little bit, we increase the tip value by one. + // This means that given two transactions without a tip, smaller one will be preferred. + let tip = tip.saturating_add(One::one()); + 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::() } } @@ -763,9 +836,10 @@ where info: &DispatchInfoOf, len: usize, ) -> TransactionValidity { - let (fee, _) = self.withdraw_fee(who, call, info, len)?; + let (final_fee, _) = self.withdraw_fee(who, call, info, len)?; + let tip = self.0; Ok(ValidTransaction { - priority: Self::get_priority(len, info, fee), + priority: Self::get_priority(info, len, tip, final_fee), ..Default::default() }) } diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index 235efa41d5..a1613c9415 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -179,6 +179,7 @@ impl module_dex::Config for Runtime { parameter_types! { pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::saturating_from_rational(1, 2); pub static TransactionByteFee: u128 = 1; + pub OperationalFeeMultiplier: u8 = 5; pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![DOT, AUSD, ACA]]; } @@ -226,6 +227,7 @@ impl Config for Runtime { type MultiCurrency = Currencies; type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; + type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = (); type DEX = DEXModule; diff --git a/modules/transaction-payment/src/tests.rs b/modules/transaction-payment/src/tests.rs index 478365ccb8..738c6db1fb 100644 --- a/modules/transaction-payment/src/tests.rs +++ b/modules/transaction-payment/src/tests.rs @@ -69,7 +69,6 @@ fn charges_fee_when_native_is_enough_but_cannot_keep_alive() { TransactionValidityError::Invalid(InvalidTransaction::Payment) ); - let fee2 = 23 * 2 + 990; assert_eq!( ChargeTransactionPayment::::from(0) .validate( @@ -84,7 +83,7 @@ fn charges_fee_when_native_is_enough_but_cannot_keep_alive() { ) .unwrap() .priority, - fee2.saturated_into::() + 1 ); assert_eq!(Currencies::free_balance(ACA, &ALICE), Currencies::minimum_balance(ACA)); }); @@ -102,9 +101,9 @@ fn charges_fee() { .validate(&ALICE, CALL, &INFO, 23) .unwrap() .priority, - fee + 1 ); - assert_eq!(Currencies::free_balance(ACA, &ALICE), (100000 - fee).into()); + assert_eq!(Currencies::free_balance(ACA, &ALICE), 100000 - fee); let fee2 = 18 * 2 + 1000; // len * byte + weight assert_eq!( @@ -112,12 +111,9 @@ fn charges_fee() { .validate(&ALICE, CALL2, &INFO, 18) .unwrap() .priority, - fee2 - ); - assert_eq!( - Currencies::free_balance(ACA, &ALICE), - (100000 - fee - fee2).unique_saturated_into() + 1 ); + assert_eq!(Currencies::free_balance(ACA, &ALICE), 100000 - fee - fee2); }); } @@ -220,13 +216,12 @@ fn charges_fee_when_validate_and_native_is_not_enough() { assert_eq!(>::free_balance(AUSD, &BOB), 1000); // total balance is lt ED, will swap fee and ED - let fee = 500 * 2 + 1000; // len * byte + weight assert_eq!( ChargeTransactionPayment::::from(0) .validate(&BOB, CALL2, &INFO, 500) .unwrap() .priority, - fee + 1 ); assert_eq!(Currencies::total_balance(ACA, &BOB), 10); assert_eq!(Currencies::free_balance(ACA, &BOB), 10); @@ -238,13 +233,12 @@ fn charges_fee_when_validate_and_native_is_not_enough() { // total balance is gte ED, but cannot keep alive after charge, // will swap extra gap to keep alive - let fee_2 = 100 * 2 + 1000; // len * byte + weight assert_eq!( ChargeTransactionPayment::::from(0) .validate(&BOB, CALL2, &INFO, 100) .unwrap() .priority, - fee_2 + 1 ); assert_eq!(Currencies::total_balance(ACA, &BOB), 10); assert_eq!(Currencies::free_balance(ACA, &BOB), 10); @@ -364,13 +358,12 @@ fn charge_fee_by_default_swap_path() { assert_eq!(>::free_balance(AUSD, &BOB), 0); assert_eq!(>::free_balance(DOT, &BOB), 100); - let fee = 500 * 2 + 1000; // len * byte + weight assert_eq!( ChargeTransactionPayment::::from(0) .validate(&BOB, CALL2, &INFO, 500) .unwrap() .priority, - fee + 1 ); assert_eq!(Currencies::free_balance(ACA, &BOB), Currencies::minimum_balance(ACA)); @@ -536,3 +529,103 @@ fn compute_fee_does_not_overflow() { ); }); } + +#[test] +fn should_alter_operational_priority() { + let tip = 5; + let len = 10; + + ExtBuilder::default() + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let normal = DispatchInfo { + weight: 100, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 60); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 110); + }); + + ExtBuilder::default() + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let op = DispatchInfo { + weight: 100, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &op, len) + .unwrap() + .priority; + // final_fee = base_fee + len_fee + adjusted_weight_fee + tip = 0 + 20 + 100 + 5 = 125 + // priority = final_fee * fee_multiplier * max_tx_per_block + (tip + 1) * max_tx_per_block + // = 125 * 5 * 10 + 60 = 6310 + assert_eq!(priority, 6310); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&ALICE, CALL, &op, len) + .unwrap() + .priority; + // final_fee = base_fee + len_fee + adjusted_weight_fee + tip = 0 + 20 + 100 + 10 = 130 + // priority = final_fee * fee_multiplier * max_tx_per_block + (tip + 1) * max_tx_per_block + // = 130 * 5 * 10 + 110 = 6610 + assert_eq!(priority, 6610); + }); +} + +#[test] +fn no_tip_has_some_priority() { + let tip = 0; + let len = 10; + + ExtBuilder::default() + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let normal = DispatchInfo { + weight: 100, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10); + }); + + ExtBuilder::default() + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let op = DispatchInfo { + weight: 100, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &op, len) + .unwrap() + .priority; + // final_fee = base_fee + len_fee + adjusted_weight_fee + tip = 0 + 20 + 100 + 0 = 120 + // priority = final_fee * fee_multiplier * max_tx_per_block + (tip + 1) * max_tx_per_block + // = 120 * 5 * 10 + 10 = 6010 + assert_eq!(priority, 6010); + }); +} diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index 89829f223f..e19431acc0 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -1090,6 +1090,7 @@ impl module_transaction_pause::Config for Runtime { parameter_types! { // Sort by fee charge order pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, DOT, ACA], vec![DOT, ACA], vec![LDOT, DOT, ACA]]; + pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1113,6 +1114,7 @@ impl module_transaction_payment::Config for Runtime { type MultiCurrency = Currencies; type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; + type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type DEX = Dex; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 39d4efd6e8..883dc7d02b 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -68,7 +68,10 @@ pub type TimeStampedPrice = orml_oracle::TimestampedValue Operational tx -> Unsigned tx -> Signed normal tx pub const CdpEngineUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2; // 50% pub const AuctionManagerUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 5; // 20% diff --git a/runtime/common/src/precompile/mock.rs b/runtime/common/src/precompile/mock.rs index 347e5391dc..7cc3363574 100644 --- a/runtime/common/src/precompile/mock.rs +++ b/runtime/common/src/precompile/mock.rs @@ -244,6 +244,7 @@ parameter_types! { pub const GetStableCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::AUSD); pub DefaultFeeSwapPathList: Vec> = vec![vec![CurrencyId::Token(TokenSymbol::AUSD), CurrencyId::Token(TokenSymbol::ACA)]]; pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::one(); + pub OperationalFeeMultiplier: u8 = 5; } impl module_transaction_payment::Config for Test { @@ -253,6 +254,7 @@ impl module_transaction_payment::Config for Test { type MultiCurrency = Currencies; type OnTransactionPayment = (); type TransactionByteFee = TransactionByteFee; + type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); type DEX = (); diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 74d3e6b214..b6f7a5a405 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -1106,6 +1106,7 @@ parameter_types! { vec![LKSM, KSM, KAR], vec![BNC, KUSD, KSM, KAR], ]; + pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1129,6 +1130,7 @@ impl module_transaction_payment::Config for Runtime { type MultiCurrency = Currencies; type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; + type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type DEX = Dex; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 4addb5ea96..460a8a9fd0 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -1123,6 +1123,7 @@ impl module_transaction_pause::Config for Runtime { parameter_types! { // Sort by fee charge order pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![AUSD, LDOT], vec![AUSD, DOT], vec![AUSD, RENBTC]]; + pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1153,6 +1154,7 @@ impl module_transaction_payment::Config for Runtime { type MultiCurrency = Currencies; type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; + type OperationalFeeMultiplier = OperationalFeeMultiplier; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = TargetedFeeAdjustment; type DEX = Dex; From 46cf8f940f58d3d683be462fe31582e5ec3c5135 Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Wed, 8 Dec 2021 14:47:16 +0800 Subject: [PATCH 2/5] add MaxTipsOfPriority --- modules/transaction-payment/src/lib.rs | 10 ++++++-- modules/transaction-payment/src/mock.rs | 4 +++- modules/transaction-payment/src/tests.rs | 30 ++++++++++++++++++++++++ runtime/acala/src/lib.rs | 11 +++++---- runtime/common/src/lib.rs | 15 ++++++++---- runtime/common/src/precompile/mock.rs | 4 +++- runtime/integration-tests/src/runtime.rs | 5 ++++ runtime/integration-tests/src/setup.rs | 27 +++++++++++---------- runtime/karura/src/lib.rs | 11 +++++---- runtime/mandala/src/lib.rs | 11 +++++---- 10 files changed, 91 insertions(+), 37 deletions(-) diff --git a/modules/transaction-payment/src/lib.rs b/modules/transaction-payment/src/lib.rs index 0bed15962f..15e58019f0 100644 --- a/modules/transaction-payment/src/lib.rs +++ b/modules/transaction-payment/src/lib.rs @@ -270,7 +270,12 @@ pub mod module { /// on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational` /// transactions. #[pallet::constant] - type OperationalFeeMultiplier: Get; + type OperationalFeeMultiplier: Get; + + /// The maximum value of tips that affect the priority. + /// Set the maximum value of tips to prevent affecting the unsigned extrinsic. + #[pallet::constant] + type MaxTipsOfPriority: Get>; /// Convert a weight value into a deductible fee based on the currency /// type. @@ -779,7 +784,8 @@ where // To distribute no-tip transactions a little bit, we increase the tip value by one. // This means that given two transactions without a tip, smaller one will be preferred. - let tip = tip.saturating_add(One::one()); + // Set the maximum value of tips to prevent affecting the unsigned extrinsic. + let tip = tip.saturating_add(One::one()).min(T::MaxTipsOfPriority::get()); let scaled_tip = max_reward(tip); match info.class { diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index a1613c9415..02e49b68fe 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -179,7 +179,8 @@ impl module_dex::Config for Runtime { parameter_types! { pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::saturating_from_rational(1, 2); pub static TransactionByteFee: u128 = 1; - pub OperationalFeeMultiplier: u8 = 5; + pub OperationalFeeMultiplier: u64 = 5; + pub MaxTipsOfPriority: u128 = 1000; pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![DOT, AUSD, ACA]]; } @@ -228,6 +229,7 @@ impl Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = (); type DEX = DEXModule; diff --git a/modules/transaction-payment/src/tests.rs b/modules/transaction-payment/src/tests.rs index 738c6db1fb..28e97cab3c 100644 --- a/modules/transaction-payment/src/tests.rs +++ b/modules/transaction-payment/src/tests.rs @@ -629,3 +629,33 @@ fn no_tip_has_some_priority() { assert_eq!(priority, 6010); }); } + +#[test] +fn max_tip_has_some_priority() { + let tip = 1000; + let len = 10; + + ExtBuilder::default() + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let normal = DispatchInfo { + weight: 100, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10_000); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10_000); + }); +} diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index e19431acc0..78beba75d0 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -110,10 +110,11 @@ pub use runtime_common::{ EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, EnsureRootOrThreeFourthsGeneralCouncil, EnsureRootOrTwoThirdsGeneralCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, FinancialCouncilInstance, FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, - GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, OffchainSolutionWeightLimit, - OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, RelayChainBlockNumberProvider, - RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, SystemContractsFilter, TechnicalCommitteeInstance, - TechnicalCommitteeMembershipInstance, TimeStampedPrice, ACA, AUSD, DOT, LDOT, RENBTC, + GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, + OffchainSolutionWeightLimit, OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, + Ratio, RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, + SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, ACA, + AUSD, DOT, LDOT, RENBTC, }; mod authority; @@ -1090,7 +1091,6 @@ impl module_transaction_pause::Config for Runtime { parameter_types! { // Sort by fee charge order pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, DOT, ACA], vec![DOT, ACA], vec![LDOT, DOT, ACA]]; - pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1115,6 +1115,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type DEX = Dex; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 883dc7d02b..dc48c3a887 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -69,13 +69,18 @@ pub type TimeStampedPrice = orml_oracle::TimestampedValue Operational tx -> Unsigned tx -> Signed normal tx - pub const CdpEngineUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2; // 50% - pub const AuctionManagerUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 5; // 20% - pub const RenvmBridgeUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 10; // 10% + pub const OperationalFeeMultiplier: u64 = 1_000_000_000u64; + // 1_504_592_000u64 from https://github.com/AcalaNetwork/Acala/blob/bda4d430cbecebf8720d700b976875d0d805ceca/runtime/integration-tests/src/runtime.rs#L275 + const MinOperationalPriority: TransactionPriority = 1_500_000_000u64 * OperationalFeeMultiplier::get() as u64; + pub const CdpEngineUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 1000; + pub const AuctionManagerUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 2000; + pub const RenvmBridgeUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 3000; + const MaxNormalPriority: TransactionPriority = MinOperationalPriority::get() / 2; // 50% + pub MaxTipsOfPriority: Balance = (MaxNormalPriority::get() / RuntimeBlockWeights::get().max_block.min(*RuntimeBlockLength::get().max.get(DispatchClass::Normal) as u64)).into(); } /// The call is allowed only if caller is a system contract. diff --git a/runtime/common/src/precompile/mock.rs b/runtime/common/src/precompile/mock.rs index 7cc3363574..b77bff9584 100644 --- a/runtime/common/src/precompile/mock.rs +++ b/runtime/common/src/precompile/mock.rs @@ -244,7 +244,8 @@ parameter_types! { pub const GetStableCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::AUSD); pub DefaultFeeSwapPathList: Vec> = vec![vec![CurrencyId::Token(TokenSymbol::AUSD), CurrencyId::Token(TokenSymbol::ACA)]]; pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::one(); - pub OperationalFeeMultiplier: u8 = 5; + pub OperationalFeeMultiplier: u64 = 5; + pub MaxTipsOfPriority: Balance = 1000; } impl module_transaction_payment::Config for Test { @@ -255,6 +256,7 @@ impl module_transaction_payment::Config for Test { type OnTransactionPayment = (); type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); type DEX = (); diff --git a/runtime/integration-tests/src/runtime.rs b/runtime/integration-tests/src/runtime.rs index 4228e238af..456f99f9b8 100644 --- a/runtime/integration-tests/src/runtime.rs +++ b/runtime/integration-tests/src/runtime.rs @@ -276,3 +276,8 @@ mod mandala_only_tests { }); } } + +#[test] +fn check_max_tips_of_priority() { + assert_eq!(MaxTipsOfPriority::get(), 204_358_782_087); // 0.2 KAR/ACA +} diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 5a15659465..7fc83e4441 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -49,11 +49,11 @@ mod mandala_imports { CollatorSelection, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DealWithFees, DefaultExchangeRate, Dex, EmergencyShutdown, EnabledTradingPairs, Event, EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, HomaLite, Honzon, IdleScheduler, Loans, - MinRewardDistributeAmount, MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, NetworkId, - NftPalletId, OneDay, Origin, OriginCaller, PalletCurrency, ParachainInfo, ParachainSystem, Proxy, ProxyType, - RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionKeys, SessionManager, SevenDays, System, - Timestamp, TokenSymbol, Tokens, TransactionPayment, TreasuryAccount, TreasuryPalletId, UncheckedExtrinsic, - Utility, Vesting, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, + MaxTipsOfPriority, MinRewardDistributeAmount, MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, + NetworkId, NftPalletId, OneDay, Origin, OriginCaller, PalletCurrency, ParachainInfo, ParachainSystem, Proxy, + ProxyType, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionKeys, SessionManager, SevenDays, + System, Timestamp, TokenSymbol, Tokens, TransactionPayment, TreasuryAccount, TreasuryPalletId, + UncheckedExtrinsic, Utility, Vesting, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, }; pub use runtime_common::{cent, dollar, millicent, ACA, AUSD, DOT, LDOT}; @@ -77,9 +77,9 @@ mod karura_imports { AuctionManager, Authority, AuthoritysOriginId, Balance, Balances, BlockNumber, Call, CdpEngine, CdpTreasury, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DefaultExchangeRate, Dex, EmergencyShutdown, Event, EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, - HomaLite, Honzon, IdleScheduler, KaruraFoundationAccounts, Loans, MinimumDebitValue, MultiLocation, - NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, - ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, + HomaLite, Honzon, IdleScheduler, KaruraFoundationAccounts, Loans, MaxTipsOfPriority, MinimumDebitValue, + MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, + ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, @@ -116,11 +116,12 @@ mod acala_imports { AuctionManager, Authority, AuthoritysOriginId, Balance, Balances, BlockNumber, Call, CdpEngine, CdpTreasury, CreateClassDeposit, CreateTokenDeposit, Currencies, CurrencyId, CurrencyIdConvert, DataDepositPerByte, DefaultExchangeRate, Dex, EmergencyShutdown, Event, EvmAccounts, ExistentialDeposits, Get, GetNativeCurrencyId, - HomaLite, Honzon, IdleScheduler, Loans, MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, - NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, ParachainInfo, ParachainSystem, - PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, - Scheduler, Session, SessionManager, SevenDays, System, Timestamp, TokenSymbol, Tokens, TreasuryPalletId, - Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, + HomaLite, Honzon, IdleScheduler, Loans, MaxTipsOfPriority, MinimumDebitValue, MultiLocation, + NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, + ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, + RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, + TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, + NFT, }; pub use frame_support::parameter_types; pub use primitives::TradingPair; diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index b6f7a5a405..8b75ae9019 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -111,10 +111,11 @@ pub use runtime_common::{ EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, EnsureRootOrThreeFourthsGeneralCouncil, EnsureRootOrTwoThirdsGeneralCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, FinancialCouncilInstance, FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, - GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, - OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, RelayChainBlockNumberProvider, - RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, SystemContractsFilter, TechnicalCommitteeInstance, - TechnicalCommitteeMembershipInstance, TimeStampedPrice, BNC, KAR, KSM, KUSD, LKSM, PHA, RENBTC, VSKSM, + GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, + OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, + RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, + SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, BNC, + KAR, KSM, KUSD, LKSM, PHA, RENBTC, VSKSM, }; mod authority; @@ -1106,7 +1107,6 @@ parameter_types! { vec![LKSM, KSM, KAR], vec![BNC, KUSD, KSM, KAR], ]; - pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1131,6 +1131,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; type DEX = Dex; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index 460a8a9fd0..bb3945f4f3 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -119,10 +119,11 @@ pub use runtime_common::{ EnsureRootOrThreeFourthsGeneralCouncil, EnsureRootOrTwoThirdsGeneralCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, FinancialCouncilInstance, FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, GeneralCouncilMembershipInstance, - HomaCouncilInstance, HomaCouncilMembershipInstance, OffchainSolutionWeightLimit, OperatorMembershipInstanceAcala, - Price, ProxyType, Rate, Ratio, RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, - RuntimeBlockWeights, SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, - TimeStampedPrice, ACA, AUSD, DOT, LDOT, RENBTC, + HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, OffchainSolutionWeightLimit, + OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, + RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, + SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, ACA, + AUSD, DOT, LDOT, RENBTC, }; /// Import the stable_asset pallet. @@ -1123,7 +1124,6 @@ impl module_transaction_pause::Config for Runtime { parameter_types! { // Sort by fee charge order pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![AUSD, LDOT], vec![AUSD, DOT], vec![AUSD, RENBTC]]; - pub OperationalFeeMultiplier: u8 = 5; } type NegativeImbalance = >::NegativeImbalance; @@ -1155,6 +1155,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = TargetedFeeAdjustment; type DEX = Dex; From 1f891c49c0eefd0fa3fe97b9b299c92376a975dc Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Tue, 14 Dec 2021 22:44:35 +0800 Subject: [PATCH 3/5] add TipPerWeightStep and integration tests --- modules/transaction-payment/src/lib.rs | 19 ++- modules/transaction-payment/src/mock.rs | 2 + runtime/acala/src/lib.rs | 23 ++-- runtime/common/src/lib.rs | 37 ++++-- runtime/common/src/precompile/mock.rs | 2 + runtime/integration-tests/src/runtime.rs | 143 ++++++++++++++++++++++- runtime/integration-tests/src/setup.rs | 19 ++- runtime/karura/src/lib.rs | 23 ++-- runtime/mandala/src/lib.rs | 5 +- 9 files changed, 224 insertions(+), 49 deletions(-) diff --git a/modules/transaction-payment/src/lib.rs b/modules/transaction-payment/src/lib.rs index 15e58019f0..01567d70b1 100644 --- a/modules/transaction-payment/src/lib.rs +++ b/modules/transaction-payment/src/lib.rs @@ -43,8 +43,8 @@ use primitives::{Balance, CurrencyId, ReserveIdentifier}; use scale_info::TypeInfo; use sp_runtime::{ traits::{ - Bounded, CheckedSub, Convert, DispatchInfoOf, One, PostDispatchInfoOf, SaturatedConversion, Saturating, - SignedExtension, UniqueSaturatedInto, Zero, + Bounded, CheckedDiv, CheckedSub, Convert, DispatchInfoOf, One, PostDispatchInfoOf, SaturatedConversion, + Saturating, SignedExtension, UniqueSaturatedInto, Zero, }, transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransaction, @@ -272,6 +272,10 @@ pub mod module { #[pallet::constant] type OperationalFeeMultiplier: Get; + /// The minimum value of tips per weight. + #[pallet::constant] + type TipPerWeightStep: Get>; + /// The maximum value of tips that affect the priority. /// Set the maximum value of tips to prevent affecting the unsigned extrinsic. #[pallet::constant] @@ -780,7 +784,16 @@ where let max_tx_per_block = max_tx_per_block_length .min(max_tx_per_block_weight) .saturated_into::>(); - let max_reward = |val: PalletBalanceOf| val.saturating_mul(max_tx_per_block); + // tipPerWeight = tipPerWight / TipPerWeightStep * TipPerWeightStep + // = tip / bounded_{weight|length} / TipPerWeightStep * TipPerWeightStep + // priority = tipPerWeight * max_block_{weight|length} + // MaxTipsOfPriority = 10_000 KAR/ACA = 10^16. + // `MaxTipsOfPriority * max_block_{weight|length}` will overflow, so div `TipPerWeightStep` here. + let max_reward = |val: PalletBalanceOf| { + val.checked_div(&T::TipPerWeightStep::get()) + .expect("TipPerWeightStep is non-zero; qed") + .saturating_mul(max_tx_per_block) + }; // To distribute no-tip transactions a little bit, we increase the tip value by one. // This means that given two transactions without a tip, smaller one will be preferred. diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index 02e49b68fe..e3da7917b5 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -180,6 +180,7 @@ parameter_types! { pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::saturating_from_rational(1, 2); pub static TransactionByteFee: u128 = 1; pub OperationalFeeMultiplier: u64 = 5; + pub TipPerWeightStep: u128 = 1; pub MaxTipsOfPriority: u128 = 1000; pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![DOT, AUSD, ACA]]; } @@ -229,6 +230,7 @@ impl Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type TipPerWeightStep = TipPerWeightStep; type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = (); diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index 78beba75d0..55859b94f8 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -103,18 +103,18 @@ pub use primitives::{ AccountIndex, Address, Amount, AuctionId, AuthoritysOriginId, Balance, BlockNumber, CurrencyId, DataProviderId, EraIndex, Hash, Moment, Nonce, ReserveIdentifier, Share, Signature, TokenSymbol, TradingPair, }; -use runtime_common::AcalaDropAssets; pub use runtime_common::{ - cent, dollar, microcent, millicent, EnsureRootOrAllGeneralCouncil, EnsureRootOrAllTechnicalCommittee, - EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, EnsureRootOrHalfHomaCouncil, - EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, EnsureRootOrThreeFourthsGeneralCouncil, - EnsureRootOrTwoThirdsGeneralCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, - FinancialCouncilInstance, FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, - GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, - OffchainSolutionWeightLimit, OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, - Ratio, RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, - SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, ACA, - AUSD, DOT, LDOT, RENBTC, + cent, dollar, microcent, millicent, AcalaDropAssets, EnsureRootOrAllGeneralCouncil, + EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, + EnsureRootOrHalfHomaCouncil, EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, + EnsureRootOrThreeFourthsGeneralCouncil, EnsureRootOrTwoThirdsGeneralCouncil, + EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, FinancialCouncilInstance, + FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, GeneralCouncilMembershipInstance, + HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, OffchainSolutionWeightLimit, + OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, + RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, + SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, + TipPerWeightStep, ACA, AUSD, DOT, LDOT, RENBTC, }; mod authority; @@ -1115,6 +1115,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type TipPerWeightStep = TipPerWeightStep; type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index dc48c3a887..6e9c05ea28 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -68,19 +68,23 @@ pub type TimeStampedPrice = orml_oracle::TimestampedValue Operational tx -> Unsigned tx -> Signed normal tx - pub const OperationalFeeMultiplier: u64 = 1_000_000_000u64; - // 1_504_592_000u64 from https://github.com/AcalaNetwork/Acala/blob/bda4d430cbecebf8720d700b976875d0d805ceca/runtime/integration-tests/src/runtime.rs#L275 - const MinOperationalPriority: TransactionPriority = 1_500_000_000u64 * OperationalFeeMultiplier::get() as u64; - pub const CdpEngineUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 1000; - pub const AuctionManagerUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 2000; - pub const RenvmBridgeUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 3000; - const MaxNormalPriority: TransactionPriority = MinOperationalPriority::get() / 2; // 50% - pub MaxTipsOfPriority: Balance = (MaxNormalPriority::get() / RuntimeBlockWeights::get().max_block.min(*RuntimeBlockLength::get().max.get(DispatchClass::Normal) as u64)).into(); + // Ensure `max_normal_priority < MinOperationalPriority / 2` + pub TipPerWeightStep: Balance = cent(KAR); // 0.01 KAR/ACA + pub MaxTipsOfPriority: Balance = 10_000 * dollar(KAR); // 10_000 KAR/ACA + pub const OperationalFeeMultiplier: u64 = 100_000_000_000_000u64; + // MinOperationalPriority = final_fee_min * OperationalFeeMultiplier / TipPerWeightStep + // 1_500_000_000u128 from https://github.com/AcalaNetwork/Acala/blob/bda4d430cbecebf8720d700b976875d0d805ceca/runtime/integration-tests/src/runtime.rs#L275 + MinOperationalPriority: TransactionPriority = (1_500_000_000u128 * OperationalFeeMultiplier::get() as u128 / TipPerWeightStep::get()) + .try_into() + .expect("Check that there is no overflow here"); + pub CdpEngineUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 1000; + pub AuctionManagerUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 2000; + pub RenvmBridgeUnsignedPriority: TransactionPriority = MinOperationalPriority::get() - 3000; } /// The call is allowed only if caller is a system contract. @@ -429,4 +433,15 @@ mod tests { min_blocked_addr[SYSTEM_CONTRACT_ADDRESS_PREFIX.len() - 1] = 1u8; assert!(!SystemContractsFilter::is_allowed(min_blocked_addr.into())); } + + #[test] + fn check_max_normal_priority() { + let max_normal_priority: TransactionPriority = (MaxTipsOfPriority::get() / TipPerWeightStep::get() + * RuntimeBlockWeights::get() + .max_block + .min(*RuntimeBlockLength::get().max.get(DispatchClass::Normal) as u64) as u128) + .try_into() + .expect("Check that there is no overflow here"); + assert!(max_normal_priority < MinOperationalPriority::get() / 2); // 50% + } } diff --git a/runtime/common/src/precompile/mock.rs b/runtime/common/src/precompile/mock.rs index b77bff9584..129ce5359c 100644 --- a/runtime/common/src/precompile/mock.rs +++ b/runtime/common/src/precompile/mock.rs @@ -245,6 +245,7 @@ parameter_types! { pub DefaultFeeSwapPathList: Vec> = vec![vec![CurrencyId::Token(TokenSymbol::AUSD), CurrencyId::Token(TokenSymbol::ACA)]]; pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::one(); pub OperationalFeeMultiplier: u64 = 5; + pub TipPerWeightStep: Balance = 1; pub MaxTipsOfPriority: Balance = 1000; } @@ -256,6 +257,7 @@ impl module_transaction_payment::Config for Test { type OnTransactionPayment = (); type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type TipPerWeightStep = TipPerWeightStep; type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); diff --git a/runtime/integration-tests/src/runtime.rs b/runtime/integration-tests/src/runtime.rs index 456f99f9b8..f5d1de0dbf 100644 --- a/runtime/integration-tests/src/runtime.rs +++ b/runtime/integration-tests/src/runtime.rs @@ -249,8 +249,17 @@ fn parachain_subaccounts_are_unique() { #[cfg(feature = "with-mandala-runtime")] mod mandala_only_tests { use super::*; + use ecosystem_renvm_bridge::EcdsaSignature; + use frame_support::dispatch::GetDispatchInfo; + use hex_literal::hex; + use mandala_runtime::RenVmBridge; + use module_transaction_payment::ChargeTransactionPayment; use pallet_transaction_payment::InclusionFee; - use sp_runtime::traits::Extrinsic; + use sp_runtime::{ + traits::{Extrinsic, SignedExtension, ValidateUnsigned}, + transaction_validity::{TransactionSource, ValidTransaction}, + }; + #[test] fn check_transaction_fee_for_empty_remark() { ExtBuilder::default().build().execute_with(|| { @@ -275,9 +284,133 @@ mod mandala_only_tests { assert_eq!(total_fee, 1_504_592_000); }); } -} -#[test] -fn check_max_tips_of_priority() { - assert_eq!(MaxTipsOfPriority::get(), 204_358_782_087); // 0.2 KAR/ACA + #[test] + fn check_tx_priority() { + ExtBuilder::default() + .balances(vec![ + (alice(), NATIVE_CURRENCY, 20_000 * dollar(NATIVE_CURRENCY)), + ]) + .build().execute_with(|| { + // Ensure tx priority order: + // Inherent -> Operational tx -> Unsigned tx -> Signed normal tx + let call = Call::System(frame_system::Call::remark { remark: vec![] }); + let bytes = UncheckedExtrinsic::new(call.clone().into(), None).expect("This should not fail").encode(); + + // tips = 0 + assert_eq!( + ChargeTransactionPayment::::from(0).validate( + &alice(), + &call.clone(), + &call.get_dispatch_info(), + bytes.len() + ), + Ok(ValidTransaction { + priority: 0, + requires: vec![], + provides: vec![], + longevity: 18_446_744_073_709_551_615, + propagate: true, + }) + ); + + // tips = TipPerWeightStep + assert_eq!( + ChargeTransactionPayment::::from(TipPerWeightStep::get()).validate( + &alice(), + &call.clone(), + &call.get_dispatch_info(), + bytes.len() + ), + Ok(ValidTransaction { + priority: 734_003, + requires: vec![], + provides: vec![], + longevity: 18_446_744_073_709_551_615, + propagate: true, + }) + ); + + // tips = TipPerWeightStep + 1 + assert_eq!( + ChargeTransactionPayment::::from(TipPerWeightStep::get() + 1).validate( + &alice(), + &call.clone(), + &call.get_dispatch_info(), + bytes.len() + ), + Ok(ValidTransaction { + priority: 734_003, + requires: vec![], + provides: vec![], + longevity: 18_446_744_073_709_551_615, + propagate: true, + }) + ); + + // tips = MaxTipsOfPriority + 1 + assert_eq!( + ChargeTransactionPayment::::from(MaxTipsOfPriority::get() + 1).validate( + &alice(), + &call.clone(), + &call.get_dispatch_info(), + bytes.len() + ), + Ok(ValidTransaction { + priority: 734_003_000_000, + requires: vec![], + provides: vec![], + longevity: 18_446_744_073_709_551_615, + propagate: true, + }) + ); + + // tips = 0 + // unsigned extrinsic + let sig = EcdsaSignature::from_slice(&hex!["defda6eef01da2e2a90ce30ba73e90d32204ae84cae782b485f01d16b69061e0381a69cafed3deb6112af044c42ed0f7c73ee0eec7b533334d31a06db50fc40e1b"]); + let call = ecosystem_renvm_bridge::Call::mint { + who: hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"].into(), + p_hash: hex!["67028f26328144de6ef80b8cd3b05e0cefb488762c340d1574c0542f752996cb"], + amount: 93963, + n_hash: hex!["f6a75cc370a2dda6dfc8d016529766bb6099d7fa0d787d9fe5d3a7e60c9ac2a0"], + sig: sig.clone(), + }; + + assert_eq!( + RenVmBridge::validate_unsigned( + TransactionSource::Local, + &call, + ), + Ok(ValidTransaction { + priority: 14_999_999_997_000, + requires: vec![], + provides: vec![("renvm-bridge", sig).encode()], + longevity: 64, + propagate: true, + }) + ); + + // tips = 0 + // operational extrinsic + let call = Call::Sudo(pallet_sudo::Call::sudo { call: Box::new(module_emergency_shutdown::Call::emergency_shutdown { }.into()) }); + let bytes = UncheckedExtrinsic::new(call.clone().into(), None).expect("This should not fail").encode(); + + assert_eq!( + ChargeTransactionPayment::::from(0).validate( + &alice(), + &call.clone(), + &call.get_dispatch_info(), + bytes.len() + ), + Ok(ValidTransaction { + priority: 43_824_742_400_000_000, + requires: vec![], + provides: vec![], + longevity: 18_446_744_073_709_551_615, + propagate: true, + }) + ); + + }); + } } diff --git a/runtime/integration-tests/src/setup.rs b/runtime/integration-tests/src/setup.rs index 7fc83e4441..c2c8d0cfac 100644 --- a/runtime/integration-tests/src/setup.rs +++ b/runtime/integration-tests/src/setup.rs @@ -52,8 +52,8 @@ mod mandala_imports { MaxTipsOfPriority, MinRewardDistributeAmount, MinimumDebitValue, MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, PalletCurrency, ParachainInfo, ParachainSystem, Proxy, ProxyType, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionKeys, SessionManager, SevenDays, - System, Timestamp, TokenSymbol, Tokens, TransactionPayment, TreasuryAccount, TreasuryPalletId, - UncheckedExtrinsic, Utility, Vesting, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, + System, Timestamp, TipPerWeightStep, TokenSymbol, Tokens, TransactionPayment, TreasuryAccount, + TreasuryPalletId, UncheckedExtrinsic, Utility, Vesting, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, NFT, }; pub use runtime_common::{cent, dollar, millicent, ACA, AUSD, DOT, LDOT}; @@ -81,8 +81,8 @@ mod karura_imports { MultiLocation, NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, - TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, + TipPerWeightStep, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, + XcmUnbondFee, EVM, NFT, }; pub use primitives::TradingPair; pub use runtime_common::{cent, dollar, millicent, KAR, KSM, KUSD, LKSM}; @@ -120,8 +120,8 @@ mod acala_imports { NativeTokenExistentialDeposit, NetworkId, NftPalletId, OneDay, Origin, OriginCaller, ParachainAccount, ParachainInfo, ParachainSystem, PolkadotXcm, Proxy, ProxyType, RelayChainBlockNumberProvider, RelayChainSovereignSubAccount, Runtime, Scheduler, Session, SessionManager, SevenDays, System, Timestamp, - TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, XcmUnbondFee, EVM, - NFT, + TipPerWeightStep, TokenSymbol, Tokens, TreasuryPalletId, Utility, Vesting, XTokens, XcmConfig, XcmExecutor, + XcmUnbondFee, EVM, NFT, }; pub use frame_support::parameter_types; pub use primitives::TradingPair; @@ -234,6 +234,13 @@ impl ExtBuilder { let existential_deposit = NativeTokenExistentialDeposit::get(); let initial_enabled_trading_pairs = EnabledTradingPairs::get(); + #[cfg(feature = "with-mandala-runtime")] + ecosystem_renvm_bridge::GenesisConfig { + ren_vm_public_key: hex_literal::hex!["4b939fc8ade87cb50b78987b1dda927460dc456a"], + } + .assimilate_storage::(&mut t) + .unwrap(); + module_dex::GenesisConfig:: { initial_enabled_trading_pairs: initial_enabled_trading_pairs, initial_listing_trading_pairs: Default::default(), diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 8b75ae9019..9645fc3663 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -104,18 +104,18 @@ pub use primitives::{ AccountIndex, Address, Amount, AuctionId, AuthoritysOriginId, Balance, BlockNumber, CurrencyId, DataProviderId, EraIndex, Hash, Moment, Nonce, ReserveIdentifier, Share, Signature, TokenSymbol, TradingPair, }; -use runtime_common::AcalaDropAssets; pub use runtime_common::{ - cent, dollar, microcent, millicent, EnsureRootOrAllGeneralCouncil, EnsureRootOrAllTechnicalCommittee, - EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, EnsureRootOrHalfHomaCouncil, - EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, EnsureRootOrThreeFourthsGeneralCouncil, - EnsureRootOrTwoThirdsGeneralCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, - FinancialCouncilInstance, FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, - GeneralCouncilMembershipInstance, HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, - OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, - RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, - SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, BNC, - KAR, KSM, KUSD, LKSM, PHA, RENBTC, VSKSM, + cent, dollar, microcent, millicent, AcalaDropAssets, EnsureRootOrAllGeneralCouncil, + EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfFinancialCouncil, EnsureRootOrHalfGeneralCouncil, + EnsureRootOrHalfHomaCouncil, EnsureRootOrOneGeneralCouncil, EnsureRootOrOneThirdsTechnicalCommittee, + EnsureRootOrThreeFourthsGeneralCouncil, EnsureRootOrTwoThirdsGeneralCouncil, + EnsureRootOrTwoThirdsTechnicalCommittee, ExchangeRate, FinancialCouncilInstance, + FinancialCouncilMembershipInstance, GasToWeight, GeneralCouncilInstance, GeneralCouncilMembershipInstance, + HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, OperationalFeeMultiplier, + OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, RelayChainBlockNumberProvider, + RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, SystemContractsFilter, TechnicalCommitteeInstance, + TechnicalCommitteeMembershipInstance, TimeStampedPrice, TipPerWeightStep, BNC, KAR, KSM, KUSD, LKSM, PHA, RENBTC, + VSKSM, }; mod authority; @@ -1131,6 +1131,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type TipPerWeightStep = TipPerWeightStep; type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index bb3945f4f3..9c420e842b 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -122,8 +122,8 @@ pub use runtime_common::{ HomaCouncilInstance, HomaCouncilMembershipInstance, MaxTipsOfPriority, OffchainSolutionWeightLimit, OperationalFeeMultiplier, OperatorMembershipInstanceAcala, Price, ProxyType, Rate, Ratio, RelayChainBlockNumberProvider, RelayChainSubAccountId, RuntimeBlockLength, RuntimeBlockWeights, - SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, ACA, - AUSD, DOT, LDOT, RENBTC, + SystemContractsFilter, TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, TimeStampedPrice, + TipPerWeightStep, ACA, AUSD, DOT, LDOT, RENBTC, }; /// Import the stable_asset pallet. @@ -1155,6 +1155,7 @@ impl module_transaction_payment::Config for Runtime { type OnTransactionPayment = DealWithFees; type TransactionByteFee = TransactionByteFee; type OperationalFeeMultiplier = OperationalFeeMultiplier; + type TipPerWeightStep = TipPerWeightStep; type MaxTipsOfPriority = MaxTipsOfPriority; type WeightToFee = WeightToFee; type FeeMultiplierUpdate = TargetedFeeAdjustment; From 0833076fda25258557d15d087e7ff3cc42dcbf37 Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Wed, 15 Dec 2021 10:14:34 +0800 Subject: [PATCH 4/5] add unit test --- modules/transaction-payment/src/mock.rs | 9 +++- modules/transaction-payment/src/tests.rs | 68 +++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/modules/transaction-payment/src/mock.rs b/modules/transaction-payment/src/mock.rs index e3da7917b5..cfbbeed38f 100644 --- a/modules/transaction-payment/src/mock.rs +++ b/modules/transaction-payment/src/mock.rs @@ -180,7 +180,7 @@ parameter_types! { pub MaxSwapSlippageCompareToOracle: Ratio = Ratio::saturating_from_rational(1, 2); pub static TransactionByteFee: u128 = 1; pub OperationalFeeMultiplier: u64 = 5; - pub TipPerWeightStep: u128 = 1; + pub static TipPerWeightStep: u128 = 1; pub MaxTipsOfPriority: u128 = 1000; pub DefaultFeeSwapPathList: Vec> = vec![vec![AUSD, ACA], vec![DOT, AUSD, ACA]]; } @@ -282,6 +282,7 @@ pub struct ExtBuilder { base_weight: u64, byte_fee: u128, weight_to_fee: u128, + tip_per_weight_step: u128, native_balances: Vec<(AccountId, Balance)>, } @@ -292,6 +293,7 @@ impl Default for ExtBuilder { base_weight: 0, byte_fee: 2, weight_to_fee: 1, + tip_per_weight_step: 1, native_balances: vec![], } } @@ -310,6 +312,10 @@ impl ExtBuilder { self.weight_to_fee = weight_to_fee; self } + pub fn tip_per_weight_step(mut self, tip_per_weight_step: u128) -> Self { + self.tip_per_weight_step = tip_per_weight_step; + self + } pub fn one_hundred_thousand_for_alice_n_charlie(mut self) -> Self { self.native_balances = vec![(ALICE, 100000), (CHARLIE, 100000)]; self @@ -318,6 +324,7 @@ impl ExtBuilder { EXTRINSIC_BASE_WEIGHT.with(|v| *v.borrow_mut() = self.base_weight); TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + TIP_PER_WEIGHT_STEP.with(|v| *v.borrow_mut() = self.tip_per_weight_step); } pub fn build(self) -> sp_io::TestExternalities { self.set_constants(); diff --git a/modules/transaction-payment/src/tests.rs b/modules/transaction-payment/src/tests.rs index 28e97cab3c..046c2e9a5a 100644 --- a/modules/transaction-payment/src/tests.rs +++ b/modules/transaction-payment/src/tests.rs @@ -631,7 +631,73 @@ fn no_tip_has_some_priority() { } #[test] -fn max_tip_has_some_priority() { +fn min_tip_has_same_priority() { + let tip = 100; + let len = 10; + + ExtBuilder::default() + .tip_per_weight_step(tip) + .one_hundred_thousand_for_alice_n_charlie() + .build() + .execute_with(|| { + let normal = DispatchInfo { + weight: 100, + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(0) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 0); + + let priority = ChargeTransactionPayment::(tip - 2) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 0); + + let priority = ChargeTransactionPayment::(tip - 1) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10); + + let priority = ChargeTransactionPayment::(tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10); + + let priority = ChargeTransactionPayment::(2 * tip - 2) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 10); + + let priority = ChargeTransactionPayment::(2 * tip - 1) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 20); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&ALICE, CALL, &normal, len) + .unwrap() + .priority; + // max_tx_per_block = 10 + assert_eq!(priority, 20); + }); +} + +#[test] +fn max_tip_has_same_priority() { let tip = 1000; let len = 10; From 084b08bb36b9b48aeb752f2a80dc70cb7084f95f Mon Sep 17 00:00:00 2001 From: zjb0807 Date: Wed, 15 Dec 2021 10:27:39 +0800 Subject: [PATCH 5/5] Update modules/transaction-payment/src/lib.rs Co-authored-by: Xiliang Chen --- modules/transaction-payment/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/transaction-payment/src/lib.rs b/modules/transaction-payment/src/lib.rs index 01567d70b1..723140ba50 100644 --- a/modules/transaction-payment/src/lib.rs +++ b/modules/transaction-payment/src/lib.rs @@ -272,7 +272,7 @@ pub mod module { #[pallet::constant] type OperationalFeeMultiplier: Get; - /// The minimum value of tips per weight. + /// The step amount of tips required to effect transaction priority. #[pallet::constant] type TipPerWeightStep: Get>;