Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
decouple transaction payment and currency (#6912)
Browse files Browse the repository at this point in the history
* wip: setup types

* fix types

* make tx payment pallet independent from balances

* fix dependent tests

* comments

* restructure a bit and  include more info

* clean up ugly phantom

* reduce complexity

* minor doc improvements

* use shorthand

* doc

* fix line lenght and style

* readd BalanceOf

* some clarifications and readability improvements

* move balance type to OnChargeTransaction

* remove noise

* fix style

* Apply suggestions from code review

improved documentation

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Improve naming and documentation

Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* always call withdraw_fee

* move NegativeImbalanceOf to payment module

* fix unused import

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
  • Loading branch information
3 people committed Oct 30, 2020
1 parent d5464dd commit 5d30787
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 78 deletions.
4 changes: 2 additions & 2 deletions bin/node-template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub use frame_support::{
constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},
},
};
use pallet_transaction_payment::CurrencyAdapter;

/// Import the template pallet.
pub use template;
Expand Down Expand Up @@ -249,8 +250,7 @@ parameter_types! {
}

impl pallet_transaction_payment::Trait for Runtime {
type Currency = Balances;
type OnTransactionPayment = ();
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<Balance>;
type FeeMultiplierUpdate = ();
Expand Down
5 changes: 2 additions & 3 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use pallet_grandpa::fg_primitives;
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
pub use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
pub use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment, CurrencyAdapter};
use pallet_session::{historical as pallet_session_historical};
use sp_inherents::{InherentData, CheckInherentsResult};
use static_assertions::const_assert;
Expand Down Expand Up @@ -360,8 +360,7 @@ parameter_types! {
}

