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:
Gavin Wood
2019-07-23 01:06:49 +08:00
committed by Kian Peymani
parent 4f5654b67d
commit 78bc5edc14
55 changed files with 1965 additions and 1646 deletions
+80 -15
View File
@@ -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)
}
}
+13 -3
View File
@@ -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() }
}
+39 -8
View File
@@ -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);
}
);
}