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
+4
View File
@@ -257,6 +257,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -270,6 +272,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl Trait for Test {
type Event = ();
+4
View File
@@ -37,6 +37,8 @@ pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const MinimumPeriod: u64 = 1;
}
@@ -52,6 +54,8 @@ impl system::Trait for Test {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl timestamp::Trait for Test {
+4
View File
@@ -337,6 +337,8 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
@@ -351,6 +353,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl Trait for Test {
+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);
}
);
}
+5 -1
View File
@@ -399,6 +399,8 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -412,6 +414,8 @@ mod tests {
type Event = Event;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl Trait<Instance1> for Test {
type Origin = Origin;
@@ -425,7 +429,7 @@ mod tests {
}
pub type Block = primitives::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic<u32, u64, Call, ()>;
pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic<u32, u64, Call, ()>;
srml_support::construct_runtime!(
pub enum Test where
+4
View File
@@ -96,6 +96,8 @@ impl Get<u64> for BlockGasLimit {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const BalancesTransactionBaseFee: u64 = 0;
pub const BalancesTransactionByteFee: u64 = 0;
}
@@ -111,6 +113,8 @@ impl system::Trait for Test {
type WeightMultiplierUpdate = ();
type Event = MetaEvent;
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl balances::Trait for Test {
type Balance = u64;
+4
View File
@@ -98,6 +98,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -111,6 +113,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = Event;
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
+4
View File
@@ -998,6 +998,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -1011,6 +1013,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
+5 -1
View File
@@ -1108,6 +1108,8 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -1121,6 +1123,8 @@ mod tests {
type Event = Event;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
@@ -1212,7 +1216,7 @@ mod tests {
}
pub type Block = primitives::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic<u32, u64, Call, ()>;
pub type UncheckedExtrinsic = primitives::generic::UncheckedExtrinsic<u32, u64, Call, ()>;
srml_support::construct_runtime!(
pub enum Test where
+16 -13
View File
@@ -255,7 +255,7 @@
use srml_support::{StorageValue, dispatch::Result, decl_module, decl_storage, decl_event};
use system::{ensure_signed, ensure_root};
use sr_primitives::weights::TransactionWeight;
use sr_primitives::weights::SimpleDispatchInfo;
/// Our module's configuration trait. All our types and consts go in here. If the
/// module is dependent on specific other modules, then their configuration traits
@@ -396,19 +396,18 @@ decl_module! {
//
// If you don't respect these rules, it is likely that your chain will be attackable.
//
// Each transaction can optionally indicate a weight. The weight is passed in as a
// custom attribute and the value can be anything that implements the `Weighable`
// trait. Most often using substrate's default `TransactionWeight` is enough for you.
// Each transaction can define an optional `#[weight]` attribute to convey a set of static
// information about its dispatch. The `system` and `executive` module then use this
// information to properly execute the transaction, whilst keeping the total load of the
// chain in a moderate rate.
//
// A basic weight is a tuple of `(base_weight, byte_weight)`. Upon including each transaction
// in a block, the final weight is calculated as `base_weight + byte_weight * tx_size`.
// If this value, added to the weight of all included transactions, exceeds `MAX_TRANSACTION_WEIGHT`,
// the transaction is not included. If no weight attribute is provided, the `::default()`
// implementation of `TransactionWeight` is used.
//
// The example below showcases a transaction which is relatively costly, but less dependent on
// the input, hence `byte_weight` is configured smaller.
#[weight = TransactionWeight::Basic(100_000, 10)]
// The _right-hand-side_ value of the `#[weight]` attribute can be any type that implements
// a set of traits, namely [`WeighData`] and [`ClassifyDispatch`]. The former conveys the
// weight (a numeric representation of pure execution time and difficulty) of the
// transaction and the latter demonstrates the `DispatchClass` of the call. A higher weight
// means a larger transaction (less of which can be placed in a single block). See the
// `CheckWeight` signed extension struct in the `system` module for more information.
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
fn accumulate_dummy(origin, increase_by: T::Balance) -> Result {
// This is a public call, so we ensure that the origin is some signed account.
let _sender = ensure_signed(origin)?;
@@ -525,6 +524,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -538,6 +539,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
+355 -85
View File
@@ -69,7 +69,7 @@
//! # }
//! # }
//! /// Executive: handles dispatch to the various modules.
//! pub type Executive = executive::Executive<Runtime, Block, Context, Balances, Runtime, AllModules>;
//! pub type Executive = executive::Executive<Runtime, Block, Context, Runtime, AllModules>;
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
@@ -79,20 +79,17 @@ use rstd::marker::PhantomData;
use rstd::result;
use primitives::{generic::Digest, traits::{
self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalize,
OnInitialize, NumberFor, Block as BlockT, OffchainWorker,
ValidateUnsigned,
OnInitialize, NumberFor, Block as BlockT, OffchainWorker, ValidateUnsigned
}};
use srml_support::{Dispatchable, traits::MakePayment};
use srml_support::Dispatchable;
use parity_codec::{Codec, Encode};
use system::{extrinsics_root, DigestOf};
use primitives::{ApplyOutcome, ApplyError};
use primitives::transaction_validity::{TransactionValidity, TransactionPriority, TransactionLongevity};
use primitives::weights::{Weighable, MAX_TRANSACTIONS_WEIGHT};
mod mock;
mod tests;
use primitives::transaction_validity::TransactionValidity;
use primitives::weights::GetDispatchInfo;
mod internal {
use primitives::traits::DispatchError;
pub enum ApplyError {
BadSignature(&'static str),
@@ -106,6 +103,19 @@ mod internal {
Success,
Fail(&'static str),
}
impl From<DispatchError> for ApplyError {
fn from(d: DispatchError) -> Self {
match d {
DispatchError::Payment => ApplyError::CantPay,
DispatchError::NoPermission => ApplyError::CantPay,
DispatchError::BadState => ApplyError::CantPay,
DispatchError::Stale => ApplyError::Stale,
DispatchError::Future => ApplyError::Future,
DispatchError::BadProof => ApplyError::BadSignature(""),
}
}
}
}
/// Trait that can be used to execute a block.
@@ -118,27 +128,26 @@ pub type CheckedOf<E, C> = <E as Checkable<C>>::Checked;
pub type CallOf<E, C> = <CheckedOf<E, C> as Applyable>::Call;
pub type OriginOf<E, C> = <CallOf<E, C> as Dispatchable>::Origin;
pub struct Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>(
PhantomData<(System, Block, Context, Payment, UnsignedValidator, AllModules)>
pub struct Executive<System, Block, Context, UnsignedValidator, AllModules>(
PhantomData<(System, Block, Context, UnsignedValidator, AllModules)>
);
impl<
System: system::Trait,
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default,
Payment: MakePayment<System::AccountId>,
UnsignedValidator,
AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> ExecuteBlock<Block> for Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>
> ExecuteBlock<Block> for Executive<System, Block, Context, UnsignedValidator, AllModules>
where
Block::Extrinsic: Checkable<Context> + Codec,
CheckedOf<Block::Extrinsic, Context>: Applyable<Index=System::Index, AccountId=System::AccountId> + Weighable,
CheckedOf<Block::Extrinsic, Context>: Applyable<AccountId=System::AccountId> + GetDispatchInfo,
CallOf<Block::Extrinsic, Context>: Dispatchable,
OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call=CallOf<Block::Extrinsic, Context>>,
{
fn execute_block(block: Block) {
Executive::<System, Block, Context, Payment, UnsignedValidator, AllModules>::execute_block(block);
Executive::<System, Block, Context, UnsignedValidator, AllModules>::execute_block(block);
}
}
@@ -146,13 +155,12 @@ impl<
System: system::Trait,
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Context: Default,
Payment: MakePayment<System::AccountId>,
UnsignedValidator,
AllModules: OnInitialize<System::BlockNumber> + OnFinalize<System::BlockNumber> + OffchainWorker<System::BlockNumber>,
> Executive<System, Block, Context, Payment, UnsignedValidator, AllModules>
> Executive<System, Block, Context, UnsignedValidator, AllModules>
where
Block::Extrinsic: Checkable<Context> + Codec,
CheckedOf<Block::Extrinsic, Context>: Applyable<Index=System::Index, AccountId=System::AccountId> + Weighable,
CheckedOf<Block::Extrinsic, Context>: Applyable<AccountId=System::AccountId> + GetDispatchInfo,
CallOf<Block::Extrinsic, Context>: Dispatchable,
OriginOf<Block::Extrinsic, Context>: From<Option<System::AccountId>>,
UnsignedValidator: ValidateUnsigned<Call=CallOf<Block::Extrinsic, Context>>,
@@ -266,39 +274,21 @@ where
// Verify that the signature is good.
let xt = uxt.check(&Default::default()).map_err(internal::ApplyError::BadSignature)?;
// Check the weight of the block if that extrinsic is applied.
let weight = xt.weight(encoded_len);
if <system::Module<System>>::all_extrinsics_weight() + weight > MAX_TRANSACTIONS_WEIGHT {
return Err(internal::ApplyError::FullBlock);
}
if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) {
// check index
let expected_index = <system::Module<System>>::account_nonce(sender);
if index != &expected_index { return Err(
if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future }
) }
// pay any fees
let weight_multiplier = <system::Module<System>>::next_weight_multiplier();
Payment::make_payment(sender, weight_multiplier.apply_to(weight)).map_err(|_| internal::ApplyError::CantPay)?;
// AUDIT: Under no circumstances may this function panic from here onwards.
// FIXME: ensure this at compile-time (such as by not defining a panic function, forcing
// a linker error unless the compiler can prove it cannot be called).
// increment nonce in storage
<system::Module<System>>::inc_account_nonce(sender);
}
// Make sure to `note_extrinsic` only after we know it's going to be executed
// to prevent it from leaking in storage.
// We don't need to make sure to `note_extrinsic` only after we know it's going to be
// executed to prevent it from leaking in storage since at this point, it will either
// execute or panic (and revert storage changes).
if let Some(encoded) = to_note {
<system::Module<System>>::note_extrinsic(encoded);
}
// AUDIT: Under no circumstances may this function panic from here onwards.
// Decode parameters and dispatch
let (f, s) = xt.deconstruct();
let r = f.dispatch(s.into());
<system::Module<System>>::note_applied_extrinsic(&r, weight);
let dispatch_info = xt.get_dispatch_info();
let r = Applyable::dispatch(xt, dispatch_info, encoded_len)
.map_err(internal::ApplyError::from)?;
<system::Module<System>>::note_applied_extrinsic(&r, encoded_len as u32);
r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e {
primitives::BLOCK_FULL => Err(internal::ApplyError::FullBlock),
@@ -335,11 +325,9 @@ where
pub fn validate_transaction(uxt: Block::Extrinsic) -> TransactionValidity {
// Note errors > 0 are from ApplyError
const UNKNOWN_ERROR: i8 = -127;
const MISSING_SENDER: i8 = -20;
const INVALID_INDEX: i8 = -10;
let encoded_len = uxt.encode().len();
let encoded_len = uxt.using_encoded(|d| d.len());
let xt = match uxt.check(&Default::default()) {
// Checks out. Carry on.
Ok(xt) => xt,
@@ -351,42 +339,8 @@ where
Err(_) => return TransactionValidity::Invalid(UNKNOWN_ERROR),
};
match (xt.sender(), xt.index()) {
(Some(sender), Some(index)) => {
let weight = xt.weight(encoded_len);
// pay any fees
let weight_multiplier = <system::Module<System>>::next_weight_multiplier();
if Payment::make_payment(sender, weight_multiplier.apply_to(weight)).is_err() {
return TransactionValidity::Invalid(ApplyError::CantPay as i8)
}
// check index
let expected_index = <system::Module<System>>::account_nonce(sender);
if index < &expected_index {
return TransactionValidity::Invalid(ApplyError::Stale as i8)
}
let index = *index;
let provides = vec![(sender, index).encode()];
let requires = if expected_index < index {
vec![(sender, index - One::one()).encode()]
} else {
vec![]
};
TransactionValidity::Valid {
priority: encoded_len as TransactionPriority,
requires,
provides,
longevity: TransactionLongevity::max_value(),
propagate: true,
}
},
(None, None) => UnsignedValidator::validate_unsigned(&xt.deconstruct().0),
(Some(_), None) => TransactionValidity::Invalid(INVALID_INDEX),
(None, Some(_)) => TransactionValidity::Invalid(MISSING_SENDER),
}
let dispatch_info = xt.get_dispatch_info();
xt.validate::<UnsignedValidator>(dispatch_info, encoded_len)
}
/// Start an offchain worker and generate extrinsics.
@@ -394,3 +348,319 @@ where
<AllModules as OffchainWorker<System::BlockNumber>>::generate_extrinsics(n)
}
}
#[cfg(test)]
mod tests {
use super::*;
use balances::Call;
use runtime_io::with_externalities;
use substrate_primitives::{H256, Blake2Hasher};
use primitives::generic::Era;
use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup};
use primitives::testing::{Digest, Header, Block};
use srml_support::{impl_outer_event, impl_outer_origin, parameter_types};
use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason, Get};
use system;
use hex_literal::hex;
impl_outer_origin! {
pub enum Origin for Runtime {
}
}
impl_outer_event!{
pub enum MetaEvent for Runtime {
balances<T>,
}
}
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
#[derive(Clone, Eq, PartialEq)]
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;
type Index = u64;
type BlockNumber = u64;
type Hash = substrate_primitives::H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type Event = MetaEvent;
type BlockHashCount = BlockHashCount;
type WeightMultiplierUpdate = ();
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
pub const TransferFee: u64 = 0;
pub const CreationFee: u64 = 0;
pub const TransactionBaseFee: u64 = 10;
pub const TransactionByteFee: u64 = 0;
}
impl balances::Trait for Runtime {
type Balance = u64;
type OnFreeBalanceZero = ();
type OnNewAccount = ();
type Event = MetaEvent;
type TransactionPayment = ();
type DustRemoval = ();
type TransferPayment = ();
type ExistentialDeposit = ExistentialDeposit;
type TransferFee = TransferFee;
type CreationFee = CreationFee;
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
}
impl ValidateUnsigned for Runtime {
type Call = Call<Runtime>;
fn validate_unsigned(call: &Self::Call) -> TransactionValidity {
match call {
Call::set_balance(_, _, _) => TransactionValidity::Valid(Default::default()),
_ => TransactionValidity::Invalid(0),
}
}
}
type SignedExtra = (
system::CheckEra<Runtime>,
system::CheckNonce<Runtime>,
system::CheckWeight<Runtime>,
balances::TakeFees<Runtime>
);
type TestXt = primitives::testing::TestXt<Call<Runtime>, SignedExtra>;
type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, Runtime, ()>;
fn extra(nonce: u64, fee: u64) -> SignedExtra {
(
system::CheckEra::from(Era::Immortal),
system::CheckNonce::from(nonce),
system::CheckWeight::from(),
balances::TakeFees::from(fee)
)
}
fn sign_extra(who: u64, nonce: u64, fee: u64) -> Option<(u64, SignedExtra)> {
Some((who, extra(nonce, fee)))
}
#[test]
fn balance_transfer_dispatch_works() {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
balances::GenesisConfig::<Runtime> {
balances: vec![(1, 111)],
vesting: vec![],
}.assimilate_storage(&mut t.0, &mut t.1).unwrap();
let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 69));
let weight = xt.get_dispatch_info().weight as u64;
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new_with_children(t);
with_externalities(&mut t, || {
Executive::initialize_block(&Header::new(
1,
H256::default(),
H256::default(),
[69u8; 32].into(),
Digest::default(),
));
let r = Executive::apply_extrinsic(xt);
assert_eq!(r, Ok(ApplyOutcome::Success));
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 42 - 10 - weight);
assert_eq!(<balances::Module<Runtime>>::total_balance(&2), 69);
});
}
fn new_test_ext(balance_factor: u64) -> runtime_io::TestExternalities<Blake2Hasher> {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 111 * balance_factor)],
vesting: vec![],
}.build_storage().unwrap().0);
t.into()
}
#[test]
fn block_import_works() {
with_externalities(&mut new_test_ext(1), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("ba811447b8ae3bf798a07a18f5355ea59926917c8a9cc7527ede20b261aacfdf").into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
#[should_panic]
fn block_import_of_bad_state_root_fails() {
with_externalities(&mut new_test_ext(1), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: [0u8; 32].into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
#[should_panic]
fn block_import_of_bad_extrinsic_root_fails() {
with_externalities(&mut new_test_ext(1), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(),
extrinsics_root: [0u8; 32].into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
fn bad_extrinsic_not_inserted() {
let mut t = new_test_ext(1);
// bad nonce check!
let xt = primitives::testing::TestXt(sign_extra(1, 30, 0), Call::transfer(33, 69));
with_externalities(&mut t, || {
Executive::initialize_block(&Header::new(
1,
H256::default(),
H256::default(),
[69u8; 32].into(),
Digest::default(),
));
assert!(Executive::apply_extrinsic(xt).is_err());
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(0));
});
}
#[test]
fn block_weight_limit_enforced() {
let mut t = new_test_ext(10000);
// given: TestXt uses the encoded len as fixed Len:
let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer::<Runtime>(33, 0));
let encoded = xt.encode();
let encoded_len = encoded.len() as u32;
let limit = <MaximumBlockWeight as Get<u32>>::get() / 4;
let num_to_exhaust_block = limit / encoded_len;
with_externalities(&mut t, || {
Executive::initialize_block(&Header::new(
1,
H256::default(),
H256::default(),
[69u8; 32].into(),
Digest::default(),
));
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
for nonce in 0..=num_to_exhaust_block {
let xt = primitives::testing::TestXt(sign_extra(1, nonce.into(), 0), Call::transfer::<Runtime>(33, 0));
let res = Executive::apply_extrinsic(xt);
if nonce != num_to_exhaust_block {
assert_eq!(res.unwrap(), ApplyOutcome::Success);
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), encoded_len * (nonce + 1));
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(nonce + 1));
} else {
assert_eq!(res, Err(ApplyError::CantPay));
}
}
});
}
#[test]
fn block_weight_and_size_is_stored_per_tx() {
let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(33, 0));
let x1 = primitives::testing::TestXt(sign_extra(1, 1, 0), Call::transfer(33, 0));
let x2 = primitives::testing::TestXt(sign_extra(1, 2, 0), Call::transfer(33, 0));
let len = xt.clone().encode().len() as u32;
let mut t = new_test_ext(1);
with_externalities(&mut t, || {
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
assert_eq!(Executive::apply_extrinsic(xt.clone()).unwrap(), ApplyOutcome::Success);
assert_eq!(Executive::apply_extrinsic(x1.clone()).unwrap(), ApplyOutcome::Success);
assert_eq!(Executive::apply_extrinsic(x2.clone()).unwrap(), ApplyOutcome::Success);
// default weight for `TestXt` == encoded length.
assert_eq!( <system::Module<Runtime>>::all_extrinsics_weight(), 3 * len);
assert_eq!(<system::Module<Runtime>>::all_extrinsics_len(), 3 * len);
let _ = <system::Module<Runtime>>::finalize();
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
});
}
#[test]
fn validate_unsigned() {
let xt = primitives::testing::TestXt(None, Call::set_balance(33, 69, 69));
let valid = TransactionValidity::Valid(Default::default());
let mut t = new_test_ext(1);
with_externalities(&mut t, || {
assert_eq!(Executive::validate_transaction(xt.clone()), valid);
assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail));
});
}
#[test]
fn can_pay_for_tx_fee_on_full_lock() {
let id: LockIdentifier = *b"0 ";
let execute_with_lock = |lock: WithdrawReasons| {
let mut t = new_test_ext(1);
with_externalities(&mut t, || {
<balances::Module<Runtime> as LockableCurrency<u64>>::set_lock(
id,
&1,
110,
10,
lock,
);
let xt = primitives::testing::TestXt(sign_extra(1, 0, 0), Call::transfer(2, 10));
let weight = xt.get_dispatch_info().weight as u64;
Executive::initialize_block(&Header::new(
1,
H256::default(),
H256::default(),
[69u8; 32].into(),
Digest::default(),
));
if lock == WithdrawReasons::except(WithdrawReason::TransactionPayment) {
assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Fail);
// but tx fee has been deducted. the transaction failed on transfer, not on fee.
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 111 - 10 - weight);
} else {
assert_eq!(Executive::apply_extrinsic(xt), Err(ApplyError::CantPay));
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 111);
}
});
};
execute_with_lock(WithdrawReasons::all());
execute_with_lock(WithdrawReasons::except(WithdrawReason::TransactionPayment));
}
}
-128
View File
@@ -1,128 +0,0 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Test utilities
#![cfg(test)]
use super::*;
use runtime_io;
use substrate_primitives::{Blake2Hasher};
use srml_support::{impl_outer_origin, impl_outer_event, impl_outer_dispatch, parameter_types};
use primitives::traits::{IdentityLookup, BlakeTwo256};
use primitives::testing::{Header, Block};
use system;
pub use balances::Call as balancesCall;
pub use system::Call as systemCall;
impl_outer_origin! {
pub enum Origin for Runtime {
}
}
impl_outer_event!{
pub enum MetaEvent for Runtime {
balances<T>,
}
}
impl_outer_dispatch! {
pub enum Call for Runtime where origin: Origin {
balances::Balances,
system::System,
}
}
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
#[derive(Clone, Eq, PartialEq)]
pub struct Runtime;
parameter_types! {
pub const BlockHashCount: u64 = 250;
}
impl system::Trait for Runtime {
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Hash = substrate_primitives::H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
type Event = MetaEvent;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
pub const TransferFee: u64 = 0;
pub const CreationFee: u64 = 0;
pub const TransactionBaseFee: u64 = 0;
pub const TransactionByteFee: u64 = 0;
}
impl balances::Trait for Runtime {
type Balance = u64;
type OnFreeBalanceZero = ();
type OnNewAccount = ();
type Event = MetaEvent;
type TransactionPayment = ();
type DustRemoval = ();
type TransferPayment = ();
type ExistentialDeposit = ExistentialDeposit;
type TransferFee = TransferFee;
type CreationFee = CreationFee;
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
}
impl ValidateUnsigned for Runtime {
type Call = Call;
fn validate_unsigned(call: &Self::Call) -> TransactionValidity {
match call {
Call::Balances(balancesCall::set_balance(_, _, _)) => TransactionValidity::Valid {
priority: 0,
requires: vec![],
provides: vec![],
longevity: std::u64::MAX,
propagate: false,
},
_ => TransactionValidity::Invalid(0),
}
}
}
pub fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 1000_000_000)],
vesting: vec![],
}.build_storage().unwrap().0);
t.into()
}
type Balances = balances::Module<Runtime>;
type System = system::Module<Runtime>;
pub type TestXt = primitives::testing::TestXt<Call>;
pub type Executive = super::Executive<
Runtime,
Block<TestXt>,
system::ChainContext<Runtime>,
balances::Module<Runtime>,
Runtime,
()
>;
-246
View File
@@ -1,246 +0,0 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Tests for the module.
#![cfg(test)]
use super::*;
use mock::{new_test_ext, TestXt, Executive, Runtime, Call, systemCall, balancesCall};
use system;
use primitives::weights::{MAX_TRANSACTIONS_WEIGHT};
use primitives::testing::{Digest, Header, Block};
use primitives::traits::{Header as HeaderT};
use substrate_primitives::{Blake2Hasher, H256};
use runtime_io::with_externalities;
use srml_support::traits::Currency;
use hex_literal::hex;
#[test]
fn balance_transfer_dispatch_works() {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 129)],
vesting: vec![],
}.build_storage().unwrap().0);
let xt = primitives::testing::TestXt(Some(1), 0, Call::Balances(balancesCall::transfer(2, 69)));
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new(t);
with_externalities(&mut t, || {
Executive::initialize_block(
&Header::new(1, H256::default(), H256::default(),[69u8; 32].into(), Digest::default())
);
assert_eq!(Executive::apply_extrinsic(xt.clone()).unwrap(), ApplyOutcome::Success);
// default fee.
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 129 - 69 - 29);
assert_eq!(<balances::Module<Runtime>>::total_balance(&2), 69);
});
}
#[test]
fn block_import_works() {
with_externalities(&mut new_test_ext(), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("5518fc5383e35df8bf7cda7d6467d1307cc907424b7c8633148163aba5ee6aa8").into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
#[should_panic]
fn block_import_of_bad_state_root_fails() {
with_externalities(&mut new_test_ext(), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: [0u8; 32].into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
#[should_panic]
fn block_import_of_bad_extrinsic_root_fails() {
with_externalities(&mut new_test_ext(), || {
Executive::execute_block(Block {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(),
extrinsics_root: [0u8; 32].into(),
digest: Digest { logs: vec![], },
},
extrinsics: vec![],
});
});
}
#[test]
fn bad_extrinsic_not_inserted() {
let mut t = new_test_ext();
let xt = primitives::testing::TestXt(Some(1), 42, Call::Balances(balancesCall::transfer(33, 69)));
with_externalities(&mut t, || {
Executive::initialize_block(&Header::new(1, H256::default(), H256::default(),
[69u8; 32].into(), Digest::default()));
assert!(Executive::apply_extrinsic(xt).is_err());
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(0));
});
}
#[test]
fn block_weight_limit_enforced() {
let run_test = |should_fail: bool| {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 129)],
vesting: vec![],
}.build_storage().unwrap().0);
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new(t);
let xt = primitives::testing::TestXt(Some(1), 0, Call::Balances(balancesCall::transfer(1, 15)));
let xt2 = primitives::testing::TestXt(Some(1), 1, Call::Balances(balancesCall::transfer(2, 30)));
let encoded = xt2.encode();
let len = if should_fail { (MAX_TRANSACTIONS_WEIGHT - 1) as usize } else { encoded.len() };
with_externalities(&mut t, || {
Executive::initialize_block(&Header::new(
1,
H256::default(),
H256::default(),
[69u8; 32].into(),
Digest::default()
));
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
assert_eq!(Executive::apply_extrinsic(xt).unwrap(), ApplyOutcome::Success);
let res = Executive::apply_extrinsic_with_len(xt2, len, Some(encoded));
if should_fail {
assert!(res.is_err());
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 28);
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(1));
} else {
assert!(res.is_ok());
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 56);
assert_eq!(<system::Module<Runtime>>::extrinsic_index(), Some(2));
}
});
};
run_test(false);
run_test(true);
}
#[test]
fn exceeding_block_weight_fails() {
let mut t = new_test_ext();
let xt = |i: u32|
primitives::testing::TestXt(
Some(1),
i.into(),
Call::System(systemCall::remark(vec![0_u8; 1024 * 128]))
);
with_externalities(&mut t, || {
let len = xt(0).clone().encode().len() as u32;
let xts_to_overflow = MAX_TRANSACTIONS_WEIGHT / len;
let xts: Vec<TestXt> = (0..xts_to_overflow).map(|i| xt(i) ).collect::<_>();
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), 0);
let _ = xts.into_iter().for_each(|x| assert_eq!(
Executive::apply_extrinsic(x).unwrap(),
ApplyOutcome::Success
));
assert_eq!(<system::Module<Runtime>>::all_extrinsics_weight(), xts_to_overflow * len);
// next one will be rejected.
assert_eq!(Executive::apply_extrinsic(xt(xts_to_overflow)).err().unwrap(), ApplyError::FullBlock);
});
}
#[test]
fn default_block_weight() {
let len = primitives::testing::TestXt(Some(1), 0, Call::Balances(balancesCall::transfer(69, 10)))
.encode()
.len() as u32;
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(
Executive::apply_extrinsic(
primitives::testing::TestXt(Some(1), 0, Call::Balances(balancesCall::transfer(69, 10)))
).unwrap(),
ApplyOutcome::Success
);
assert_eq!(
Executive::apply_extrinsic(
primitives::testing::TestXt(Some(1), 1, Call::Balances(balancesCall::transfer(69, 10)))
).unwrap(),
ApplyOutcome::Success
);
assert_eq!(
Executive::apply_extrinsic(
primitives::testing::TestXt(Some(1), 2, Call::Balances(balancesCall::transfer(69, 10)))
).unwrap(),
ApplyOutcome::Success
);
assert_eq!(
<system::Module<Runtime>>::all_extrinsics_weight(),
3 * (0 /*base*/ + len /*len*/ * 1 /*byte*/)
);
});
}
#[test]
fn fail_on_bad_nonce() {
let mut t = system::GenesisConfig::default().build_storage::<Runtime>().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 129)],
vesting: vec![],
}.build_storage().unwrap().0);
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new(t);
let xt = primitives::testing::TestXt(Some(1), 0, Call::Balances(balancesCall::transfer(1, 15)));
let xt2 = primitives::testing::TestXt(Some(1), 10, Call::Balances(balancesCall::transfer(1, 15)));
with_externalities(&mut t, || {
Executive::apply_extrinsic(xt).unwrap();
let res = Executive::apply_extrinsic(xt2);
assert_eq!(res, Err(ApplyError::Future));
});
}
#[test]
fn validate_unsigned() {
let xt = primitives::testing::TestXt(None, 0, Call::Balances(balancesCall::set_balance(33, 69, 69)));
let valid = TransactionValidity::Valid {
priority: 0,
requires: vec![],
provides: vec![],
longevity: 18446744073709551615,
propagate: false,
};
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(Executive::validate_transaction(xt.clone()), valid);
assert_eq!(Executive::apply_extrinsic(xt), Ok(ApplyOutcome::Fail));
});
}
@@ -299,6 +299,8 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -312,6 +314,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const WindowSize: u64 = 11;
+2
View File
@@ -1056,6 +1056,8 @@ impl<T: Subtrait> system::Trait for ElevatedTrait<T> {
type Lookup = T::Lookup;
type Header = T::Header;
type Event = ();
type MaximumBlockWeight = T::MaximumBlockWeight;
type MaximumBlockLength = T::MaximumBlockLength;
type WeightMultiplierUpdate = ();
type BlockHashCount = T::BlockHashCount;
}
+4
View File
@@ -40,6 +40,8 @@ impl_outer_origin! {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -51,6 +53,8 @@ impl system::Trait for Test {
type Lookup = IdentityLookup<u64>;
type Header = Header;
type Event = TestEvent;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
}
+4
View File
@@ -43,6 +43,8 @@ impl Trait for Test {
}
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -56,6 +58,8 @@ impl system::Trait for Test {
type WeightMultiplierUpdate = ();
type Event = TestEvent;
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
mod grandpa {
+3 -3
View File
@@ -77,7 +77,7 @@ use substrate_primitives::{
use parity_codec::{Encode, Decode};
use primitives::{
ApplyError, traits::{Member, IsMember, Extrinsic as ExtrinsicT},
transaction_validity::{TransactionValidity, TransactionLongevity},
transaction_validity::{TransactionValidity, TransactionLongevity, ValidTransaction},
};
use rstd::prelude::*;
use session::SessionIndex;
@@ -411,13 +411,13 @@ impl<T: Trait> srml_support::unsigned::ValidateUnsigned for Module<T> {
return TransactionValidity::Invalid(ApplyError::BadSignature as i8);
}
return srml_support::unsigned::TransactionValidity::Valid {
return TransactionValidity::Valid(ValidTransaction {
priority: 0,
requires: vec![],
provides: vec![encoded_heartbeat],
longevity: TransactionLongevity::max_value(),
propagate: true,
}
})
}
TransactionValidity::Invalid(0)
}
+4
View File
@@ -66,6 +66,8 @@ impl ResolveHint<u64, u64> for TestResolveHint {
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;
@@ -79,6 +81,8 @@ impl system::Trait for Runtime {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl Trait for Runtime {
type AccountIndex = u64;
+4
View File
@@ -109,6 +109,8 @@ pub fn set_next_validators(next: Vec<u64>) {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const MinimumPeriod: u64 = 5;
}
impl system::Trait for Test {
@@ -123,6 +125,8 @@ impl system::Trait for Test {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl timestamp::Trait for Test {
type Moment = u64;
+4
View File
@@ -87,6 +87,8 @@ impl_outer_origin!{
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -100,6 +102,8 @@ impl system::Trait for Test {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const TransferFee: Balance = 0;
+1 -1
View File
@@ -20,7 +20,7 @@ bitmask = { version = "0.5", default-features = false }
[dev-dependencies]
pretty_assertions = "0.6.1"
srml-system = { path = "../system", default-features = false }
srml-system = { path = "../system" }
[features]
default = ["std"]
+57 -30
View File
@@ -25,25 +25,18 @@ pub use srml_metadata::{
FunctionMetadata, DecodeDifferent, DecodeDifferentArray, FunctionArgumentMetadata,
ModuleConstantMetadata, DefaultByte, DefaultByteGetter,
};
pub use sr_primitives::weights::{TransactionWeight, Weighable, Weight};
pub use sr_primitives::weights::{SimpleDispatchInfo, GetDispatchInfo, DispatchInfo, WeighData,
ClassifyDispatch,
TransactionPriority
};
pub use sr_primitives::traits::{Dispatchable, DispatchResult};
/// A type that cannot be instantiated.
pub enum Never {}
/// Result of a module function call; either nothing (functions are only called for "side effects")
/// or an error message.
pub type Result = result::Result<(), &'static str>;
/// A lazy call (module function and argument values) that can be executed via its `dispatch`
/// method.
pub trait Dispatchable {
/// Every function call from your runtime has an origin, which specifies where the extrinsic was
/// generated from. In the case of a signed extrinsic (transaction), the origin contains an
/// identifier for the caller. The origin can be empty in the case of an inherent extrinsic.
type Origin;
type Trait;
fn dispatch(self, origin: Self::Origin) -> Result;
}
pub type Result = DispatchResult;
/// Serializable version of Dispatchable.
/// This value can be used as a "function" in an extrinsic.
@@ -593,7 +586,7 @@ macro_rules! decl_module {
{ $( $constants )* }
[ $( $dispatchables )* ]
$(#[doc = $doc_attr])*
#[weight = $crate::dispatch::TransactionWeight::default()]
#[weight = $crate::dispatch::SimpleDispatchInfo::default()]
$fn_vis fn $fn_name(
$from $(, $(#[$codec_attr])* $param_name : $param )*
) $( -> $result )* { $( $impl )* }
@@ -1117,14 +1110,38 @@ macro_rules! decl_module {
}
// Implement weight calculation function for Call
impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::Weighable
impl<$trait_instance: $trait_name $(<I>, $instance: $instantiable)?> $crate::dispatch::GetDispatchInfo
for $call_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )*
{
fn weight(&self, _len: usize) -> $crate::dispatch::Weight {
match self {
$( $call_type::$fn_name(..) => $crate::dispatch::Weighable::weight(&$weight, _len), )*
$call_type::__PhantomItem(_, _) => { unreachable!("__PhantomItem should never be used.") },
}
fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo {
$(
if let $call_type::$fn_name($( ref $param_name ),*) = self {
let weight = <dyn $crate::dispatch::WeighData<( $( & $param, )* )>>::weigh_data(
&$weight,
($( $param_name, )*)
);
let class = <dyn $crate::dispatch::ClassifyDispatch<( $( & $param, )* )>>::classify_dispatch(
&$weight,
($( $param_name, )*)
);
return $crate::dispatch::DispatchInfo { weight, class };
}
if let $call_type::__PhantomItem(_, _) = self { unreachable!("__PhantomItem should never be used.") }
)*
// Defensive only: this function must have already returned at this point.
// all dispatchable function will have a weight which has the `::default`
// implementation of `SimpleDispatchInfo`. Nonetheless, we create one if it does
// not exist.
let weight = <dyn $crate::dispatch::WeighData<_>>::weigh_data(
&$crate::dispatch::SimpleDispatchInfo::default(),
()
);
let class = <dyn $crate::dispatch::ClassifyDispatch<_>>::classify_dispatch(
&$crate::dispatch::SimpleDispatchInfo::default(),
()
);
$crate::dispatch::DispatchInfo { weight, class }
}
}
@@ -1269,10 +1286,10 @@ macro_rules! impl_outer_dispatch {
$camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> )
,)*
}
impl $crate::dispatch::Weighable for $call_type {
fn weight(&self, len: usize) -> $crate::dispatch::Weight {
impl $crate::dispatch::GetDispatchInfo for $call_type {
fn get_dispatch_info(&self) -> $crate::dispatch::DispatchInfo {
match self {
$( $call_type::$camelcase(call) => call.weight(len), )*
$( $call_type::$camelcase(call) => call.get_dispatch_info(), )*
}
}
}
@@ -1579,6 +1596,7 @@ macro_rules! __check_reserved_fn_name {
mod tests {
use super::*;
use crate::runtime_primitives::traits::{OnInitialize, OnFinalize};
use sr_primitives::weights::{DispatchInfo, DispatchClass};
pub trait Trait: system::Trait + Sized where Self::AccountId: From<u32> {
type Origin;
@@ -1604,7 +1622,7 @@ mod tests {
fn aux_0(_origin) -> Result { unreachable!() }
fn aux_1(_origin, #[compact] _data: u32) -> Result { unreachable!() }
fn aux_2(_origin, _data: i32, _data2: String) -> Result { unreachable!() }
#[weight = TransactionWeight::Basic(10, 100)]
#[weight = SimpleDispatchInfo::FixedNormal(10)]
fn aux_3(_origin) -> Result { unreachable!() }
fn aux_4(_origin, _data: i32) -> Result { unreachable!() }
fn aux_5(_origin, _data: i32, #[compact] _data2: u32) -> Result { unreachable!() }
@@ -1613,8 +1631,8 @@ mod tests {
fn on_finalize(n: T::BlockNumber) { if n.into() == 42 { panic!("on_finalize") } }
fn offchain_worker() {}
#[weight = TransactionWeight::Max]
fn weighted(_origin) { unreachable!() }
#[weight = SimpleDispatchInfo::FixedOperational(5)]
fn operational(_origin) { unreachable!() }
}
}
@@ -1680,7 +1698,7 @@ mod tests {
documentation: DecodeDifferent::Encode(&[]),
},
FunctionMetadata {
name: DecodeDifferent::Encode("weighted"),
name: DecodeDifferent::Encode("operational"),
arguments: DecodeDifferent::Encode(&[]),
documentation: DecodeDifferent::Encode(&[]),
},
@@ -1755,10 +1773,19 @@ mod tests {
#[test]
fn weight_should_attach_to_call_enum() {
// max weight. not dependent on input.
assert_eq!(Call::<TraitImpl>::weighted().weight(100), 3 * 1024 * 1024);
assert_eq!(
Call::<TraitImpl>::operational().get_dispatch_info(),
DispatchInfo { weight: 5, class: DispatchClass::Operational },
);
// default weight.
assert_eq!(Call::<TraitImpl>::aux_0().weight(5), 5 /*tx-len*/);
assert_eq!(
Call::<TraitImpl>::aux_0().get_dispatch_info(),
DispatchInfo { weight: 100, class: DispatchClass::Normal },
);
// custom basic
assert_eq!(Call::<TraitImpl>::aux_3().weight(5), 10 + 100 * 5 );
assert_eq!(
Call::<TraitImpl>::aux_3().get_dispatch_info(),
DispatchInfo { weight: 10, class: DispatchClass::Normal },
);
}
}
-14
View File
@@ -23,7 +23,6 @@ use crate::codec::{Codec, Encode, Decode};
use substrate_primitives::u32_trait::Value as U32;
use crate::runtime_primitives::traits::{MaybeSerializeDebug, SimpleArithmetic, Saturating};
use crate::runtime_primitives::ConsensusEngineId;
use crate::runtime_primitives::weights::Weight;
use super::for_each_tuple;
@@ -90,19 +89,6 @@ pub enum UpdateBalanceOutcome {
AccountKilled,
}
/// Simple trait designed for hooking into a transaction payment.
///
/// It operates over a single generic `AccountId` type.
pub trait MakePayment<AccountId> {
/// Make transaction payment from `who` for an extrinsic of encoded length
/// `encoded_len` bytes. Return `Ok` iff the payment was successful.
fn make_payment(who: &AccountId, weight: Weight) -> Result<(), &'static str>;
}
impl<T> MakePayment<T> for () {
fn make_payment(_: &T, _: Weight) -> Result<(), &'static str> { Ok(()) }
}
/// A trait for finding the author of a block header based on the `PreRuntime` digests contained
/// within it.
pub trait FindAuthor<Author> {
@@ -269,7 +269,7 @@ srml_support::construct_runtime!(
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<u32, Index, Call, Signature>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
fn new_test_ext() -> runtime_io::TestExternalities<Blake2Hasher> {
GenesisConfig{
@@ -407,4 +407,4 @@ fn storage_with_instance_basic_operation() {
DoubleMap::remove(key1, key2);
assert_eq!(DoubleMap::get(key1, key2), 0);
});
}
}
@@ -152,7 +152,7 @@ pub type BlockNumber = u64;
pub type Index = u64;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<u32, Index, Call, Signature>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
impl system::Trait for Runtime {
type Hash = H256;
@@ -183,4 +183,4 @@ fn create_genesis_config() {
enable_storage_role: true,
})
};
}
}
+372 -10
View File
@@ -76,16 +76,19 @@ use serde::Serialize;
use rstd::prelude::*;
#[cfg(any(feature = "std", test))]
use rstd::map;
use primitives::weights::{Weight, WeightMultiplier};
use primitives::{generic, traits::{self, CheckEqual, SimpleArithmetic,
use rstd::marker::PhantomData;
use primitives::generic::{self, Era};
use primitives::weights::{Weight, DispatchInfo, DispatchClass, WeightMultiplier};
use primitives::transaction_validity::{ValidTransaction, TransactionPriority, TransactionLongevity};
use primitives::traits::{self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Convert,
SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash,
MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup,
Zero, Convert,
}};
MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded,
Lookup, DispatchError, SaturatedConversion,
};
use substrate_primitives::storage::well_known_keys;
use srml_support::{
storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue,
StorageMap, Parameter, for_each_tuple, traits::{Contains, Get},
storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, StorageMap,
Parameter, for_each_tuple, traits::{Contains, Get}
};
use safe_mix::TripletMix;
use parity_codec::{Encode, Decode};
@@ -194,6 +197,12 @@ pub trait Trait: 'static + Eq + Clone {
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
type BlockHashCount: Get<Self::BlockNumber>;
/// The maximum weight of a block.
type MaximumBlockWeight: Get<Weight>;
/// The maximum length of a block (in bytes).
type MaximumBlockLength: Get<u32>;
}
pub type DigestOf<T> = generic::Digest<<T as Trait>::Hash>;
@@ -325,6 +334,8 @@ decl_storage! {
ExtrinsicCount: Option<u32>;
/// Total weight for all extrinsics put together, for the current block.
AllExtrinsicsWeight: Option<Weight>;
/// Total length (in bytes) for all extrinsics put together, for the current block.
AllExtrinsicsLen: Option<u32>;
/// The next weight multiplier. This should be updated at the end of each block based on the
/// saturation level (weight).
pub NextWeightMultiplier get(next_weight_multiplier): WeightMultiplier = Default::default();
@@ -549,6 +560,10 @@ impl<T: Trait> Module<T> {
AllExtrinsicsWeight::get().unwrap_or_default()
}
pub fn all_extrinsics_len() -> u32 {
AllExtrinsicsLen::get().unwrap_or_default()
}
/// Update the next weight multiplier.
///
/// This should be called at then end of each block, before `all_extrinsics_weight` is cleared.
@@ -590,6 +605,7 @@ impl<T: Trait> Module<T> {
ExtrinsicCount::kill();
Self::update_weight_multiplier();
AllExtrinsicsWeight::kill();
AllExtrinsicsLen::kill();
let number = <Number<T>>::take();
let parent_hash = <ParentHash<T>>::take();
@@ -744,17 +760,15 @@ impl<T: Trait> Module<T> {
}
/// To be called immediately after an extrinsic has been applied.
pub fn note_applied_extrinsic(r: &Result<(), &'static str>, weight: Weight) {
pub fn note_applied_extrinsic(r: &Result<(), &'static str>, _encoded_len: u32) {
Self::deposit_event(match r {
Ok(_) => Event::ExtrinsicSuccess,
Err(_) => Event::ExtrinsicFailed,
}.into());
let next_extrinsic_index = Self::extrinsic_index().unwrap_or_default() + 1u32;
let total_weight = weight.saturating_add(Self::all_extrinsics_weight());
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &next_extrinsic_index);
AllExtrinsicsWeight::put(&total_weight);
}
/// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block
@@ -772,6 +786,207 @@ impl<T: Trait> Module<T> {
}
}
/// resource limit check.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct CheckWeight<T: Trait + Send + Sync>(PhantomData<T>);
impl<T: Trait + Send + Sync> CheckWeight<T> {
/// Get the quota divisor of each dispatch class type. This indicates that all operational
/// dispatches can use the full capacity of any resource, while user-triggered ones can consume
/// a quarter.
fn get_dispatch_limit_divisor(class: DispatchClass) -> Weight {
match class {
DispatchClass::Operational => 1,
DispatchClass::Normal => 4,
}
}
/// Checks if the current extrinsic can fit into the block with respect to block weight limits.
///
/// Upon successes, it returns the new block weight as a `Result`.
fn check_weight(info: DispatchInfo) -> Result<Weight, DispatchError> {
let current_weight = Module::<T>::all_extrinsics_weight();
let maximum_weight = T::MaximumBlockWeight::get();
let limit = maximum_weight / Self::get_dispatch_limit_divisor(info.class);
let added_weight = info.weight.min(limit);
let next_weight = current_weight.saturating_add(added_weight);
if next_weight > limit {
return Err(DispatchError::BadState)
}
Ok(next_weight)
}
/// Checks if the current extrinsic can fit into the block with respect to block length limits.
///
/// Upon successes, it returns the new block length as a `Result`.
fn check_block_length(info: DispatchInfo, len: usize) -> Result<u32, DispatchError> {
let current_len = Module::<T>::all_extrinsics_len();
let maximum_len = T::MaximumBlockLength::get();
let limit = maximum_len / Self::get_dispatch_limit_divisor(info.class);
let added_len = len as u32;
let next_len = current_len.saturating_add(added_len);
if next_len > limit {
return Err(DispatchError::BadState)
}
Ok(next_len)
}
/// get the priority of an extrinsic denoted by `info`.
fn get_priority(info: DispatchInfo) -> TransactionPriority {
match info.class {
DispatchClass::Normal => info.weight.into(),
DispatchClass::Operational => Bounded::max_value()
}
}
/// Utility constructor for tests and client code.
#[cfg(feature = "std")]
pub fn from() -> Self {
Self(PhantomData)
}
}
impl<T: Trait + Send + Sync> SignedExtension for CheckWeight<T> {
type AccountId = T::AccountId;
type AdditionalSigned = ();
fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) }
fn pre_dispatch(
self,
_who: &Self::AccountId,
info: DispatchInfo,
len: usize,
) -> Result<(), DispatchError> {
let next_len = Self::check_block_length(info, len)?;
AllExtrinsicsLen::put(next_len);
let next_weight = Self::check_weight(info)?;
AllExtrinsicsWeight::put(next_weight);
Ok(())
}
fn validate(
&self,
_who: &Self::AccountId,
info: DispatchInfo,
len: usize,
) -> Result<ValidTransaction, DispatchError> {
// There is no point in writing to storage here since changes are discarded. This basically
// discards any transaction which is bigger than the length or weight limit alone, which is
// a guarantee that it will fail in the pre-dispatch phase.
let _ = Self::check_block_length(info, len)?;
let _ = Self::check_weight(info)?;
Ok(ValidTransaction { priority: Self::get_priority(info), ..Default::default() })
}
}
#[cfg(feature = "std")]
impl<T: Trait + Send + Sync> rstd::fmt::Debug for CheckWeight<T> {
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
write!(f, "CheckWeight<T>")
}
}
/// Nonce check and increment to give replay protection for transactions.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct CheckNonce<T: Trait>(#[codec(compact)] T::Index);
#[cfg(feature = "std")]
impl<T: Trait> CheckNonce<T> {
/// utility constructor. Used only in client/factory code.
pub fn from(nonce: T::Index) -> Self {
Self(nonce)
}
}
#[cfg(feature = "std")]
impl<T: Trait> rstd::fmt::Debug for CheckNonce<T> {
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
self.0.fmt(f)
}
}
impl<T: Trait> SignedExtension for CheckNonce<T> {
type AccountId = T::AccountId;
type AdditionalSigned = ();
fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) }
fn pre_dispatch(
self,
who: &Self::AccountId,
_info: DispatchInfo,
_len: usize,
) -> Result<(), DispatchError> {
let expected = <AccountNonce<T>>::get(who);
if self.0 != expected {
return Err(
if self.0 < expected { DispatchError::Stale } else { DispatchError::Future }
)
}
<AccountNonce<T>>::insert(who, expected + T::Index::one());
Ok(())
}
fn validate(
&self,
who: &Self::AccountId,
info: DispatchInfo,
_len: usize,
) -> Result<ValidTransaction, DispatchError> {
// check index
let expected = <AccountNonce<T>>::get(who);
if self.0 < expected {
return Err(DispatchError::Stale)
}
let provides = vec![Encode::encode(&(who, self.0))];
let requires = if expected < self.0 {
vec![Encode::encode(&(who, self.0 - One::one()))]
} else {
vec![]
};
Ok(ValidTransaction {
priority: info.weight as TransactionPriority,
requires,
provides,
longevity: TransactionLongevity::max_value(),
propagate: true,
})
}
}
/// Nonce check and increment to give replay protection for transactions.
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
pub struct CheckEra<T: Trait + Send + Sync>((Era, rstd::marker::PhantomData<T>));
#[cfg(feature = "std")]
impl<T: Trait + Send + Sync> CheckEra<T> {
/// utility constructor. Used only in client/factory code.
pub fn from(era: Era) -> Self {
Self((era, rstd::marker::PhantomData))
}
}
#[cfg(feature = "std")]
impl<T: Trait + Send + Sync> rstd::fmt::Debug for CheckEra<T> {
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
self.0.fmt(f)
}
}
impl<T: Trait + Send + Sync> SignedExtension for CheckEra<T> {
type AccountId = T::AccountId;
type AdditionalSigned = T::Hash;
fn additional_signed(&self) -> Result<Self::AdditionalSigned, &'static str> {
let current_u64 = <Module<T>>::block_number().saturated_into::<u64>();
let n = (self.0).0.birth(current_u64).saturated_into::<T::BlockNumber>();
if !<BlockHash<T>>::exists(n) { Err("transaction birth block ancient")? }
Ok(<Module<T>>::block_hash(n))
}
}
pub struct ChainContext<T>(::rstd::marker::PhantomData<T>);
impl<T> Default for ChainContext<T> {
fn default() -> Self {
@@ -819,6 +1034,8 @@ mod tests {
parameter_types! {
pub const BlockHashCount: u64 = 10;
pub const MaximumBlockWeight: Weight = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl Trait for Test {
@@ -833,6 +1050,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = u16;
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
impl From<Event> for u16 {
@@ -983,4 +1202,147 @@ mod tests {
}
})
}
#[test]
fn signed_ext_check_nonce_works() {
with_externalities(&mut new_test_ext(), || {
<AccountNonce<Test>>::insert(1, 1);
let info = DispatchInfo::default();
let len = 0_usize;
// stale
assert!(CheckNonce::<Test>(0).validate(&1, info, len).is_err());
assert!(CheckNonce::<Test>(0).pre_dispatch(&1, info, len).is_err());
// correct
assert!(CheckNonce::<Test>(1).validate(&1, info, len).is_ok());
assert!(CheckNonce::<Test>(1).pre_dispatch(&1, info, len).is_ok());
// future
assert!(CheckNonce::<Test>(5).validate(&1, info, len).is_ok());
assert!(CheckNonce::<Test>(5).pre_dispatch(&1, info, len).is_err());
})
}
#[test]
fn signed_ext_check_weight_works_user_tx() {
with_externalities(&mut new_test_ext(), || {
let small = DispatchInfo { weight: 100, ..Default::default() };
let medium = DispatchInfo {
weight: <MaximumBlockWeight as Get<Weight>>::get() / 4 - 1,
..Default::default()
};
let big = DispatchInfo {
weight: <MaximumBlockWeight as Get<Weight>>::get() / 4 + 1,
..Default::default()
};
let len = 0_usize;
let reset_check_weight = |i, f, s| {
AllExtrinsicsWeight::put(s);
let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, i, len);
if f { assert!(r.is_err()) } else { assert!(r.is_ok()) }
};
reset_check_weight(small, false, 0);
reset_check_weight(medium, false, 0);
reset_check_weight(big, true, 1);
})
}
#[test]
fn signed_ext_check_weight_fee_works() {
with_externalities(&mut new_test_ext(), || {
let free = DispatchInfo { weight: 0, ..Default::default() };
let len = 0_usize;
assert_eq!(System::all_extrinsics_weight(), 0);
let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, free, len);
assert!(r.is_ok());
assert_eq!(System::all_extrinsics_weight(), 0);
})
}
#[test]
fn signed_ext_check_weight_max_works() {
with_externalities(&mut new_test_ext(), || {
let max = DispatchInfo { weight: Weight::max_value(), ..Default::default() };
let len = 0_usize;
assert_eq!(System::all_extrinsics_weight(), 0);
let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, max, len);
assert!(r.is_ok());
assert_eq!(System::all_extrinsics_weight(), <MaximumBlockWeight as Get<Weight>>::get() / 4);
})
}
#[test]
fn signed_ext_check_weight_works_operational_tx() {
with_externalities(&mut new_test_ext(), || {
let normal = DispatchInfo { weight: 100, ..Default::default() };
let op = DispatchInfo { weight: 100, class: DispatchClass::Operational };
let len = 0_usize;
// given almost full block
AllExtrinsicsWeight::put(<MaximumBlockWeight as Get<Weight>>::get() / 4);
// will not fit.
assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, normal, len).is_err());
// will fit.
assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, op, len).is_ok());
// likewise for length limit.
let len = 100_usize;
AllExtrinsicsLen::put(<MaximumBlockLength as Get<Weight>>::get() / 4);
assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, normal, len).is_err());
assert!(CheckWeight::<Test>(PhantomData).pre_dispatch(&1, op, len).is_ok());
})
}
#[test]
fn signed_ext_check_weight_priority_works() {
with_externalities(&mut new_test_ext(), || {
let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal };
let op = DispatchInfo { weight: 100, class: DispatchClass::Operational };
let len = 0_usize;
assert_eq!(
CheckWeight::<Test>(PhantomData).validate(&1, normal, len).unwrap().priority,
100,
);
assert_eq!(
CheckWeight::<Test>(PhantomData).validate(&1, op, len).unwrap().priority,
Bounded::max_value(),
);
})
}
#[test]
fn signed_ext_check_weight_block_size_works() {
with_externalities(&mut new_test_ext(), || {
let tx = DispatchInfo::default();
let reset_check_weight = |s, f| {
AllExtrinsicsLen::put(0);
let r = CheckWeight::<Test>(PhantomData).pre_dispatch(&1, tx, s);
if f { assert!(r.is_err()) } else { assert!(r.is_ok()) }
};
reset_check_weight(128, false);
reset_check_weight(512, false);
reset_check_weight(513, true);
})
}
#[test]
fn signed_ext_check_era_should_work() {
with_externalities(&mut new_test_ext(), || {
// future
assert_eq!(
CheckEra::<Test>::from(Era::mortal(4, 2)).additional_signed().err().unwrap(),
"transaction birth block ancient"
);
// correct
System::set_block_number(13);
<BlockHash<Test>>::insert(12, H256::repeat_byte(1));
assert!(CheckEra::<Test>::from(Era::mortal(4, 12)).additional_signed().is_ok());
})
}
}
+4
View File
@@ -338,6 +338,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -351,6 +353,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const MinimumPeriod: u64 = 5;
+4
View File
@@ -370,6 +370,8 @@ mod tests {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: u32 = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -383,6 +385,8 @@ mod tests {
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;