diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index bf55885363..75809e0ed6 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -141,27 +141,11 @@ impl Module where // a very very little potential gain in the future. let dispatch_info = ::get_dispatch_info(&unchecked_extrinsic); - let partial_fee = - >::compute_fee(len, &dispatch_info, 0u32.into()); + let partial_fee = Self::compute_fee(len, &dispatch_info, 0u32.into()); let DispatchInfo { weight, class, .. } = dispatch_info; RuntimeDispatchInfo { weight, class, partial_fee } } -} - -/// Require the transactor pay for themselves and maybe include a tip to gain additional priority -/// in the queue. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct ChargeTransactionPayment(#[codec(compact)] BalanceOf); - -impl ChargeTransactionPayment where - T::Call: Dispatchable, - BalanceOf: Send + Sync, -{ - /// utility constructor. Used only in client/factory code. - pub fn from(fee: BalanceOf) -> Self { - Self(fee) - } /// Compute the final fee value for a particular transaction. /// @@ -186,12 +170,11 @@ impl ChargeTransactionPayment where let len = >::from(len); let per_byte = T::TransactionByteFee::get(); let len_fee = per_byte.saturating_mul(len); - let weight_fee = Self::compute_weight_fee(info.weight); + let unadjusted_weight_fee = Self::weight_to_fee(info.weight); // the adjustable part of the fee - let adjustable_fee = len_fee.saturating_add(weight_fee); + let adjustable_fee = len_fee.saturating_add(unadjusted_weight_fee); let targeted_fee_adjustment = NextFeeMultiplier::get(); - // adjusted_fee = adjustable_fee + (adjustable_fee * targeted_fee_adjustment) let adjusted_fee = targeted_fee_adjustment.saturated_multiply_accumulate(adjustable_fee.saturated_into()); let base_fee = T::TransactionBaseFee::get(); @@ -201,12 +184,39 @@ impl ChargeTransactionPayment where } } - fn compute_weight_fee(weight: Weight) -> BalanceOf { + /// Compute the fee for the specified weight. + /// + /// This fee is already adjusted by the per block fee adjustment factor and is therefore + /// the share that the weight contributes to the overall fee of a transaction. + pub fn weight_to_fee_with_adjustment(weight: Weight) -> BalanceOf where + BalanceOf: From + { + NextFeeMultiplier::get().saturated_multiply_accumulate( + Self::weight_to_fee(weight) + ) + } + + fn weight_to_fee(weight: Weight) -> BalanceOf { // cap the weight to the maximum defined in runtime, otherwise it will be the // `Bounded` maximum of its data type, which is not desired. let capped_weight = weight.min(::MaximumBlockWeight::get()); T::WeightToFee::convert(capped_weight) } +} + +/// Require the transactor pay for themselves and maybe include a tip to gain additional priority +/// in the queue. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct ChargeTransactionPayment(#[codec(compact)] BalanceOf); + +impl ChargeTransactionPayment where + T::Call: Dispatchable, + BalanceOf: Send + Sync, +{ + /// utility constructor. Used only in client/factory code. + pub fn from(fee: BalanceOf) -> Self { + Self(fee) + } fn withdraw_fee( &self, @@ -215,7 +225,7 @@ impl ChargeTransactionPayment where len: usize, ) -> Result<(BalanceOf, Option>), TransactionValidityError> { let tip = self.0; - let fee = Self::compute_fee(len as u32, info, tip); + let fee = Module::::compute_fee(len as u32, info, tip); // Only mess with balances if fee is not zero. if fee.is_zero() { @@ -250,7 +260,7 @@ impl sp_std::fmt::Debug for ChargeTransactionPayment } impl SignedExtension for ChargeTransactionPayment where - BalanceOf: Send + Sync, + BalanceOf: Send + Sync + From, T::Call: Dispatchable, { const IDENTIFIER: &'static str = "ChargeTransactionPayment"; @@ -296,7 +306,7 @@ impl SignedExtension for ChargeTransactionPayment whe ) -> Result<(), TransactionValidityError> { let (tip, who, imbalance) = pre; if let Some(payed) = imbalance { - let refund = Self::compute_weight_fee(post_info.calc_unspent(info)); + let refund = Module::::weight_to_fee_with_adjustment(post_info.calc_unspent(info)); let actual_payment = match T::Currency::deposit_into_existing(&who, refund) { Ok(refund_imbalance) => { // The refund cannot be larger than the up front payed max weight. @@ -541,6 +551,33 @@ mod tests { }); } + #[test] + fn signed_extension_transaction_payment_multiplied_refund_works() { + ExtBuilder::default() + .balance_factor(10) + .base_fee(5) + .build() + .execute_with(|| + { + let len = 10; + NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(100), len) + .unwrap(); + // 5 base fee, 3/2 * 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 15 - 150 - 5); + + assert!( + ChargeTransactionPayment:: + ::post_dispatch(pre, &info_from_weight(100), &post_info_from_weight(50), len, &Ok(())) + .is_ok() + ); + // 75 (3/2 of the returned 50 units of weight ) is refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 15 - 75 - 5); + }); + } + #[test] fn signed_extension_transaction_payment_is_bounded() { ExtBuilder::default() @@ -676,25 +713,25 @@ mod tests { class: DispatchClass::Operational, pays_fee: false, }; - assert_eq!(ChargeTransactionPayment::::compute_fee(0, &dispatch_info, 10), 10); + assert_eq!(Module::::compute_fee(0, &dispatch_info, 10), 10); // No tip, only base fee works let dispatch_info = DispatchInfo { weight: 0, class: DispatchClass::Operational, pays_fee: true, }; - assert_eq!(ChargeTransactionPayment::::compute_fee(0, &dispatch_info, 0), 100); + assert_eq!(Module::::compute_fee(0, &dispatch_info, 0), 100); // Tip + base fee works - assert_eq!(ChargeTransactionPayment::::compute_fee(0, &dispatch_info, 69), 169); + assert_eq!(Module::::compute_fee(0, &dispatch_info, 69), 169); // Len (byte fee) + base fee works - assert_eq!(ChargeTransactionPayment::::compute_fee(42, &dispatch_info, 0), 520); + assert_eq!(Module::::compute_fee(42, &dispatch_info, 0), 520); // Weight fee + base fee works let dispatch_info = DispatchInfo { weight: 1000, class: DispatchClass::Operational, pays_fee: true, }; - assert_eq!(ChargeTransactionPayment::::compute_fee(0, &dispatch_info, 0), 1100); + assert_eq!(Module::::compute_fee(0, &dispatch_info, 0), 1100); }); } @@ -715,7 +752,7 @@ mod tests { class: DispatchClass::Operational, pays_fee: true, }; - assert_eq!(ChargeTransactionPayment::::compute_fee(0, &dispatch_info, 0), 100); + assert_eq!(Module::::compute_fee(0, &dispatch_info, 0), 100); // Everything works together :) let dispatch_info = DispatchInfo { @@ -727,7 +764,7 @@ mod tests { // adjustable fee = (123 * 1) + (456 * 10) = 4683 // adjusted fee = (4683 * .5) + 4683 = 7024.5 -> 7024 // final fee = 100 + 7024 + 789 tip = 7913 - assert_eq!(ChargeTransactionPayment::::compute_fee(456, &dispatch_info, 789), 7913); + assert_eq!(Module::::compute_fee(456, &dispatch_info, 789), 7913); }); } @@ -747,7 +784,7 @@ mod tests { pays_fee: true, }; assert_eq!( - ChargeTransactionPayment::::compute_fee( + Module::::compute_fee( ::max_value(), &dispatch_info, ::max_value()