impl pallet_transaction_payment::Trait for Runtime {
type Currency = Balances;
type OnTransactionPayment = DealWithFees;
type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<Balance>;
type FeeMultiplierUpdate =
Expand Down
4 changes: 2 additions & 2 deletions frame/balances/src/tests_composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use sp_io;
use frame_support::{impl_outer_origin, impl_outer_event, parameter_types};
use frame_support::traits::Get;
use frame_support::weights::{Weight, DispatchInfo, IdentityFee};
use pallet_transaction_payment::CurrencyAdapter;
use std::cell::RefCell;
use crate::{GenesisConfig, Module, Trait, decl_tests, tests::CallWithDispatchInfo};

Expand Down Expand Up @@ -97,8 +98,7 @@ parameter_types! {
pub const TransactionByteFee: u64 = 1;
}
impl pallet_transaction_payment::Trait for Test {
type Currency = Module<Test>;
type OnTransactionPayment = ();
type OnChargeTransaction = CurrencyAdapter<Module<Test>, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<u64>;
type FeeMultiplierUpdate = ();
Expand Down
4 changes: 2 additions & 2 deletions frame/balances/src/tests_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use frame_support::traits::{Get, StorageMapShim};
use frame_support::weights::{Weight, DispatchInfo, IdentityFee};
use std::cell::RefCell;
use crate::{GenesisConfig, Module, Trait, decl_tests, tests::CallWithDispatchInfo};
use pallet_transaction_payment::CurrencyAdapter;

use frame_system as system;
impl_outer_origin!{
Expand Down Expand Up @@ -97,8 +98,7 @@ parameter_types! {
pub const TransactionByteFee: u64 = 1;
}
impl pallet_transaction_payment::Trait for Test {
type Currency = Module<Test>;
type OnTransactionPayment = ();
type OnChargeTransaction = CurrencyAdapter<Module<Test>, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<u64>;
type FeeMultiplierUpdate = ();
Expand Down
4 changes: 2 additions & 2 deletions frame/executive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ mod tests {
traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons},
};
use frame_system::{Call as SystemCall, ChainContext, LastRuntimeUpgradeInfo};
use pallet_transaction_payment::CurrencyAdapter;
use pallet_balances::Call as BalancesCall;
use hex_literal::hex;
const TEST_KEY: &[u8] = &*b":test:key:";
Expand Down Expand Up @@ -632,8 +633,7 @@ mod tests {
pub const TransactionByteFee: Balance = 0;
}
impl pallet_transaction_payment::Trait for Runtime {
type Currency = Balances;
type OnTransactionPayment = ();
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = IdentityFee<Balance>;
type FeeMultiplierUpdate = ();
Expand Down
115 changes: 48 additions & 67 deletions frame/transaction-payment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@
//! - A means of updating the fee for the next block, via defining a multiplier, based on the
//! final state of the chain at the end of the previous block. This can be configured via
//! [`Trait::FeeMultiplierUpdate`]
//! - How the fees are paid via [`Trait::OnChargeTransaction`].

#![cfg_attr(not(feature = "std"), no_std)]

use sp_std::prelude::*;
use codec::{Encode, Decode};
use frame_support::{
decl_storage, decl_module,
traits::{Currency, Get, OnUnbalanced, ExistenceRequirement, WithdrawReasons, Imbalance},
traits::Get,
weights::{
Weight, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Pays, WeightToFeePolynomial,
WeightToFeeCoefficient,
Expand All @@ -46,23 +47,23 @@ use frame_support::{
use sp_runtime::{
FixedU128, FixedPointNumber, FixedPointOperand, Perquintill, RuntimeDebug,
transaction_validity::{
TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError,
TransactionValidity,
TransactionPriority, ValidTransaction, TransactionValidityError, TransactionValidity,
},
traits::{
Zero, Saturating, SignedExtension, SaturatedConversion, Convert, Dispatchable,
Saturating, SignedExtension, SaturatedConversion, Convert, Dispatchable,
DispatchInfoOf, PostDispatchInfoOf,
},
};
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;

mod payment;
pub use payment::*;

/// Fee multiplier.
pub type Multiplier = FixedU128;

type BalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
type NegativeImbalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
<<T as Trait>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;

/// A struct to update the weight multiplier per block. It implements `Convert<Multiplier,
/// Multiplier>`, meaning that it can convert the previous multiplier to the next one. This should
Expand Down Expand Up @@ -213,13 +214,13 @@ impl Default for Releases {
}

pub trait Trait: frame_system::Trait {
/// The currency type in which fees will be paid.
type Currency: Currency<Self::AccountId> + Send + Sync;

/// Handler for the unbalanced reduction when taking transaction fees. This is either one or
/// two separate imbalances, the first is the transaction fee paid, the second is the tip paid,
/// if any.
type OnTransactionPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Handler for withdrawing, refunding and depositing the transaction fee.
/// Transaction fees are withdrawn before the transaction is executed.
/// After the transaction was executed the transaction weight can be
/// adjusted, depending on the used resources by the transaction. If the
/// transaction weight is lower than expected, parts of the transaction fee
/// might be refunded. In the end the fees can be deposited.
type OnChargeTransaction: OnChargeTransaction<Self>;

/// The fee to be paid for making a transaction; the per-byte portion.
type TransactionByteFee: Get<BalanceOf<Self>>;
Expand Down Expand Up @@ -442,30 +443,21 @@ impl<T: Trait + Send + Sync> ChargeTransactionPayment<T> where
fn withdraw_fee(
&self,
who: &T::AccountId,
call: &T::Call,
info: &DispatchInfoOf<T::Call>,
len: usize,
) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {
) -> Result<
(
BalanceOf<T>,
<<T as Trait>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
),
TransactionValidityError,
> {
let tip = self.0;
let fee = Module::<T>::compute_fee(len as u32, info, tip);

// Only mess with balances if fee is not zero.
if fee.is_zero() {
return Ok((fee, None));
}

match T::Currency::withdraw(
who,
fee,
if tip.is_zero() {
WithdrawReasons::TRANSACTION_PAYMENT
} else {
WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP
},
ExistenceRequirement::KeepAlive,
) {
Ok(imbalance) => Ok((fee, Some(imbalance))),
Err(_) => Err(InvalidTransaction::Payment.into()),
}
<<T as Trait>::OnChargeTransaction as OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)
.map(|i| (fee, i))
}

/// Get an appropriate priority for a transaction with the given length and info.
Expand Down Expand Up @@ -505,17 +497,24 @@ impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> whe
type AccountId = T::AccountId;
type Call = T::Call;
type AdditionalSigned = ();
type Pre = (BalanceOf<T>, Self::AccountId, Option<NegativeImbalanceOf<T>>, BalanceOf<T>);
type Pre = (
// tip
BalanceOf<T>,
// who paid the fee
Self::AccountId,
// imbalance resulting from withdrawing the fee
<<T as Trait>::OnChargeTransaction as OnChargeTransaction<T>>::LiquidityInfo,
);
fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }

fn validate(
&self,
who: &Self::AccountId,
_call: &Self::Call,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize,
) -> TransactionValidity {
let (fee, _) = self.withdraw_fee(who, info, len)?;
let (fee, _) = self.withdraw_fee(who, call, info, len)?;
Ok(ValidTransaction {
priority: Self::get_priority(len, info, fee),
..Default::default()
Expand All @@ -525,12 +524,12 @@ impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> whe
fn pre_dispatch(
self,
who: &Self::AccountId,
_call: &Self::Call,
call: &Self::Call,
info: &DispatchInfoOf<Self::Call>,
len: usize
) -> Result<Self::Pre, TransactionValidityError> {
let (fee, imbalance) = self.withdraw_fee(who, info, len)?;
Ok((self.0, who.clone(), imbalance, fee))
let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;
Ok((self.0, who.clone(), imbalance))
}

fn post_dispatch(
Expand All @@ -540,32 +539,14 @@ impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> whe
len: usize,
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
let (tip, who, imbalance, fee) = pre;
if let Some(payed) = imbalance {
let actual_fee = Module::<T>::compute_actual_fee(
len as u32,
info,
post_info,
tip,
);
let refund = fee.saturating_sub(actual_fee);
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.
// `PostDispatchInfo::calc_unspent` guards against such a case.
match payed.offset(refund_imbalance) {
Ok(actual_payment) => actual_payment,
Err(_) => return Err(InvalidTransaction::Payment.into()),
}
}
// We do not recreate the account using the refund. The up front payment
// is gone in that case.
Err(_) => payed,
};
let imbalances = actual_payment.split(tip);
T::OnTransactionPayment::on_unbalanceds(Some(imbalances.0).into_iter()
.chain(Some(imbalances.1)));
}
let (tip, who, imbalance) = pre;
let actual_fee = Module::<T>::compute_actual_fee(
len as u32,
info,
post_info,
tip,
);
T::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;
Ok(())
}
}
Expand All @@ -580,6 +561,7 @@ mod tests {
DispatchClass, DispatchInfo, PostDispatchInfo, GetDispatchInfo, Weight,
WeightToFeePolynomial, WeightToFeeCoefficients, WeightToFeeCoefficient,
},
traits::Currency,
};
use pallet_balances::Call as BalancesCall;
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
Expand Down Expand Up @@ -699,8 +681,7 @@ mod tests {
}

impl Trait for Runtime {
type Currency = pallet_balances::Module<Runtime>;
type OnTransactionPayment = ();
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = WeightToFee;
type FeeMultiplierUpdate = ();
Expand Down
Loading

0 comments on commit 5d30787

Please sign in to comment.