Transaction Fee Multiplier (#2854)

* added fee calculations; need some type conversions

* cleaned up make_payment and other stuff

* rename vars to compile

* add WeightToFee type

* clean test files after new type added to balances

* fmting

* fix balance configs in tests

* more fixing mocks and tests

* more comprehensive block weight limit test

* fix compilation errors

* more srml/executive tests && started fixing node/executor tests

* new fee multiplier; still overflows :(

* perbill at the end attempt; needs to be changed

* clean fmting, rename some vars

* new PoC implementation.

* test weight_to_fee range and verify functionality

* 12 of 15 tests in node executor are passing

* 1 test failing; big_block imports are failing for wrong reasons

* Update srml/executive/src/lib.rs

Co-Authored-By: Kian Peymani <Kianenigma@users.noreply.github.com>

* Some cleanup.

* consolidate tests in runtime impls

* clean and condition executive for stateful fee range test

* remove comments to self

* Major cleanup.

* More cleanup.

* Fix lock files.

* Fix build.

* Update node-template/runtime/Cargo.toml

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Update node/executor/src/lib.rs

Co-Authored-By: Gavin Wood <github@gavwood.com>

* Per-block update.

* nit.

* Update docs.

* Fix contracts test.

* Stateful fee update.

* Update lock files.

* Update node/runtime/src/impls.rs

* Revamped again with fixed64.

* fix cargo file.

* nits.

* Some cleanup.

* Some nits.

* Fix build.

* Bump.

* Rename to WeightMultiplier

* Update node/executor/src/lib.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Add weight to election module mock.

* Fix build.

* finalize merge

* Update srml/system/src/lib.rs

* Bring back fees.

* Some nits.

* Code shifting for simplicity.

* Fix build + more tests.

* Update weights.rs

* Update core/sr-primitives/src/weights.rs

* Update lib.rs

* Fix test build
This commit is contained in:
Amar Singh
2019-07-19 14:21:05 +02:00
committed by Kian Peymani
parent a313935947
commit a757dfb222
36 changed files with 1103 additions and 411 deletions
+1
View File
@@ -267,6 +267,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -49,6 +49,7 @@ impl system::Trait for Test {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -348,6 +348,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1 -1
View File
@@ -15,7 +15,7 @@ srml-support = { path = "../support", default-features = false }
system = { package = "srml-system", path = "../system", default-features = false }
[dev-dependencies]
runtime_io = { package = "sr-io", path = "../../core/sr-io" }
runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
substrate-primitives = { path = "../../core/primitives" }
[features]
+4 -3
View File
@@ -163,6 +163,7 @@ use primitives::traits::{
Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub,
MaybeSerializeDebug, Saturating, Bounded
};
use primitives::weights::Weight;
use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root};
mod mock;
@@ -759,6 +760,7 @@ impl<T: Subtrait<I>, I: Instance> system::Trait for ElevatedTrait<T, I> {
type AccountId = T::AccountId;
type Lookup = T::Lookup;
type Header = T::Header;
type WeightMultiplierUpdate = T::WeightMultiplierUpdate;
type Event = ();
type BlockHashCount = T::BlockHashCount;
}
@@ -1145,9 +1147,8 @@ where
}
impl<T: Trait<I>, I: Instance> MakePayment<T::AccountId> for Module<T, I> {
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
let encoded_len = T::Balance::from(encoded_len as u32);
let transaction_fee = T::TransactionBaseFee::get() + T::TransactionByteFee::get() * encoded_len;
fn make_payment(transactor: &T::AccountId, weight: Weight) -> Result {
let transaction_fee = T::Balance::from(weight);
let imbalance = Self::withdraw(
transactor,
transaction_fee,
+7 -6
View File
@@ -18,7 +18,7 @@
#![cfg(test)]
use primitives::{traits::{IdentityLookup}, testing::Header};
use primitives::{traits::IdentityLookup, testing::Header};
use substrate_primitives::{H256, Blake2Hasher};
use runtime_io;
use srml_support::{impl_outer_origin, parameter_types, traits::Get};
@@ -77,6 +77,7 @@ impl system::Trait for Runtime {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
@@ -118,6 +119,11 @@ impl Default for ExtBuilder {
}
}
impl ExtBuilder {
pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64) -> Self {
self.transaction_base_fee = base_fee;
self.transaction_byte_fee = byte_fee;
self
}
pub fn existential_deposit(mut self, existential_deposit: u64) -> Self {
self.existential_deposit = existential_deposit;
self
@@ -131,11 +137,6 @@ impl ExtBuilder {
self.creation_fee = creation_fee;
self
}
pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64) -> Self {
self.transaction_base_fee = base_fee;
self.transaction_byte_fee = byte_fee;
self
}
pub fn monied(mut self, monied: bool) -> Self {
self.monied = monied;
if self.existential_deposit == 0 {
+1 -1
View File
@@ -616,7 +616,7 @@ fn check_vesting_status() {
assert_eq!(System::block_number(), 10);
// Account 1 has fully vested by block 10
assert_eq!(Balances::vesting_balance(&1), 0);
assert_eq!(Balances::vesting_balance(&1), 0);
// Account 2 has started vesting by block 10
assert_eq!(Balances::vesting_balance(&2), user2_free_balance);
// Account 12 has started vesting by block 10
+1
View File
@@ -410,6 +410,7 @@ mod tests {
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
}
impl Trait<Instance1> for Test {
+3 -4
View File
@@ -96,6 +96,8 @@ impl Get<u64> for BlockGasLimit {
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const BalancesTransactionBaseFee: u64 = 0;
pub const BalancesTransactionByteFee: u64 = 0;
}
impl system::Trait for Test {
type Origin = Origin;
@@ -106,13 +108,10 @@ impl system::Trait for Test {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = MetaEvent;
type BlockHashCount = BlockHashCount;
}
parameter_types! {
pub const BalancesTransactionBaseFee: u64 = 0;
pub const BalancesTransactionByteFee: u64 = 0;
}
impl balances::Trait for Test {
type Balance = u64;
type OnFreeBalanceZero = Contract;
+2 -1
View File
@@ -108,6 +108,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = Event;
type BlockHashCount = BlockHashCount;
}
@@ -115,7 +116,7 @@ mod tests {
pub const ExistentialDeposit: u64 = 0;
pub const TransferFee: u64 = 0;
pub const CreationFee: u64 = 0;
pub const TransactionBaseFee: u64 = 0;
pub const TransactionBaseFee: u64 = 1;
pub const TransactionByteFee: u64 = 0;
}
impl balances::Trait for Test {
+1
View File
@@ -1008,6 +1008,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -1119,6 +1119,7 @@ mod tests {
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = Event;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
}
parameter_types! {
+1
View File
@@ -535,6 +535,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+12 -304
View File
@@ -87,10 +87,12 @@ 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;
use primitives::weights::{Weighable, MAX_TRANSACTIONS_WEIGHT};
mod mock;
mod tests;
mod internal {
pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024;
pub enum ApplyError {
BadSignature(&'static str),
@@ -266,7 +268,7 @@ where
// 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 > internal::MAX_TRANSACTIONS_WEIGHT {
if <system::Module<System>>::all_extrinsics_weight() + weight > MAX_TRANSACTIONS_WEIGHT {
return Err(internal::ApplyError::FullBlock);
}
@@ -277,7 +279,8 @@ where
if index < &expected_index { internal::ApplyError::Stale } else { internal::ApplyError::Future }
) }
// pay any fees
Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?;
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
@@ -295,7 +298,7 @@ where
// Decode parameters and dispatch
let (f, s) = xt.deconstruct();
let r = f.dispatch(s.into());
<system::Module<System>>::note_applied_extrinsic(&r, encoded_len as u32);
<system::Module<System>>::note_applied_extrinsic(&r, weight);
r.map(|_| internal::ApplyOutcome::Success).or_else(|e| match e {
primitives::BLOCK_FULL => Err(internal::ApplyError::FullBlock),
@@ -350,8 +353,11 @@ where
match (xt.sender(), xt.index()) {
(Some(sender), Some(index)) => {
let weight = xt.weight(encoded_len);
// pay any fees
if Payment::make_payment(sender, encoded_len).is_err() {
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)
}
@@ -388,301 +394,3 @@ 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::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};
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;
}
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;
}
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 {
priority: 0,
requires: vec![],
provides: vec![],
longevity: std::u64::MAX,
propagate: false,
},
_ => TransactionValidity::Invalid(0),
}
}
}
type TestXt = primitives::testing::TestXt<Call<Runtime>>;
type Executive = super::Executive<
Runtime,
Block<TestXt>,
system::ChainContext<Runtime>,
balances::Module<Runtime>,
Runtime,
()
>;
#[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(Some(1), 0, Call::transfer(2, 69));
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(),
));
Executive::apply_extrinsic(xt).unwrap();
assert_eq!(<balances::Module<Runtime>>::total_balance(&1), 42 - 10);
assert_eq!(<balances::Module<Runtime>>::total_balance(&2), 69);
});
}
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, 111)],
vesting: vec![],
}.build_storage().unwrap().0);
t.into()
}
#[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!("9159f07939faa7de6ec7f46e292144fc82112c42ead820dfb588f1788f3e8058").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::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 = new_test_ext();
let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(33, 69));
let xt2 = primitives::testing::TestXt(Some(1), 1, Call::transfer(33, 69));
let encoded = xt2.encode();
let len = if should_fail { (internal::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);
Executive::apply_extrinsic(xt).unwrap();
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 default_block_weight() {
let xt = primitives::testing::TestXt(None, 0, Call::set_balance(33, 69, 69));
let mut t = new_test_ext();
with_externalities(&mut t, || {
Executive::apply_extrinsic(xt.clone()).unwrap();
Executive::apply_extrinsic(xt.clone()).unwrap();
Executive::apply_extrinsic(xt.clone()).unwrap();
assert_eq!(
<system::Module<Runtime>>::all_extrinsics_weight(),
3 * (0 /*base*/ + 22 /*len*/ * 1 /*byte*/)
);
});
}
#[test]
fn validate_unsigned() {
let xt = primitives::testing::TestXt(None, 0, Call::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));
});
}
#[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();
with_externalities(&mut t, || {
<balances::Module<Runtime> as LockableCurrency<u64>>::set_lock(
id,
&1,
110,
10,
lock,
);
let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 10));
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);
} 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
@@ -0,0 +1,128 @@
// 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
@@ -0,0 +1,246 @@
// 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));
});
}
@@ -309,6 +309,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -1056,6 +1056,7 @@ impl<T: Subtrait> system::Trait for ElevatedTrait<T> {
type Lookup = T::Lookup;
type Header = T::Header;
type Event = ();
type WeightMultiplierUpdate = ();
type BlockHashCount = T::BlockHashCount;
}
impl<T: Subtrait> Trait for ElevatedTrait<T> {
+1
View File
@@ -51,6 +51,7 @@ impl system::Trait for Test {
type Lookup = IdentityLookup<u64>;
type Header = Header;
type Event = TestEvent;
type WeightMultiplierUpdate = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -53,6 +53,7 @@ impl system::Trait for Test {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = TestEvent;
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -76,6 +76,7 @@ impl system::Trait for Runtime {
type AccountId = u64;
type Lookup = Indices;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -120,6 +120,7 @@ impl system::Trait for Test {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+3 -2
View File
@@ -97,12 +97,13 @@ impl system::Trait for Test {
type AccountId = AccountId;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
parameter_types! {
pub const TransferFee: u64 = 0;
pub const CreationFee: u64 = 0;
pub const TransferFee: Balance = 0;
pub const CreationFee: Balance = 0;
pub const TransactionBaseFee: u64 = 0;
pub const TransactionByteFee: u64 = 0;
}
+4 -5
View File
@@ -21,10 +21,9 @@
use crate::rstd::{result, marker::PhantomData, ops::Div};
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::traits::{MaybeSerializeDebug, SimpleArithmetic, Saturating};
use crate::runtime_primitives::ConsensusEngineId;
use crate::runtime_primitives::weights::Weight;
use super::for_each_tuple;
@@ -97,11 +96,11 @@ pub enum UpdateBalanceOutcome {
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, encoded_len: usize) -> Result<(), &'static str>;
fn make_payment(who: &AccountId, weight: Weight) -> Result<(), &'static str>;
}
impl<T> MakePayment<T> for () {
fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) }
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
+1
View File
@@ -65,6 +65,7 @@ impl system::Trait for Runtime {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = Event;
}
+31 -6
View File
@@ -76,10 +76,11 @@ 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,
SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, CurrentHeight, BlockNumberToHash,
MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, Lookup,
Zero,
Zero, Convert,
}};
use substrate_primitives::storage::well_known_keys;
use srml_support::{
@@ -174,6 +175,14 @@ pub trait Trait: 'static + Eq + Clone {
/// (e.g. Indices module) may provide more functional/efficient alternatives.
type Lookup: StaticLookup<Target = Self::AccountId>;
/// Handler for updating the weight multiplier at the end of each block.
///
/// It receives the current block's weight as input and returns the next weight multiplier for next
/// block.
///
/// Note that passing `()` will keep the value constant.
type WeightMultiplierUpdate: Convert<(Weight, WeightMultiplier), WeightMultiplier>;
/// The block header.
type Header: Parameter + traits::Header<
Number = Self::BlockNumber,
@@ -315,7 +324,10 @@ decl_storage! {
/// Total extrinsics count for the current block.
ExtrinsicCount: Option<u32>;
/// Total weight for all extrinsics put together, for the current block.
AllExtrinsicsWeight: Option<u32>;
AllExtrinsicsWeight: Option<Weight>;
/// 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();
/// Map of block numbers to block hashes.
pub BlockHash get(block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map T::BlockNumber => T::Hash;
/// Extrinsics data for the current block (maps an extrinsic's index to its data).
@@ -533,10 +545,21 @@ impl<T: Trait> Module<T> {
}
/// Gets a total weight of all executed extrinsics.
pub fn all_extrinsics_weight() -> u32 {
pub fn all_extrinsics_weight() -> Weight {
AllExtrinsicsWeight::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.
pub fn update_weight_multiplier() {
// update the multiplier based on block weight.
let current_weight = Self::all_extrinsics_weight();
NextWeightMultiplier::mutate(|fm| {
*fm = T::WeightMultiplierUpdate::convert((current_weight, *fm))
});
}
/// Start the execution of a particular block.
pub fn initialize(
number: &T::BlockNumber,
@@ -565,6 +588,7 @@ impl<T: Trait> Module<T> {
/// Remove temporary "environment" entries in storage.
pub fn finalize() -> T::Header {
ExtrinsicCount::kill();
Self::update_weight_multiplier();
AllExtrinsicsWeight::kill();
let number = <Number<T>>::take();
@@ -720,17 +744,17 @@ impl<T: Trait> Module<T> {
}
/// To be called immediately after an extrinsic has been applied.
pub fn note_applied_extrinsic(r: &Result<(), &'static str>, encoded_len: u32) {
pub fn note_applied_extrinsic(r: &Result<(), &'static str>, weight: Weight) {
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_length = encoded_len.saturating_add(Self::all_extrinsics_weight());
let total_weight = weight.saturating_add(Self::all_extrinsics_weight());
storage::unhashed::put(well_known_keys::EXTRINSIC_INDEX, &next_extrinsic_index);
AllExtrinsicsWeight::put(&total_length);
AllExtrinsicsWeight::put(&total_weight);
}
/// To be called immediately after `note_applied_extrinsic` of the last extrinsic of the block
@@ -806,6 +830,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = u16;
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -348,6 +348,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}
+1
View File
@@ -380,6 +380,7 @@ mod tests {
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type WeightMultiplierUpdate = ();
type Event = ();
type BlockHashCount = BlockHashCount;
}