mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 05:21:08 +00:00
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:
@@ -34,7 +34,7 @@ pub use paste;
|
||||
#[cfg(feature = "std")]
|
||||
pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay};
|
||||
|
||||
use rstd::{prelude::*, ops};
|
||||
use rstd::{prelude::*, ops, convert::TryInto};
|
||||
use substrate_primitives::{crypto, ed25519, sr25519, hash::{H256, H512}};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
@@ -43,7 +43,7 @@ pub mod testing;
|
||||
|
||||
pub mod weights;
|
||||
pub mod traits;
|
||||
use traits::{SaturatedConversion, UniqueSaturatedInto};
|
||||
use traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, Bounded, CheckedSub, CheckedAdd};
|
||||
|
||||
pub mod generic;
|
||||
pub mod transaction_validity;
|
||||
@@ -168,7 +168,7 @@ impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) {
|
||||
pub type ConsensusEngineId = [u8; 4];
|
||||
|
||||
/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000).
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Permill(u32);
|
||||
|
||||
@@ -273,7 +273,7 @@ impl From<codec::Compact<Permill>> for Permill {
|
||||
/// Perbill is parts-per-billion. It stores a value between 0 and 1 in fixed point and
|
||||
/// provides a means to multiply some other value by that.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct Perbill(u32);
|
||||
|
||||
impl Perbill {
|
||||
@@ -377,6 +377,128 @@ impl From<codec::Compact<Perbill>> for Perbill {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A fixed point number by the scale of 1 billion.
|
||||
///
|
||||
/// cannot hold a value larger than +-`9223372036854775807 / 1_000_000_000` (~9 billion).
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Fixed64(i64);
|
||||
|
||||
/// The maximum value of the `Fixed64` type
|
||||
const DIV: i64 = 1_000_000_000;
|
||||
|
||||
impl Fixed64 {
|
||||
/// creates self from a natural number.
|
||||
///
|
||||
/// Note that this might be lossy.
|
||||
pub fn from_natural(int: i64) -> Self {
|
||||
Self(int.saturating_mul(DIV))
|
||||
}
|
||||
|
||||
/// Return the accuracy of the type. Given that this function returns the value `X`, it means
|
||||
/// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`.
|
||||
pub fn accuracy() -> i64 {
|
||||
DIV
|
||||
}
|
||||
|
||||
/// creates self from a rational number. Equal to `n/d`.
|
||||
///
|
||||
/// Note that this might be lossy.
|
||||
pub fn from_rational(n: i64, d: u64) -> Self {
|
||||
Self((n as i128 * DIV as i128 / (d as i128).max(1)).try_into().unwrap_or(Bounded::max_value()))
|
||||
}
|
||||
|
||||
/// Performs a saturated multiply and accumulate.
|
||||
///
|
||||
/// Returns `n + (self * n)`.
|
||||
pub fn saturated_multiply_accumulate(&self, int: u32) -> u32 {
|
||||
let parts = self.0;
|
||||
|
||||
let positive = parts > 0;
|
||||
// natural parts might overflow.
|
||||
let natural_parts = self.clone().saturated_into::<u32>();
|
||||
// fractional parts can always fit into u32.
|
||||
let perbill_parts = (parts.abs() % DIV) as u32;
|
||||
|
||||
let n = int.saturating_mul(natural_parts);
|
||||
let p = Perbill::from_parts(perbill_parts) * int;
|
||||
// everything that needs to be either added or subtracted from the original weight.
|
||||
let excess = n.saturating_add(p);
|
||||
|
||||
if positive {
|
||||
int.saturating_add(excess)
|
||||
} else {
|
||||
int.saturating_sub(excess)
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw constructor. Equal to `parts / 1_000_000_000`.
|
||||
pub fn from_parts(parts: i64) -> Self {
|
||||
Self(parts)
|
||||
}
|
||||
}
|
||||
|
||||
impl UniqueSaturatedInto<u32> for Fixed64 {
|
||||
/// Note that the maximum value of Fixed64 might be more than what can fit in u32. This is hence,
|
||||
/// expected to be lossy.
|
||||
fn unique_saturated_into(self) -> u32 {
|
||||
(self.0.abs() / DIV).try_into().unwrap_or(Bounded::max_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for Fixed64 {
|
||||
fn saturating_add(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_add(rhs.0))
|
||||
}
|
||||
fn saturating_mul(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_mul(rhs.0) / DIV)
|
||||
}
|
||||
fn saturating_sub(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_sub(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait for
|
||||
/// safe addition.
|
||||
impl ops::Add for Fixed64 {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait for
|
||||
/// safe subtraction.
|
||||
impl ops::Sub for Fixed64 {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedSub for Fixed64 {
|
||||
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
|
||||
if let Some(v) = self.0.checked_sub(rhs.0) {
|
||||
Some(Self(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckedAdd for Fixed64 {
|
||||
fn checked_add(&self, rhs: &Self) -> Option<Self> {
|
||||
if let Some(v) = self.0.checked_add(rhs.0) {
|
||||
Some(Self(v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PerU128 is parts-per-u128-max-value. It stores a value between 0 and 1 in fixed point.
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)]
|
||||
@@ -755,6 +877,7 @@ impl traits::Extrinsic for OpaqueExtrinsic {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::codec::{Encode, Decode};
|
||||
use super::{Perbill, Permill};
|
||||
|
||||
macro_rules! per_thing_upper_test {
|
||||
($num_type:tt, $per:tt) => {
|
||||
@@ -798,19 +921,19 @@ mod tests {
|
||||
fn compact_permill_perbill_encoding() {
|
||||
let tests = [(0u32, 1usize), (63, 1), (64, 2), (16383, 2), (16384, 4), (1073741823, 4), (1073741824, 5), (u32::max_value(), 5)];
|
||||
for &(n, l) in &tests {
|
||||
let compact: crate::codec::Compact<super::Permill> = super::Permill(n).into();
|
||||
let compact: crate::codec::Compact<Permill> = Permill(n).into();
|
||||
let encoded = compact.encode();
|
||||
assert_eq!(encoded.len(), l);
|
||||
let decoded = <crate::codec::Compact<super::Permill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let permill: super::Permill = decoded.into();
|
||||
assert_eq!(permill, super::Permill(n));
|
||||
let decoded = <crate::codec::Compact<Permill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let permill: Permill = decoded.into();
|
||||
assert_eq!(permill, Permill(n));
|
||||
|
||||
let compact: crate::codec::Compact<super::Perbill> = super::Perbill(n).into();
|
||||
let compact: crate::codec::Compact<Perbill> = Perbill(n).into();
|
||||
let encoded = compact.encode();
|
||||
assert_eq!(encoded.len(), l);
|
||||
let decoded = <crate::codec::Compact<super::Perbill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let perbill: super::Perbill = decoded.into();
|
||||
assert_eq!(perbill, super::Perbill(n));
|
||||
let decoded = <crate::codec::Compact<Perbill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let perbill: Perbill = decoded.into();
|
||||
assert_eq!(perbill, Perbill(n));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,16 +944,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_has_compact_permill() {
|
||||
let data = WithCompact { data: super::Permill(1) };
|
||||
let data = WithCompact { data: Permill(1) };
|
||||
let encoded = data.encode();
|
||||
assert_eq!(data, WithCompact::<super::Permill>::decode(&mut &encoded[..]).unwrap());
|
||||
assert_eq!(data, WithCompact::<Permill>::decode(&mut &encoded[..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_compact_perbill() {
|
||||
let data = WithCompact { data: super::Perbill(1) };
|
||||
let data = WithCompact { data: Perbill(1) };
|
||||
let encoded = data.encode();
|
||||
assert_eq!(data, WithCompact::<super::Perbill>::decode(&mut &encoded[..]).unwrap());
|
||||
assert_eq!(data, WithCompact::<Perbill>::decode(&mut &encoded[..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -850,7 +973,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn per_things_operate_in_output_type() {
|
||||
assert_eq!(super::Perbill::one() * 255_u64, 255);
|
||||
assert_eq!(Perbill::one() * 255_u64, 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -858,12 +981,12 @@ mod tests {
|
||||
use primitive_types::U256;
|
||||
|
||||
assert_eq!(
|
||||
super::Perbill::from_parts(999_999_999) * std::u128::MAX,
|
||||
Perbill::from_parts(999_999_999) * std::u128::MAX,
|
||||
((Into::<U256>::into(std::u128::MAX) * 999_999_999u32) / 1_000_000_000u32).as_u128()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
super::Permill::from_parts(999_999) * std::u128::MAX,
|
||||
Permill::from_parts(999_999) * std::u128::MAX,
|
||||
((Into::<U256>::into(std::u128::MAX) * 999_999u32) / 1_000_000u32).as_u128()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,18 +16,25 @@
|
||||
|
||||
//! Primitives for transaction weighting.
|
||||
//!
|
||||
//! Each dispatch function within `decl_module!` can now have an optional
|
||||
//! `#[weight = $x]` attribute. $x can be any object that implements the
|
||||
//! `Weighable` trait. By default, All transactions are annotated by
|
||||
//! `#[weight = TransactionWeight::default()]`.
|
||||
//! Each dispatch function within `decl_module!` can have an optional `#[weight = $x]` attribute.
|
||||
//! $x can be any object that implements the `Weighable` trait. By default, All transactions are
|
||||
//! annotated by `#[weight = TransactionWeight::default()]`.
|
||||
//!
|
||||
//! Note that the decl_module macro _cannot_ enforce this and will simply fail
|
||||
//! if an invalid struct is passed in.
|
||||
//! Note that the decl_module macro _cannot_ enforce this and will simply fail if an invalid struct
|
||||
//! (something that does not implement `Weighable`) is passed in.
|
||||
|
||||
use crate::{Fixed64, traits::Saturating};
|
||||
use crate::codec::{Encode, Decode};
|
||||
|
||||
/// The final type that each `#[weight = $x:expr]`'s
|
||||
/// expression must evaluate to.
|
||||
pub type Weight = u32;
|
||||
|
||||
/// Maximum block saturation: 4mb
|
||||
pub const MAX_TRANSACTIONS_WEIGHT: u32 = 4 * 1024 * 1024;
|
||||
/// Target block saturation: 25% of max block saturation = 1mb
|
||||
pub const IDEAL_TRANSACTIONS_WEIGHT: u32 = MAX_TRANSACTIONS_WEIGHT / 4;
|
||||
|
||||
/// A `Call` enum (aka transaction) that can be weighted using the custom weight attribute of
|
||||
/// its dispatchable functions. Is implemented by default in the `decl_module!`.
|
||||
///
|
||||
@@ -74,3 +81,76 @@ impl Default for TransactionWeight {
|
||||
TransactionWeight::Basic(0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of a weight multiplier. This represents how a fee value can be computed from a
|
||||
/// weighted transaction.
|
||||
///
|
||||
/// This is basically a wrapper for the `Fixed64` type a slightly tailored multiplication to u32
|
||||
/// in the form of the `apply_to` method.
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct WeightMultiplier(Fixed64);
|
||||
|
||||
impl WeightMultiplier {
|
||||
/// Apply the inner Fixed64 as a weight multiplier to a weight value.
|
||||
///
|
||||
/// This will perform a saturated `weight + weight * self.0`.
|
||||
pub fn apply_to(&self, weight: Weight) -> Weight {
|
||||
self.0.saturated_multiply_accumulate(weight)
|
||||
}
|
||||
|
||||
/// build self from raw parts per billion.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from_parts(parts: i64) -> Self {
|
||||
Self(Fixed64(parts))
|
||||
}
|
||||
|
||||
/// build self from a fixed64 value.
|
||||
pub fn from_fixed(f: Fixed64) -> Self {
|
||||
Self(f)
|
||||
}
|
||||
|
||||
/// Approximate the fraction `n/d`.
|
||||
pub fn from_rational(n: i64, d: u64) -> Self {
|
||||
Self(Fixed64::from_rational(n, d))
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for WeightMultiplier {
|
||||
fn saturating_add(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_add(rhs.0))
|
||||
}
|
||||
fn saturating_mul(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_mul(rhs.0))
|
||||
|
||||
}
|
||||
fn saturating_sub(self, rhs: Self) -> Self {
|
||||
Self(self.0.saturating_sub(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn multiplier_apply_to_works() {
|
||||
let test_set = vec![0, 1, 10, 1000, 1_000_000_000];
|
||||
|
||||
// negative (1/2)
|
||||
let mut fm = WeightMultiplier::from_rational(-1, 2);
|
||||
test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i) as i32, i as i32 - i as i32 / 2); });
|
||||
|
||||
// unit (1) multiplier
|
||||
fm = WeightMultiplier::from_parts(0);
|
||||
test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i); });
|
||||
|
||||
// i.5 multiplier
|
||||
fm = WeightMultiplier::from_rational(1, 2);
|
||||
test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i * 3 / 2); });
|
||||
|
||||
// dual multiplier
|
||||
fm = WeightMultiplier::from_rational(1, 1);
|
||||
test_set.clone().into_iter().for_each(|i| { assert_eq!(fm.apply_to(i), i * 2); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ indices = { package = "srml-indices", path = "../../srml/indices", default_featu
|
||||
system = { package = "srml-system", path = "../../srml/system", default_features = false }
|
||||
timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default_features = false }
|
||||
sudo = { package = "srml-sudo", path = "../../srml/sudo", default_features = false }
|
||||
runtime-primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default_features = false }
|
||||
sr-primitives = { path = "../../core/sr-primitives", default_features = false }
|
||||
client = { package = "substrate-client", path = "../../core/client", default_features = false }
|
||||
consensus-aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default_features = false }
|
||||
offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false }
|
||||
@@ -41,7 +41,7 @@ std = [
|
||||
"aura/std",
|
||||
"indices/std",
|
||||
"primitives/std",
|
||||
"runtime-primitives/std",
|
||||
"sr-primitives/std",
|
||||
"system/std",
|
||||
"timestamp/std",
|
||||
"sudo/std",
|
||||
|
||||
@@ -15,7 +15,7 @@ use rstd::prelude::*;
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::bytes;
|
||||
use primitives::{ed25519, sr25519, OpaqueMetadata};
|
||||
use runtime_primitives::{
|
||||
use sr_primitives::{
|
||||
ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str,
|
||||
traits::{self, NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify}
|
||||
};
|
||||
@@ -29,10 +29,10 @@ use version::NativeVersion;
|
||||
|
||||
// A few exports that help ease life for downstream crates.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use runtime_primitives::BuildStorage;
|
||||
pub use sr_primitives::BuildStorage;
|
||||
pub use timestamp::Call as TimestampCall;
|
||||
pub use balances::Call as BalancesCall;
|
||||
pub use runtime_primitives::{Permill, Perbill};
|
||||
pub use sr_primitives::{Permill, Perbill};
|
||||
pub use support::{StorageValue, construct_runtime, parameter_types};
|
||||
|
||||
/// Alias to the signature scheme used for Aura authority signatures.
|
||||
@@ -56,6 +56,9 @@ pub type BlockNumber = u64;
|
||||
/// Index of an account's extrinsic in the chain.
|
||||
pub type Nonce = u64;
|
||||
|
||||
/// Balance type for the node.
|
||||
pub type Balance = u128;
|
||||
|
||||
/// Used for the module template in `./template.rs`
|
||||
mod template;
|
||||
|
||||
@@ -131,6 +134,8 @@ impl system::Trait for Runtime {
|
||||
type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
/// The ubiquitous event type.
|
||||
type Event = Event;
|
||||
/// Update weight (to fee) multiplier per-block.
|
||||
type WeightMultiplierUpdate = ();
|
||||
/// The ubiquitous origin type.
|
||||
type Origin = Origin;
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
@@ -174,7 +179,7 @@ parameter_types! {
|
||||
|
||||
impl balances::Trait for Runtime {
|
||||
/// The type for recording an account's balance.
|
||||
type Balance = u128;
|
||||
type Balance = Balance;
|
||||
/// What to do if an account's free balance gets zeroed.
|
||||
type OnFreeBalanceZero = ();
|
||||
/// What to do if a new account is created.
|
||||
|
||||
@@ -72,7 +72,7 @@ mod tests {
|
||||
use runtime_io::with_externalities;
|
||||
use primitives::{H256, Blake2Hasher};
|
||||
use support::{impl_outer_origin, assert_ok, parameter_types};
|
||||
use runtime_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
|
||||
use sr_primitives::{traits::{BlakeTwo256, IdentityLookup}, testing::Header};
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test {}
|
||||
@@ -95,6 +95,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ mod tests {
|
||||
use node_primitives::{Hash, BlockNumber, AccountId};
|
||||
use runtime_primitives::traits::{Header as HeaderT, Hash as HashT};
|
||||
use runtime_primitives::{generic::Era, ApplyOutcome, ApplyError, ApplyResult, Perbill};
|
||||
use runtime_primitives::weights::{IDEAL_TRANSACTIONS_WEIGHT, WeightMultiplier};
|
||||
use {balances, contracts, indices, staking, system, timestamp};
|
||||
use contracts::ContractAddressFor;
|
||||
use system::{EventRecord, Phase};
|
||||
@@ -77,8 +78,10 @@ mod tests {
|
||||
const BLOATY_CODE: &[u8] = node_runtime::WASM_BINARY_BLOATY;
|
||||
|
||||
const GENESIS_HASH: [u8; 32] = [69u8; 32];
|
||||
|
||||
const TX_FEE: u128 = 3 * CENTS + 460 * MILLICENTS;
|
||||
// from weight
|
||||
const TX_FEE: u128 = 146;
|
||||
// regardless of creation of transfer
|
||||
const TRANSFER_FEE: u128 = 1 * CENTS;
|
||||
|
||||
type TestExternalities<H> = CoreTestExternalities<H, u64>;
|
||||
|
||||
@@ -150,13 +153,13 @@ mod tests {
|
||||
fn panic_execution_with_foreign_code_gives_error() {
|
||||
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, map![
|
||||
blake2_256(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
69_u128.encode()
|
||||
},
|
||||
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
69_u128.encode()
|
||||
},
|
||||
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
0_u128.encode()
|
||||
},
|
||||
blake2_256(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => {
|
||||
vec![0u8; 32]
|
||||
@@ -186,13 +189,13 @@ mod tests {
|
||||
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
||||
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, map![
|
||||
blake2_256(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
69_u128.encode()
|
||||
},
|
||||
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
69_u128.encode()
|
||||
},
|
||||
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
0_u128.encode()
|
||||
},
|
||||
blake2_256(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => {
|
||||
vec![0u8; 32]
|
||||
@@ -249,7 +252,7 @@ mod tests {
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - TX_FEE - TRANSFER_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
@@ -285,7 +288,7 @@ mod tests {
|
||||
assert!(r.is_ok());
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - TX_FEE - TRANSFER_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
@@ -479,12 +482,11 @@ mod tests {
|
||||
// session change => consensus authorities change => authorities change digest item appears
|
||||
let digest = Header::decode(&mut &block2.0[..]).unwrap().digest;
|
||||
assert_eq!(digest.logs().len(), 0);
|
||||
// assert!(digest.logs()[0].as_consensus().is_some());
|
||||
|
||||
(block1, block2)
|
||||
}
|
||||
|
||||
fn big_block() -> (Vec<u8>, Hash) {
|
||||
fn block_with_size(time: u64, nonce: u64, size: usize) -> (Vec<u8>, Hash) {
|
||||
construct_block(
|
||||
&mut new_test_ext(COMPACT_CODE, false),
|
||||
1,
|
||||
@@ -492,11 +494,11 @@ mod tests {
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: Call::Timestamp(timestamp::Call::set(42)),
|
||||
function: Call::Timestamp(timestamp::Call::set(time)),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
signed: Some((alice(), 0)),
|
||||
function: Call::System(system::Call::remark(vec![0; 120000])),
|
||||
signed: Some((charlie(), nonce)),
|
||||
function: Call::System(system::Call::remark(vec![0; size])),
|
||||
}
|
||||
]
|
||||
)
|
||||
@@ -519,7 +521,7 @@ mod tests {
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
// block1 transfers from alice 69 to bob.
|
||||
// -1 is the default fee
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - TX_FEE - TRANSFER_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS);
|
||||
let events = vec![
|
||||
EventRecord {
|
||||
@@ -545,7 +547,6 @@ mod tests {
|
||||
];
|
||||
assert_eq!(System::events(), events);
|
||||
});
|
||||
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
@@ -557,9 +558,9 @@ mod tests {
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
// bob sends 5, alice sends 15 | bob += 10, alice -= 10
|
||||
// 111 - 69 - 10 = 32
|
||||
assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * (TX_FEE + TRANSFER_FEE));
|
||||
// 100 + 69 + 10 = 179
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - TX_FEE - TRANSFER_FEE);
|
||||
let events = vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
@@ -615,7 +616,7 @@ mod tests {
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
// block1 transfers from alice 69 to bob.
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - (TX_FEE + TRANSFER_FEE));
|
||||
assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS);
|
||||
});
|
||||
|
||||
@@ -624,9 +625,9 @@ mod tests {
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
// bob sends 5, alice sends 15 | bob += 10, alice -= 10
|
||||
// 111 - 69 - 10 = 32
|
||||
assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 32 * DOLLARS - 2 * (TX_FEE + TRANSFER_FEE));
|
||||
// 100 + 69 + 10 = 179
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - (TX_FEE + TRANSFER_FEE));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -724,7 +725,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn deploying_wasm_contract_should_work() {
|
||||
|
||||
let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap();
|
||||
let transfer_ch = <Runtime as system::Trait>::Hashing::hash(&transfer_code);
|
||||
|
||||
@@ -795,7 +795,7 @@ mod tests {
|
||||
4,
|
||||
COMPACT_CODE,
|
||||
"Core_execute_block",
|
||||
&big_block().0
|
||||
&block_with_size(42, 0, 120_000).0
|
||||
).is_err()
|
||||
);
|
||||
}
|
||||
@@ -807,7 +807,7 @@ mod tests {
|
||||
Executor::new(None).call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&big_block().0,
|
||||
&block_with_size(42, 0, 120_000).0,
|
||||
true,
|
||||
None,
|
||||
).0.unwrap();
|
||||
@@ -821,7 +821,7 @@ mod tests {
|
||||
Executor::new(None).call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&big_block().0,
|
||||
&block_with_size(42, 0, 120_000).0,
|
||||
false,
|
||||
None,
|
||||
).0.is_err()
|
||||
@@ -832,10 +832,10 @@ mod tests {
|
||||
fn panic_execution_gives_error() {
|
||||
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, map![
|
||||
blake2_256(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
0_u128.encode()
|
||||
},
|
||||
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => {
|
||||
vec![0u8; 16]
|
||||
0_u128.encode()
|
||||
},
|
||||
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
|
||||
blake2_256(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
|
||||
@@ -872,7 +872,7 @@ mod tests {
|
||||
assert_eq!(r, Ok(ApplyOutcome::Success));
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * TX_FEE);
|
||||
assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - TX_FEE - TRANSFER_FEE);
|
||||
assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS);
|
||||
});
|
||||
}
|
||||
@@ -900,8 +900,7 @@ mod tests {
|
||||
let block1 = changes_trie_block();
|
||||
|
||||
let mut t = new_test_ext(COMPACT_CODE, true);
|
||||
WasmExecutor::new()
|
||||
.call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap();
|
||||
WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_execute_block", &block1.0).unwrap();
|
||||
|
||||
assert!(t.storage_changes_root(GENESIS_HASH.into()).unwrap().is_some());
|
||||
}
|
||||
@@ -921,6 +920,159 @@ mod tests {
|
||||
client.import(BlockOrigin::Own, block).unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn weight_multiplier_increases_on_big_block() {
|
||||
let mut t = new_test_ext(COMPACT_CODE, false);
|
||||
|
||||
let mut prev_multiplier = WeightMultiplier::default();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(System::next_weight_multiplier(), prev_multiplier);
|
||||
});
|
||||
|
||||
let mut tt = new_test_ext(COMPACT_CODE, false);
|
||||
let block1 = construct_block(
|
||||
&mut tt,
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: Call::Timestamp(timestamp::Call::set(42)),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
signed: Some((charlie(), 0)),
|
||||
function: Call::System(system::Call::remark(vec![0; (IDEAL_TRANSACTIONS_WEIGHT*2) as usize])),
|
||||
}
|
||||
]
|
||||
);
|
||||
let block2 = construct_block(
|
||||
&mut tt,
|
||||
2,
|
||||
block1.1.clone(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: Call::Timestamp(timestamp::Call::set(52)),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
signed: Some((charlie(), 1)),
|
||||
function: Call::System(system::Call::remark(vec![0; (IDEAL_TRANSACTIONS_WEIGHT*2) as usize])),
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// execute a big block.
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&block1.0,
|
||||
true,
|
||||
None,
|
||||
).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
let fm = System::next_weight_multiplier();
|
||||
println!("After a big block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm > prev_multiplier);
|
||||
prev_multiplier = fm;
|
||||
});
|
||||
|
||||
// execute a big block.
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&block2.0,
|
||||
true,
|
||||
None,
|
||||
).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
let fm = System::next_weight_multiplier();
|
||||
println!("After a big block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm > prev_multiplier);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_multiplier_decreases_on_small_block() {
|
||||
let mut t = new_test_ext(COMPACT_CODE, false);
|
||||
|
||||
let mut prev_multiplier = WeightMultiplier::default();
|
||||
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
assert_eq!(System::next_weight_multiplier(), prev_multiplier);
|
||||
});
|
||||
|
||||
let mut tt = new_test_ext(COMPACT_CODE, false);
|
||||
let block1 = construct_block(
|
||||
&mut tt,
|
||||
1,
|
||||
GENESIS_HASH.into(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: Call::Timestamp(timestamp::Call::set(42)),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
signed: Some((charlie(), 0)),
|
||||
function: Call::System(system::Call::remark(vec![0; 120])),
|
||||
}
|
||||
]
|
||||
);
|
||||
let block2 = construct_block(
|
||||
&mut tt,
|
||||
2,
|
||||
block1.1.clone(),
|
||||
vec![
|
||||
CheckedExtrinsic {
|
||||
signed: None,
|
||||
function: Call::Timestamp(timestamp::Call::set(52)),
|
||||
},
|
||||
CheckedExtrinsic {
|
||||
signed: Some((charlie(), 1)),
|
||||
function: Call::System(system::Call::remark(vec![0; 120])),
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// execute a big block.
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&block1.0,
|
||||
true,
|
||||
None,
|
||||
).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
let fm = System::next_weight_multiplier();
|
||||
println!("After a small block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm < prev_multiplier);
|
||||
prev_multiplier = fm;
|
||||
});
|
||||
|
||||
// execute a big block.
|
||||
executor().call::<_, NeverNativeValue, fn() -> _>(
|
||||
&mut t,
|
||||
"Core_execute_block",
|
||||
&block2.0,
|
||||
true,
|
||||
None,
|
||||
).0.unwrap();
|
||||
|
||||
// weight multiplier is increased for next block.
|
||||
runtime_io::with_externalities(&mut t, || {
|
||||
let fm = System::next_weight_multiplier();
|
||||
println!("After a small block: {:?} -> {:?}", prev_multiplier, fm);
|
||||
assert!(fm < prev_multiplier);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "benchmarks")]
|
||||
mod benches {
|
||||
use super::*;
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
// 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/>.
|
||||
|
||||
//! Some configurable implementations as associated type for the substrate runtime.
|
||||
|
||||
use node_primitives::Balance;
|
||||
use runtime_primitives::weights::{Weight, WeightMultiplier, MAX_TRANSACTIONS_WEIGHT, IDEAL_TRANSACTIONS_WEIGHT};
|
||||
use runtime_primitives::traits::{Convert, Saturating};
|
||||
use runtime_primitives::Fixed64;
|
||||
use crate::Balances;
|
||||
|
||||
/// Struct that handles the conversion of Balance -> `u64`. This is used for staking's election
|
||||
/// calculation.
|
||||
pub struct CurrencyToVoteHandler;
|
||||
|
||||
impl CurrencyToVoteHandler {
|
||||
fn factor() -> Balance { (Balances::total_issuance() / u64::max_value() as Balance).max(1) }
|
||||
}
|
||||
|
||||
impl Convert<Balance, u64> for CurrencyToVoteHandler {
|
||||
fn convert(x: Balance) -> u64 { (x / Self::factor()) as u64 }
|
||||
}
|
||||
|
||||
impl Convert<u128, Balance> for CurrencyToVoteHandler {
|
||||
fn convert(x: u128) -> Balance { x * Self::factor() }
|
||||
}
|
||||
|
||||
/// A struct that updates the weight multiplier based on the saturation level of the previous block.
|
||||
/// This should typically be called once per-block.
|
||||
///
|
||||
/// This assumes that weight is a numeric value in the u32 range.
|
||||
///
|
||||
/// Formula:
|
||||
/// diff = (ideal_weight - current_block_weight)
|
||||
/// v = 0.00004
|
||||
/// next_weight = weight * (1 + (v . diff) + (v . diff)^2 / 2)
|
||||
///
|
||||
/// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#relay-chain-transaction-fees
|
||||
pub struct WeightMultiplierUpdateHandler;
|
||||
impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierUpdateHandler {
|
||||
fn convert(previous_state: (Weight, WeightMultiplier)) -> WeightMultiplier {
|
||||
let (block_weight, multiplier) = previous_state;
|
||||
let ideal = IDEAL_TRANSACTIONS_WEIGHT as u128;
|
||||
let block_weight = block_weight as u128;
|
||||
|
||||
// determines if the first_term is positive
|
||||
let positive = block_weight >= ideal;
|
||||
let diff_abs = block_weight.max(ideal) - block_weight.min(ideal);
|
||||
// diff is within u32, safe.
|
||||
let diff = Fixed64::from_rational(diff_abs as i64, MAX_TRANSACTIONS_WEIGHT as u64);
|
||||
let diff_squared = diff.saturating_mul(diff);
|
||||
|
||||
// 0.00004 = 4/100_000 = 40_000/10^9
|
||||
let v = Fixed64::from_rational(4, 100_000);
|
||||
// 0.00004^2 = 16/10^10 ~= 2/10^9. Taking the future /2 into account, then it is just 1 parts
|
||||
// from a billionth.
|
||||
let v_squared_2 = Fixed64::from_rational(1, 1_000_000_000);
|
||||
|
||||
let first_term = v.saturating_mul(diff);
|
||||
// It is very unlikely that this will exist (in our poor perbill estimate) but we are giving
|
||||
// it a shot.
|
||||
let second_term = v_squared_2.saturating_mul(diff_squared);
|
||||
|
||||
if positive {
|
||||
let excess = first_term.saturating_add(second_term);
|
||||
multiplier.saturating_add(WeightMultiplier::from_fixed(excess))
|
||||
} else {
|
||||
// first_term > second_term
|
||||
let negative = first_term - second_term;
|
||||
multiplier.saturating_sub(WeightMultiplier::from_fixed(negative))
|
||||
// despite the fact that apply_to saturates weight (final fee cannot go below 0)
|
||||
// it is crucially important to stop here and don't further reduce the weight fee
|
||||
// multiplier. While at -1, it means that the network is so un-congested that all
|
||||
// transactions are practically free. We stop here and only increase if the network
|
||||
// became more busy.
|
||||
.max(WeightMultiplier::from_rational(-1, 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use runtime_primitives::weights::{MAX_TRANSACTIONS_WEIGHT, IDEAL_TRANSACTIONS_WEIGHT, Weight};
|
||||
use runtime_primitives::Perbill;
|
||||
|
||||
// poc reference implementation.
|
||||
#[allow(dead_code)]
|
||||
fn weight_multiplier_update(block_weight: Weight) -> Perbill {
|
||||
let block_weight = block_weight as f32;
|
||||
let v: f32 = 0.00004;
|
||||
|
||||
// maximum tx weight
|
||||
let m = MAX_TRANSACTIONS_WEIGHT as f32;
|
||||
// Ideal saturation in terms of weight
|
||||
let ss = IDEAL_TRANSACTIONS_WEIGHT as f32;
|
||||
// Current saturation in terms of weight
|
||||
let s = block_weight;
|
||||
|
||||
let fm = 1.0 + (v * (s/m - ss/m)) + (v.powi(2) * (s/m - ss/m).powi(2)) / 2.0;
|
||||
// return a per-bill-like value.
|
||||
let fm = if fm >= 1.0 { fm - 1.0 } else { 1.0 - fm };
|
||||
Perbill::from_parts((fm * 1_000_000_000_f32) as u32)
|
||||
}
|
||||
|
||||
fn fm(parts: i64) -> WeightMultiplier {
|
||||
WeightMultiplier::from_parts(parts)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stateless_weight_mul() {
|
||||
// Light block. Fee is reduced a little.
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((1024, WeightMultiplier::default())),
|
||||
fm(-9990)
|
||||
);
|
||||
// a bit more. Fee is decreased less, meaning that the fee increases as the block grows.
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((1024 * 4, WeightMultiplier::default())),
|
||||
fm(-9960)
|
||||
);
|
||||
// ideal. Original fee.
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((IDEAL_TRANSACTIONS_WEIGHT as u32, WeightMultiplier::default())),
|
||||
fm(0)
|
||||
);
|
||||
// // More than ideal. Fee is increased.
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert(((IDEAL_TRANSACTIONS_WEIGHT * 2) as u32, WeightMultiplier::default())),
|
||||
fm(10000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stateful_weight_mul_grow_to_infinity() {
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((IDEAL_TRANSACTIONS_WEIGHT * 2, WeightMultiplier::default())),
|
||||
fm(10000)
|
||||
);
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((IDEAL_TRANSACTIONS_WEIGHT * 2, fm(10000))),
|
||||
fm(20000)
|
||||
);
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((IDEAL_TRANSACTIONS_WEIGHT * 2, fm(20000))),
|
||||
fm(30000)
|
||||
);
|
||||
// ...
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((IDEAL_TRANSACTIONS_WEIGHT * 2, fm(1_000_000_000))),
|
||||
fm(1_000_000_000 + 10000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stateful_weight_mil_collapse_to_minus_one() {
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((0, WeightMultiplier::default())),
|
||||
fm(-10000)
|
||||
);
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((0, fm(-10000))),
|
||||
fm(-20000)
|
||||
);
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((0, fm(-20000))),
|
||||
fm(-30000)
|
||||
);
|
||||
// ...
|
||||
assert_eq!(
|
||||
WeightMultiplierUpdateHandler::convert((0, fm(1_000_000_000 * -1))),
|
||||
fm(-1_000_000_000)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_to_fee_should_not_overflow_on_large_weights() {
|
||||
// defensive-only test. at the moment we are not allowing any weight more than
|
||||
// 4 * 1024 * 1024 in a block.
|
||||
let kb = 1024_u32;
|
||||
let mb = kb * kb;
|
||||
let max_fm = WeightMultiplier::from_fixed(Fixed64::from_natural(i64::max_value()));
|
||||
|
||||
vec![0, 1, 10, 1000, kb, 10 * kb, 100 * kb, mb, 10 * mb, Weight::max_value() / 2, Weight::max_value()]
|
||||
.into_iter()
|
||||
.for_each(|i| {
|
||||
WeightMultiplierUpdateHandler::convert((i, WeightMultiplier::default()));
|
||||
});
|
||||
|
||||
vec![10 * mb, Weight::max_value() / 2, Weight::max_value()]
|
||||
.into_iter()
|
||||
.for_each(|i| {
|
||||
let fm = WeightMultiplierUpdateHandler::convert((
|
||||
i,
|
||||
max_fm
|
||||
));
|
||||
// won't grow. The convert saturates everything.
|
||||
assert_eq!(fm, max_fm);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ use client::{
|
||||
use runtime_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_str, key_types};
|
||||
use runtime_primitives::transaction_validity::TransactionValidity;
|
||||
use runtime_primitives::traits::{
|
||||
BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, Convert,
|
||||
BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup,
|
||||
};
|
||||
use version::RuntimeVersion;
|
||||
use elections::VoteIndex;
|
||||
@@ -56,6 +56,11 @@ pub use runtime_primitives::{Permill, Perbill};
|
||||
pub use support::StorageValue;
|
||||
pub use staking::StakerStatus;
|
||||
|
||||
/// Implementations for `Convert` and other helper structs passed into runtime modules as associated
|
||||
/// types.
|
||||
pub mod impls;
|
||||
use impls::{CurrencyToVoteHandler, WeightMultiplierUpdateHandler};
|
||||
|
||||
// Make the WASM binary available.
|
||||
#[cfg(feature = "std")]
|
||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||
@@ -122,6 +127,7 @@ impl system::Trait for Runtime {
|
||||
type AccountId = AccountId;
|
||||
type Lookup = Indices;
|
||||
type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
type WeightMultiplierUpdate = WeightMultiplierUpdateHandler;
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
@@ -223,20 +229,6 @@ parameter_types! {
|
||||
pub const BondingDuration: staking::EraIndex = 24 * 28;
|
||||
}
|
||||
|
||||
pub struct CurrencyToVoteHandler;
|
||||
|
||||
impl CurrencyToVoteHandler {
|
||||
fn factor() -> u128 { (Balances::total_issuance() / u64::max_value() as u128).max(1) }
|
||||
}
|
||||
|
||||
impl Convert<u128, u64> for CurrencyToVoteHandler {
|
||||
fn convert(x: u128) -> u64 { (x / Self::factor()) as u64 }
|
||||
}
|
||||
|
||||
impl Convert<u128, u128> for CurrencyToVoteHandler {
|
||||
fn convert(x: u128) -> u128 { x * Self::factor() }
|
||||
}
|
||||
|
||||
impl staking::Trait for Runtime {
|
||||
type Currency = Balances;
|
||||
type CurrencyToVote = CurrencyToVoteHandler;
|
||||
|
||||
@@ -267,6 +267,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -348,6 +348,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1008,6 +1008,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -1119,6 +1119,7 @@ mod tests {
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
parameter_types! {
|
||||
|
||||
@@ -535,6 +535,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
()
|
||||
>;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -51,6 +51,7 @@ impl system::Trait for Test {
|
||||
type Lookup = IdentityLookup<u64>;
|
||||
type Header = Header;
|
||||
type Event = TestEvent;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -348,6 +348,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
@@ -380,6 +380,7 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user