mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 05:21:03 +00:00
Extensible transactions (and tips) (#3102)
* Make extrinsics extensible. Also Remove old extrinsic types. * Rest of mockup. Add tips. * Fix some build issues * Runtiem builds :) * Substrate builds. * Fix a doc test * Compact encoding * Extract out the era logic into an extension * Weight Check signed extension. (#3115) * Weight signed extension. * Revert a bit + test for check era. * Update Cargo.toml * Update node/cli/src/factory_impl.rs * Update node/executor/src/lib.rs * Update node/executor/src/lib.rs * Don't use len for weight - use data. * Operational Transaction; second attempt (#3138) * working poc added. * some fixes. * Update doc. * Fix all tests + final logic. * more refactoring. * nits. * System block limit in bytes. * Silent the storage macro warnings. * More logic more tests. * Fix import. * Refactor names. * Fix build. * Update srml/balances/src/lib.rs * Final refactor. * Bump transaction version * Fix weight mult test. * Fix more tests and improve doc. * Bump. * Make some tests work again. * Fix subkey. * Remove todos + bump. * Ignore expensive test. * Bump.
This commit is contained in:
@@ -154,16 +154,17 @@ use rstd::{cmp, result, mem};
|
||||
use parity_codec::{Codec, Encode, Decode};
|
||||
use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module};
|
||||
use srml_support::traits::{
|
||||
UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced,
|
||||
UpdateBalanceOutcome, Currency, OnFreeBalanceZero, OnUnbalanced,
|
||||
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
|
||||
Imbalance, SignedImbalance, ReservableCurrency
|
||||
Imbalance, SignedImbalance, ReservableCurrency, Get,
|
||||
};
|
||||
use srml_support::{dispatch::Result, traits::Get};
|
||||
use srml_support::dispatch::Result;
|
||||
use primitives::traits::{
|
||||
Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub,
|
||||
MaybeSerializeDebug, Saturating, Bounded
|
||||
Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug,
|
||||
Saturating, Bounded, SignedExtension, SaturatedConversion, DispatchError
|
||||
};
|
||||
use primitives::weights::Weight;
|
||||
use primitives::transaction_validity::{TransactionPriority, ValidTransaction};
|
||||
use primitives::weights::DispatchInfo;
|
||||
use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root};
|
||||
|
||||
mod mock;
|
||||
@@ -763,6 +764,8 @@ impl<T: Subtrait<I>, I: Instance> system::Trait for ElevatedTrait<T, I> {
|
||||
type WeightMultiplierUpdate = T::WeightMultiplierUpdate;
|
||||
type Event = ();
|
||||
type BlockHashCount = T::BlockHashCount;
|
||||
type MaximumBlockWeight = T::MaximumBlockWeight;
|
||||
type MaximumBlockLength = T::MaximumBlockLength;
|
||||
}
|
||||
impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
|
||||
type Balance = T::Balance;
|
||||
@@ -1146,17 +1149,79 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> MakePayment<T::AccountId> for Module<T, I> {
|
||||
fn make_payment(transactor: &T::AccountId, weight: Weight) -> Result {
|
||||
let transaction_fee = T::Balance::from(weight);
|
||||
let imbalance = Self::withdraw(
|
||||
transactor,
|
||||
transaction_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 TakeFees<T: Trait<I>, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance);
|
||||
|
||||
impl<T: Trait<I>, I: Instance> TakeFees<T, I> {
|
||||
/// utility constructor. Used only in client/factory code.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from(fee: T::Balance) -> Self {
|
||||
Self(fee)
|
||||
}
|
||||
|
||||
/// Compute the final fee value for a particular transaction.
|
||||
///
|
||||
/// The final fee is composed of:
|
||||
/// - _length-fee_: This is the amount paid merely to pay for size of the transaction.
|
||||
/// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike
|
||||
/// size-fee, this is not input dependent and reflects the _complexity_ of the execution
|
||||
/// and the time it consumes.
|
||||
/// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed
|
||||
/// transactions can have a tip.
|
||||
fn compute_fee(len: usize, info: DispatchInfo, tip: T::Balance) -> T::Balance {
|
||||
// length fee
|
||||
let len_fee = if info.pay_length_fee() {
|
||||
let len = T::Balance::from(len as u32);
|
||||
let base = T::TransactionBaseFee::get();
|
||||
let byte = T::TransactionByteFee::get();
|
||||
base.saturating_add(byte.saturating_mul(len))
|
||||
} else {
|
||||
Zero::zero()
|
||||
};
|
||||
|
||||
// weight fee
|
||||
let weight = info.weight;
|
||||
let weight_fee: T::Balance = <system::Module<T>>::next_weight_multiplier().apply_to(weight).into();
|
||||
|
||||
len_fee.saturating_add(weight_fee).saturating_add(tip)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Trait<I>, I: Instance> rstd::fmt::Debug for TakeFees<T, I> {
|
||||
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance + Clone + Eq> SignedExtension for TakeFees<T, I> {
|
||||
type AccountId = T::AccountId;
|
||||
type AdditionalSigned = ();
|
||||
fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) }
|
||||
|
||||
fn validate(
|
||||
&self,
|
||||
who: &Self::AccountId,
|
||||
info: DispatchInfo,
|
||||
len: usize,
|
||||
) -> rstd::result::Result<ValidTransaction, DispatchError> {
|
||||
// pay any fees.
|
||||
let fee = Self::compute_fee(len, info, self.0);
|
||||
let imbalance = <Module<T, I>>::withdraw(
|
||||
who,
|
||||
fee,
|
||||
WithdrawReason::TransactionPayment,
|
||||
ExistenceRequirement::KeepAlive
|
||||
)?;
|
||||
ExistenceRequirement::KeepAlive,
|
||||
).map_err(|_| DispatchError::Payment)?;
|
||||
T::TransactionPayment::on_unbalanced(imbalance);
|
||||
Ok(())
|
||||
|
||||
let mut r = ValidTransaction::default();
|
||||
// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which
|
||||
// will be a bit more than setting the priority to tip. For now, this is enough.
|
||||
r.priority = fee.saturated_into::<TransactionPriority>();
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use primitives::{traits::IdentityLookup, testing::Header};
|
||||
use primitives::{traits::IdentityLookup, testing::Header, weights::{DispatchInfo, Weight}};
|
||||
use substrate_primitives::{H256, Blake2Hasher};
|
||||
use runtime_io;
|
||||
use srml_support::{impl_outer_origin, parameter_types, traits::Get};
|
||||
use srml_support::{impl_outer_origin, parameter_types};
|
||||
use srml_support::traits::Get;
|
||||
use std::cell::RefCell;
|
||||
use crate::{GenesisConfig, Module, Trait};
|
||||
|
||||
@@ -34,7 +35,7 @@ thread_local! {
|
||||
static TRANSFER_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static CREATION_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BASE_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(1);
|
||||
}
|
||||
|
||||
pub struct ExistentialDeposit;
|
||||
@@ -67,6 +68,8 @@ impl Get<u64> for TransactionByteFee {
|
||||
pub struct Runtime;
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const MaximumBlockWeight: u32 = 1024;
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
}
|
||||
impl system::Trait for Runtime {
|
||||
type Origin = Origin;
|
||||
@@ -80,6 +83,8 @@ impl system::Trait for Runtime {
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
}
|
||||
impl Trait for Runtime {
|
||||
type Balance = u64;
|
||||
@@ -186,3 +191,8 @@ impl ExtBuilder {
|
||||
|
||||
pub type System = system::Module<Runtime>;
|
||||
pub type Balances = Module<Runtime>;
|
||||
|
||||
/// create a transaction info struct from weight. Handy to avoid building the whole struct.
|
||||
pub fn info_from_weight(w: Weight) -> DispatchInfo {
|
||||
DispatchInfo { weight: w, ..Default::default() }
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use mock::{Balances, ExtBuilder, Runtime, System};
|
||||
use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight};
|
||||
use runtime_io::with_externalities;
|
||||
use srml_support::{
|
||||
assert_noop, assert_ok, assert_err,
|
||||
traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons,
|
||||
Currency, MakePayment, ReservableCurrency}
|
||||
Currency, ReservableCurrency}
|
||||
};
|
||||
|
||||
const ID_1: LockIdentifier = *b"1 ";
|
||||
@@ -123,7 +123,13 @@ fn lock_reasons_should_work() {
|
||||
"account liquidity restrictions prevent withdrawal"
|
||||
);
|
||||
assert_ok!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
|
||||
assert_ok!(<Balances as MakePayment<_>>::make_payment(&1, 1));
|
||||
// NOTE: this causes a fee payment.
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
&1,
|
||||
info_from_weight(1),
|
||||
0,
|
||||
).is_ok());
|
||||
|
||||
Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into());
|
||||
assert_ok!(<Balances as Currency<_>>::transfer(&1, &2, 1));
|
||||
@@ -131,15 +137,22 @@ fn lock_reasons_should_work() {
|
||||
<Balances as ReservableCurrency<_>>::reserve(&1, 1),
|
||||
"account liquidity restrictions prevent withdrawal"
|
||||
);
|
||||
assert_ok!(<Balances as MakePayment<_>>::make_payment(&1, 1));
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
&1,
|
||||
info_from_weight(1),
|
||||
0,
|
||||
).is_ok());
|
||||
|
||||
Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into());
|
||||
assert_ok!(<Balances as Currency<_>>::transfer(&1, &2, 1));
|
||||
assert_ok!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
|
||||
assert_noop!(
|
||||
<Balances as MakePayment<_>>::make_payment(&1, 1),
|
||||
"account liquidity restrictions prevent withdrawal"
|
||||
);
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
&1,
|
||||
info_from_weight(1),
|
||||
0,
|
||||
).is_err());
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -733,3 +746,21 @@ fn liquid_funds_should_transfer_with_delayed_vesting() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_extension_take_fees_work() {
|
||||
with_externalities(
|
||||
&mut ExtBuilder::default()
|
||||
.existential_deposit(10)
|
||||
.transaction_fees(10, 1)
|
||||
.monied(true)
|
||||
.build(),
|
||||
|| {
|
||||
let len = 10;
|
||||
assert!(TakeFees::<Runtime>::from(0).pre_dispatch(&1, info_from_weight(0), len).is_ok());
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20);
|
||||
assert!(TakeFees::<Runtime>::from(5 /* tipped */).pre_dispatch(&1, info_from_weight(0), len).is_ok());
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 25);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user