diff --git a/Cargo.lock b/Cargo.lock index 1fd3c185340c1..c1aec02c00919 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2357,6 +2357,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-authority-discovery 2.0.0", "substrate-basic-authorship 2.0.0", @@ -2403,6 +2404,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "srml-treasury 2.0.0", "substrate-executor 2.0.0", "substrate-primitives 2.0.0", @@ -2488,6 +2490,7 @@ dependencies = [ "srml-system 2.0.0", "srml-system-rpc-runtime-api 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "srml-treasury 2.0.0", "srml-utility 2.0.0", "substrate-authority-discovery-primitives 2.0.0", @@ -2552,6 +2555,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", "substrate-offchain-primitives 2.0.0", @@ -2579,6 +2583,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "srml-transaction-payment 2.0.0", "srml-treasury 2.0.0", "substrate-client 2.0.0", "substrate-executor 2.0.0", @@ -3988,6 +3993,7 @@ dependencies = [ "sr-std 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-keyring 2.0.0", "substrate-primitives 2.0.0", ] @@ -4138,6 +4144,7 @@ dependencies = [ "srml-indices 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-primitives 2.0.0", ] @@ -4490,6 +4497,20 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-transaction-payment" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-balances 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-treasury" version = "2.0.0" @@ -4608,6 +4629,7 @@ dependencies = [ "sr-primitives 2.0.0", "srml-balances 2.0.0", "srml-system 2.0.0", + "srml-transaction-payment 2.0.0", "substrate-bip39 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", "tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 6508e2b41e994..5b012be0e98b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,7 @@ members = [ "srml/system/rpc", "srml/timestamp", "srml/treasury", + "srml/transaction-payment", "srml/utility", "node/cli", "node/executor", diff --git a/core/sr-primitives/src/sr_arithmetic.rs b/core/sr-primitives/src/sr_arithmetic.rs index 043a383a33244..5f5b5dc1e523b 100644 --- a/core/sr-primitives/src/sr_arithmetic.rs +++ b/core/sr-primitives/src/sr_arithmetic.rs @@ -543,7 +543,6 @@ implement_per_thing!( /// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] /// with fixed point accuracy of one billion. -#[cfg_attr(feature = "std", derive(Debug))] #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Fixed64(i64); @@ -668,6 +667,13 @@ impl CheckedAdd for Fixed64 { } } +#[cfg(feature = "std")] +impl rstd::fmt::Debug for Fixed64 { + fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + write!(f, "Fixed64({},{})", self.0 / DIV, (self.0 % DIV) / 1000) + } +} + /// Infinite precision unsigned integer for substrate runtime. pub mod biguint { use super::Zero; @@ -981,8 +987,6 @@ pub mod biguint { let mut q = Self::with_capacity(m + 1); let mut r = Self::with_capacity(n); - debug_assert!(other.msb() != 0); - // PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are // safe. let normalizer_bits = other.msb().leading_zeros() as Single; @@ -2136,6 +2140,35 @@ mod tests_fixed64 { assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); } + #[test] + fn fixed_64_growth_decrease_curve() { + let test_set = vec![0u32, 1, 10, 1000, 1_000_000_000]; + + // negative (1/2) + let mut fm = Fixed64::from_rational(-1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i) as i32, i as i32 - i as i32 / 2); + }); + + // unit (1) multiplier + fm = Fixed64::from_parts(0); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i); + }); + + // i.5 multiplier + fm = Fixed64::from_rational(1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 3 / 2); + }); + + // dual multiplier + fm = Fixed64::from_rational(1, 1); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 2); + }); + } + macro_rules! saturating_mul_acc_test { ($num_type:tt) => { assert_eq!( diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 28f1b2fe0cfc4..2ffd4ee3917e1 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -23,9 +23,6 @@ //! 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}; - pub use crate::transaction_validity::TransactionPriority; use crate::traits::Bounded; @@ -167,76 +164,3 @@ impl Default for SimpleDispatchInfo { SimpleDispatchInfo::FixedNormal(10_000) } } - -/// 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::from_parts(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); }); - } -} diff --git a/core/test-runtime/src/lib.rs b/core/test-runtime/src/lib.rs index fddee22be22f6..3cb89de56f7d6 100644 --- a/core/test-runtime/src/lib.rs +++ b/core/test-runtime/src/lib.rs @@ -382,7 +382,6 @@ impl srml_system::Trait for Runtime { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 76821b6dfd8a2..96484c16aef91 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -24,6 +24,7 @@ randomness-collective-flip = { package = "srml-randomness-collective-flip", path 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 } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment", default_features = false } sr-primitives = { path = "../../core/sr-primitives", default_features = false } client = { package = "substrate-client", path = "../../core/client", default_features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false } @@ -51,6 +52,7 @@ std = [ "system/std", "timestamp/std", "sudo/std", + "transaction-payment/std", "version/std", "serde", "safe-mix/std", diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 0e9b8050f018f..da49ad4474cba 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -166,18 +166,17 @@ impl system::Trait for Runtime { type Header = generic::Header; /// 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). type BlockHashCount = BlockHashCount; - /// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok. + /// Maximum weight of each block. type MaximumBlockWeight = MaximumBlockWeight; /// Maximum size of all encoded transactions (in bytes) that are allowed in one block. type MaximumBlockLength = MaximumBlockLength; /// Portion of the block weight that is available to all normal transactions. type AvailableBlockRatio = AvailableBlockRatio; + /// Version of the runtime. type Version = Version; } @@ -223,8 +222,6 @@ parameter_types! { pub const ExistentialDeposit: u128 = 500; pub const TransferFee: u128 = 0; pub const CreationFee: u128 = 0; - pub const TransactionBaseFee: u128 = 0; - pub const TransactionByteFee: u128 = 1; } impl balances::Trait for Runtime { @@ -236,15 +233,25 @@ impl balances::Trait for Runtime { type OnNewAccount = Indices; /// The ubiquitous event type. type Event = Event; - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 0; + pub const TransactionByteFee: Balance = 1; +} + +impl transaction_payment::Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); } impl sudo::Trait for Runtime { @@ -269,6 +276,7 @@ construct_runtime!( Grandpa: grandpa::{Module, Call, Storage, Config, Event}, Indices: indices::{default, Config}, Balances: balances::{default, Error}, + TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo, // Used for the module template in `./template.rs` TemplateModule: template::{Module, Call, Storage, Event}, @@ -293,7 +301,7 @@ pub type SignedExtra = ( system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees + transaction_payment::ChargeTransactionPayment ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index eb4398787d5a1..8c6c35edc4810 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -100,7 +100,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index 0ddd7b49ff873..5fa92360d62cf 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -45,6 +45,7 @@ finality_tracker = { package = "srml-finality-tracker", path = "../../srml/final contracts = { package = "srml-contracts", path = "../../srml/contracts" } system = { package = "srml-system", path = "../../srml/system" } balances = { package = "srml-balances", path = "../../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } support = { package = "srml-support", path = "../../srml/support", default-features = false } im_online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } sr-authority-discovery = { package = "srml-authority-discovery", path = "../../srml/authority-discovery", default-features = false } diff --git a/node/cli/src/factory_impl.rs b/node/cli/src/factory_impl.rs index 827b8e308c8f1..84c24f2af090b 100644 --- a/node/cli/src/factory_impl.rs +++ b/node/cli/src/factory_impl.rs @@ -56,7 +56,7 @@ impl FactoryState { system::CheckEra::from(Era::mortal(256, phase)), system::CheckNonce::from(index), system::CheckWeight::new(), - balances::TakeFees::from(0), + transaction_payment::ChargeTransactionPayment::from(0), Default::default(), ) } diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 7012a3d6ce0ad..485cd325b0ec1 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -528,14 +528,14 @@ mod tests { let check_era = system::CheckEra::from(Era::Immortal); let check_nonce = system::CheckNonce::from(index); let check_weight = system::CheckWeight::new(); - let take_fees = balances::TakeFees::from(0); + let payment = transaction_payment::ChargeTransactionPayment::from(0); let extra = ( check_version, check_genesis, check_era, check_nonce, check_weight, - take_fees, + payment, Default::default(), ); let raw_payload = SignedPayload::from_raw( diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index 63f1c8c0f4f3a..1908443ca9519 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -22,6 +22,7 @@ test-client = { package = "substrate-test-client", path = "../../core/test-clien sr-primitives = { path = "../../core/sr-primitives" } runtime_support = { package = "srml-support", path = "../../srml/support" } balances = { package = "srml-balances", path = "../../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } session = { package = "srml-session", path = "../../srml/session" } system = { package = "srml-system", path = "../../srml/system" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp" } diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 2c2ad479f5c19..3558987716bd3 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -46,16 +46,16 @@ mod tests { traits::{CodeExecutor, Externalities}, storage::well_known_keys, }; use sr_primitives::{ - assert_eq_error_rate, + Fixed64, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyResult, - transaction_validity::InvalidTransaction, weights::{WeightMultiplier, GetDispatchInfo}, + transaction_validity::InvalidTransaction, weights::GetDispatchInfo, }; use contracts::ContractAddressFor; use substrate_executor::{NativeExecutor, WasmExecutionMethod}; use system::{EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, BuildStorage, - System, Event, TransferFee, TransactionBaseFee, TransactionByteFee, + System, TransactionPayment, Event, TransferFee, TransactionBaseFee, TransactionByteFee, constants::currency::*, impls::WeightToFee, }; use node_primitives::{Balance, Hash, BlockNumber}; @@ -88,17 +88,15 @@ mod tests { } /// Default transfer fee - fn transfer_fee(extrinsic: &E) -> Balance { + fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed64) -> Balance { let length_fee = TransactionBaseFee::get() + TransactionByteFee::get() * (extrinsic.encode().len() as Balance); let weight = default_transfer_call().get_dispatch_info().weight; - // NOTE: this is really hard to apply, since the multiplier of each block needs to be fetched - // before the block, while we compute this after the block. - // weight = >::next_weight_multiplier().apply_to(weight); - let weight_fee = ::WeightToFee::convert(weight); - length_fee + weight_fee + TransferFee::get() + let weight_fee = ::WeightToFee::convert(weight); + + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get() } fn default_transfer_call() -> balances::Call { @@ -217,6 +215,9 @@ mod tests { None, ).0; assert!(r.is_ok()); + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", @@ -227,7 +228,7 @@ mod tests { assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -253,6 +254,9 @@ mod tests { None, ).0; assert!(r.is_ok()); + + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); + let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", @@ -263,7 +267,7 @@ mod tests { assert!(r.is_ok()); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -425,6 +429,9 @@ mod tests { let (block1, block2) = blocks(); + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", @@ -434,8 +441,9 @@ mod tests { ).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), @@ -460,6 +468,9 @@ mod tests { ]; assert_eq!(System::events(), events); }); + + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", @@ -471,15 +482,13 @@ mod tests { t.execute_with(|| { // NOTE: fees differ slightly in tests that execute more than one block due to the // weight update. Hence, using `assert_eq_error_rate`. - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&alice()), - 32 * DOLLARS - 2 * transfer_fee(&xt()), - 10_000 + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), ); - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&bob()), - 179 * DOLLARS - transfer_fee(&xt()), - 10_000 + 179 * DOLLARS - transfer_fee(&xt(), fm), ); let events = vec![ EventRecord { @@ -532,6 +541,9 @@ mod tests { let (block1, block2) = blocks(); + let mut alice_last_known_balance: Balance = Default::default(); + let mut fm = t.execute_with(TransactionPayment::next_fee_multiplier); + executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", @@ -541,10 +553,13 @@ mod tests { ).0.unwrap(); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt())); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 169 * DOLLARS); + alice_last_known_balance = Balances::total_balance(&alice()); }); + fm = t.execute_with(TransactionPayment::next_fee_multiplier); + executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "Core_execute_block", @@ -554,15 +569,13 @@ mod tests { ).0.unwrap(); t.execute_with(|| { - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&alice()), - 32 * DOLLARS - 2 * transfer_fee(&xt()), - 10_000 + alice_last_known_balance - 10 * DOLLARS - transfer_fee(&xt(), fm), ); - assert_eq_error_rate!( + assert_eq!( Balances::total_balance(&bob()), - 179 * DOLLARS - 1 * transfer_fee(&xt()), - 10_000 + 179 * DOLLARS - 1 * transfer_fee(&xt(), fm), ); }); } @@ -824,6 +837,7 @@ mod tests { None, ).0; assert!(r.is_ok()); + let fm = t.execute_with(TransactionPayment::next_fee_multiplier); let r = executor().call::<_, NeverNativeValue, fn() -> _>( &mut t, "BlockBuilder_apply_extrinsic", @@ -837,7 +851,7 @@ mod tests { .expect("Extrinsic did not fail"); t.execute_with(|| { - assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt())); + assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - 1 * transfer_fee(&xt(), fm)); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); } @@ -890,13 +904,14 @@ mod tests { #[test] - fn weight_multiplier_increases_and_decreases_on_big_weight() { + fn fee_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); - let mut prev_multiplier = WeightMultiplier::default(); + // initial fee multiplier must be zero + let mut prev_multiplier = Fixed64::from_parts(0); t.execute_with(|| { - assert_eq!(System::next_weight_multiplier(), prev_multiplier); + assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); }); let mut tt = new_test_ext(COMPACT_CODE, false); @@ -948,7 +963,7 @@ mod tests { // weight multiplier is increased for next block. t.execute_with(|| { - let fm = System::next_weight_multiplier(); + let fm = TransactionPayment::next_fee_multiplier(); println!("After a big block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm > prev_multiplier); prev_multiplier = fm; @@ -965,7 +980,7 @@ mod tests { // weight multiplier is increased for next block. t.execute_with(|| { - let fm = System::next_weight_multiplier(); + let fm = TransactionPayment::next_fee_multiplier(); println!("After a small block: {:?} -> {:?}", prev_multiplier, fm); assert!(fm < prev_multiplier); }); @@ -978,7 +993,7 @@ mod tests { // weight of transfer call as of now: 1_000_000 // if weight of the cheapest weight would be 10^7, this would be 10^9, which is: // - 1 MILLICENTS in substrate node. - // - 1 milldot based on current polkadot runtime. + // - 1 milli-dot based on current polkadot runtime. // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) let mut t = TestExternalities::::new_with_code(COMPACT_CODE, (map![ >::hashed_key_for(alice()) => { @@ -1052,6 +1067,7 @@ mod tests { fn block_weight_capacity_report() { // Just report how many transfer calls you could fit into a block. The number should at least // be a few hundred (250 at the time of writing but can change over time). Runs until panic. + use node_primitives::Index; // execution ext. let mut t = new_test_ext(COMPACT_CODE, false); @@ -1118,6 +1134,7 @@ mod tests { // Just report how big a block can get. Executes until panic. Should be ignored unless if // manually inspected. The number should at least be a few megabytes (5 at the time of // writing but can change over time). + use node_primitives::Index; // execution ext. let mut t = new_test_ext(COMPACT_CODE, false); diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index f9818c096e8e5..af66c0432f33d 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -52,6 +52,7 @@ system-rpc-runtime-api = { package = "srml-system-rpc-runtime-api", path = "../. timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false } utility = { package = "srml-utility", path = "../../srml/utility", default-features = false } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment", default-features = false } [build-dependencies] wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../core/utils/wasm-builder-runner" } @@ -100,5 +101,6 @@ std = [ "timestamp/std", "treasury/std", "utility/std", + "transaction-payment/std", "version/std", ] diff --git a/node/runtime/src/constants.rs b/node/runtime/src/constants.rs index 79d3dbd8eb2bc..ca57bdc8ba9fc 100644 --- a/node/runtime/src/constants.rs +++ b/node/runtime/src/constants.rs @@ -67,10 +67,10 @@ pub mod time { pub const DAYS: BlockNumber = HOURS * 24; } -// CRITICAL NOTE: The system module maintains two constants: a _maximum_ block weight and a -// _ratio_ of it yielding the portion which is accessible to normal transactions (reserving the rest -// for operational ones). `TARGET_BLOCK_FULLNESS` is entirely independent and the system module is -// not aware of if, nor should it care about it. This constant simply denotes on which ratio of the +// CRITICAL NOTE: The system module maintains two constants: a _maximum_ block weight and a _ratio_ +// of it yielding the portion which is accessible to normal transactions (reserving the rest for +// operational ones). `TARGET_BLOCK_FULLNESS` is entirely independent and the system module is not +// aware of if, nor should it care about it. This constant simply denotes on which ratio of the // _maximum_ block weight we tweak the fees. It does NOT care about the type of the dispatch. // // For the system to be configured in a sane way, `TARGET_BLOCK_FULLNESS` should always be less than diff --git a/node/runtime/src/impls.rs b/node/runtime/src/impls.rs index 2e1fcc8826e03..219384502032c 100644 --- a/node/runtime/src/impls.rs +++ b/node/runtime/src/impls.rs @@ -17,7 +17,7 @@ //! Some configurable implementations as associated type for the substrate runtime. use node_primitives::Balance; -use sr_primitives::weights::{Weight, WeightMultiplier}; +use sr_primitives::weights::Weight; use sr_primitives::traits::{Convert, Saturating}; use sr_primitives::Fixed64; use support::traits::{OnUnbalanced, Currency}; @@ -82,10 +82,10 @@ impl Convert for WeightToFee { /// 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; +pub struct FeeMultiplierUpdateHandler; -impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierUpdateHandler { - fn convert(previous_state: (Weight, WeightMultiplier)) -> WeightMultiplier { +impl Convert<(Weight, Fixed64), Fixed64> for FeeMultiplierUpdateHandler { + fn convert(previous_state: (Weight, Fixed64)) -> Fixed64 { let (block_weight, multiplier) = previous_state; let max_weight = MaximumBlockWeight::get(); let target_weight = (TARGET_BLOCK_FULLNESS * max_weight) as u128; @@ -113,17 +113,17 @@ impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierU // Note: this is merely bounded by how big the multiplier and the inner value can go, // not by any economical reasoning. let excess = first_term.saturating_add(second_term); - multiplier.saturating_add(WeightMultiplier::from_fixed(excess)) + multiplier.saturating_add(excess) } else { - // first_term > second_term + // Proof: first_term > second_term. Safe subtraction. let negative = first_term - second_term; - multiplier.saturating_sub(WeightMultiplier::from_fixed(negative)) + multiplier.saturating_sub(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 have no weight fee. We stop here and only increase if the network // became more busy. - .max(WeightMultiplier::from_rational(-1, 1)) + .max(Fixed64::from_rational(-1, 1)) } } } @@ -132,7 +132,6 @@ impl Convert<(Weight, WeightMultiplier), WeightMultiplier> for WeightMultiplierU mod tests { use super::*; use sr_primitives::weights::Weight; - use sr_primitives::Perbill; use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime}; use crate::constants::currency::*; @@ -145,8 +144,7 @@ mod tests { } // poc reference implementation. - #[allow(dead_code)] - fn weight_multiplier_update(block_weight: Weight) -> Perbill { + fn fee_multiplier_update(block_weight: Weight, previous: Fixed64) -> Fixed64 { let block_weight = block_weight as f32; let v: f32 = 0.00004; @@ -157,53 +155,84 @@ mod tests { // 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) + let fm = (v * (s/m - ss/m)) + (v.powi(2) * (s/m - ss/m).powi(2)) / 2.0; + let addition_fm = Fixed64::from_parts((fm * 1_000_000_000_f32) as i64); + previous.saturating_add(addition_fm) } - fn wm(parts: i64) -> WeightMultiplier { - WeightMultiplier::from_parts(parts) + fn fm(parts: i64) -> Fixed64 { + Fixed64::from_parts(parts) + } + + #[test] + fn fee_multiplier_update_poc_works() { + let fm = Fixed64::from_rational(0, 1); + let test_set = vec![ + // TODO: this has a rounding error and fails. + // (0, fm.clone()), + (100, fm.clone()), + (target(), fm.clone()), + (max() / 2, fm.clone()), + (max(), fm.clone()), + ]; + test_set.into_iter().for_each(|(w, fm)| { + assert_eq!( + fee_multiplier_update(w, fm), + FeeMultiplierUpdateHandler::convert((w, fm)), + "failed for weight {} and prev fm {:?}", + w, + fm, + ); + }) } #[test] fn empty_chain_simulation() { // just a few txs per_block. let block_weight = 1000; - let mut wm = WeightMultiplier::default(); + let mut fm = Fixed64::default(); let mut iterations: u64 = 0; loop { - let next = WeightMultiplierUpdateHandler::convert((block_weight, wm)); - wm = next; - if wm == WeightMultiplier::from_rational(-1, 1) { break; } + let next = FeeMultiplierUpdateHandler::convert((block_weight, fm)); + fm = next; + if fm == Fixed64::from_rational(-1, 1) { break; } iterations += 1; } - println!("iteration {}, new wm = {:?}. Weight fee is now zero", iterations, wm); + println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm); } #[test] #[ignore] fn congested_chain_simulation() { // `cargo test congested_chain_simulation -- --nocapture` to get some insight. + // almost full. The entire quota of normal transactions is taken. let block_weight = AvailableBlockRatio::get() * max(); - let tx_weight = 1000; - let mut wm = WeightMultiplier::default(); + + // default minimum substrate weight + let tx_weight = 10_000u32; + + // initial value of system + let mut fm = Fixed64::default(); + assert_eq!(fm, Fixed64::from_parts(0)); + let mut iterations: u64 = 0; loop { - let next = WeightMultiplierUpdateHandler::convert((block_weight, wm)); - if wm == next { break; } - wm = next; + let next = FeeMultiplierUpdateHandler::convert((block_weight, fm)); + if fm == next { break; } + fm = next; iterations += 1; - let fee = ::WeightToFee::convert(wm.apply_to(tx_weight)); + let fee = ::WeightToFee::convert(tx_weight); + let adjusted_fee = fm.saturated_multiply_accumulate(fee); println!( - "iteration {}, new wm = {:?}. Fee at this point is: {} millicents, {} cents, {} dollars", + "iteration {}, new fm = {:?}. Fee at this point is: \ + {} units, {} millicents, {} cents, {} dollars", iterations, - wm, - fee / MILLICENTS, - fee / CENTS, - fee / DOLLARS + fm, + adjusted_fee, + adjusted_fee / MILLICENTS, + adjusted_fee / CENTS, + adjusted_fee / DOLLARS ); } } @@ -212,65 +241,65 @@ mod tests { fn stateless_weight_mul() { // Light block. Fee is reduced a little. assert_eq!( - WeightMultiplierUpdateHandler::convert((target() / 4, WeightMultiplier::default())), - wm(-7500) + FeeMultiplierUpdateHandler::convert((target() / 4, Fixed64::default())), + fm(-7500) ); // a bit more. Fee is decreased less, meaning that the fee increases as the block grows. assert_eq!( - WeightMultiplierUpdateHandler::convert((target() / 2, WeightMultiplier::default())), - wm(-5000) + FeeMultiplierUpdateHandler::convert((target() / 2, Fixed64::default())), + fm(-5000) ); // ideal. Original fee. No changes. assert_eq!( - WeightMultiplierUpdateHandler::convert((target(), WeightMultiplier::default())), - wm(0) + FeeMultiplierUpdateHandler::convert((target(), Fixed64::default())), + fm(0) ); // // More than ideal. Fee is increased. assert_eq!( - WeightMultiplierUpdateHandler::convert(((target() * 2), WeightMultiplier::default())), - wm(10000) + FeeMultiplierUpdateHandler::convert(((target() * 2), Fixed64::default())), + fm(10000) ); } #[test] fn stateful_weight_mul_grow_to_infinity() { assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, WeightMultiplier::default())), - wm(10000) + FeeMultiplierUpdateHandler::convert((target() * 2, Fixed64::default())), + fm(10000) ); assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(10000))), - wm(20000) + FeeMultiplierUpdateHandler::convert((target() * 2, fm(10000))), + fm(20000) ); assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(20000))), - wm(30000) + FeeMultiplierUpdateHandler::convert((target() * 2, fm(20000))), + fm(30000) ); // ... assert_eq!( - WeightMultiplierUpdateHandler::convert((target() * 2, wm(1_000_000_000))), - wm(1_000_000_000 + 10000) + FeeMultiplierUpdateHandler::convert((target() * 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())), - wm(-10000) + FeeMultiplierUpdateHandler::convert((0, Fixed64::default())), + fm(-10000) ); assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(-10000))), - wm(-20000) + FeeMultiplierUpdateHandler::convert((0, fm(-10000))), + fm(-20000) ); assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(-20000))), - wm(-30000) + FeeMultiplierUpdateHandler::convert((0, fm(-20000))), + fm(-30000) ); // ... assert_eq!( - WeightMultiplierUpdateHandler::convert((0, wm(1_000_000_000 * -1))), - wm(-1_000_000_000) + FeeMultiplierUpdateHandler::convert((0, fm(1_000_000_000 * -1))), + fm(-1_000_000_000) ); } @@ -278,20 +307,30 @@ mod tests { fn weight_to_fee_should_not_overflow_on_large_weights() { let kb = 1024 as Weight; 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())); - }); + let max_fm = 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| { + FeeMultiplierUpdateHandler::convert((i, Fixed64::default())); + }); // Some values that are all above the target and will cause an increase. let t = target(); vec![t + 100, t * 2, t * 4] .into_iter() .for_each(|i| { - let fm = WeightMultiplierUpdateHandler::convert(( + let fm = FeeMultiplierUpdateHandler::convert(( i, max_fm )); diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 63fc856013816..fdf0bfdc144bb 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -65,7 +65,7 @@ pub use staking::StakerStatus; /// Implementations of some helper traits passed into runtime modules as associated types. pub mod impls; -use impls::{CurrencyToVoteHandler, WeightMultiplierUpdateHandler, Author, WeightToFee}; +use impls::{CurrencyToVoteHandler, FeeMultiplierUpdateHandler, Author, WeightToFee}; /// Constant values used within the runtime. pub mod constants; @@ -84,8 +84,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 177, - impl_version: 177, + spec_version: 178, + impl_version: 178, apis: RUNTIME_API_VERSIONS, }; @@ -125,7 +125,6 @@ impl system::Trait for Runtime { type AccountId = AccountId; type Lookup = Indices; type Header = generic::Header; - type WeightMultiplierUpdate = WeightMultiplierUpdateHandler; type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -161,8 +160,6 @@ parameter_types! { pub const ExistentialDeposit: Balance = 1 * DOLLARS; pub const TransferFee: Balance = 1 * CENTS; pub const CreationFee: Balance = 1 * CENTS; - pub const TransactionBaseFee: Balance = 1 * CENTS; - pub const TransactionByteFee: Balance = 10 * MILLICENTS; } impl balances::Trait for Runtime { @@ -170,15 +167,25 @@ impl balances::Trait for Runtime { type OnFreeBalanceZero = ((Staking, Contracts), Session); type OnNewAccount = Indices; type Event = Event; - type TransactionPayment = DealWithFees; type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; +} + +parameter_types! { + pub const TransactionBaseFee: Balance = 1 * CENTS; + pub const TransactionByteFee: Balance = 10 * MILLICENTS; +} + +impl transaction_payment::Trait for Runtime { + type Currency = Balances; + type OnTransactionPayment = DealWithFees; type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = WeightToFee; + type FeeMultiplierUpdate = FeeMultiplierUpdateHandler; } parameter_types! { @@ -480,7 +487,7 @@ impl system::offchain::CreateTransaction for Runtim system::CheckEra::::from(generic::Era::mortal(period, current_block)), system::CheckNonce::::from(index), system::CheckWeight::::new(), - balances::TakeFees::::from(tip), + transaction_payment::ChargeTransactionPayment::::from(tip), Default::default(), ); let raw_payload = SignedPayload::new(call, extra).ok()?; @@ -504,6 +511,7 @@ construct_runtime!( Authorship: authorship::{Module, Call, Storage, Inherent}, Indices: indices, Balances: balances::{default, Error}, + TransactionPayment: transaction_payment::{Module, Storage}, Staking: staking::{default, OfflineWorker}, Session: session::{Module, Call, Storage, Event, Config}, Democracy: democracy::{Module, Call, Storage, Config, Event}, @@ -540,7 +548,7 @@ pub type SignedExtra = ( system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees, + transaction_payment::ChargeTransactionPayment, contracts::CheckBlockGasLimit, ); /// Unchecked extrinsic type as expected by this runtime. diff --git a/node/testing/Cargo.toml b/node/testing/Cargo.toml index e622e35d4aad0..8a4c08ed11bd2 100644 --- a/node/testing/Cargo.toml +++ b/node/testing/Cargo.toml @@ -27,4 +27,5 @@ system = { package = "srml-system", path = "../../srml/system" } test-client = { package = "substrate-test-client", path = "../../core/test-client" } timestamp = { package = "srml-timestamp", path = "../../srml/timestamp" } treasury = { package = "srml-treasury", path = "../../srml/treasury" } +transaction-payment = { package = "srml-transaction-payment", path = "../../srml/transaction-payment" } wabt = "0.9.2" diff --git a/node/testing/src/keyring.rs b/node/testing/src/keyring.rs index 0c6eb478cc5a0..853ca0ef6d7a0 100644 --- a/node/testing/src/keyring.rs +++ b/node/testing/src/keyring.rs @@ -72,7 +72,7 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra { system::CheckEra::from(Era::mortal(256, 0)), system::CheckNonce::from(nonce), system::CheckWeight::new(), - balances::TakeFees::from(extra_fee), + transaction_payment::ChargeTransactionPayment::from(extra_fee), Default::default(), ) } diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index de95caf88f191..37845c3a14659 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -271,7 +271,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index 7ebfd2a533304..5c55e8bdd58df 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -54,7 +54,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/authority-discovery/src/lib.rs b/srml/authority-discovery/src/lib.rs index 177e11c4c0657..2c923103a8b5c 100644 --- a/srml/authority-discovery/src/lib.rs +++ b/srml/authority-discovery/src/lib.rs @@ -204,7 +204,6 @@ mod tests { type AccountId = AuthorityId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index dcd1985664283..b42104a26b972 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -442,7 +442,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/babe/src/mock.rs b/srml/babe/src/mock.rs index 582299f11cf5c..5a6b80a2a6717 100644 --- a/srml/babe/src/mock.rs +++ b/srml/babe/src/mock.rs @@ -62,7 +62,6 @@ impl system::Trait for Test { type AccountId = DummyValidatorId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/balances/Cargo.toml b/srml/balances/Cargo.toml index d2b6e00d98e8b..f7f6041c6bb16 100644 --- a/srml/balances/Cargo.toml +++ b/srml/balances/Cargo.toml @@ -17,6 +17,7 @@ system = { package = "srml-system", path = "../system", default-features = false [dev-dependencies] runtime-io = { package = "sr-io", path = "../../core/sr-io" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } +transaction-payment = { package = "srml-transaction-payment", path = "../transaction-payment" } [features] default = ["std"] diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index b75c7a96dea4d..4f8edc5428d1c 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -86,17 +86,6 @@ //! //! - `vesting_balance` - Get the amount that is currently being vested and cannot be transferred out of this account. //! -//! ### Signed Extensions -//! -//! The balances module defines the following extensions: -//! -//! - [`TakeFees`]: Consumes fees proportional to the length and weight of the transaction. -//! Additionally, it can contain a single encoded payload as a `tip`. The inclusion priority -//! is increased proportional to the tip. -//! -//! Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed -//! extensions included in a chain. -//! //! ## Usage //! //! The following examples show how to use the Balances module in your custom module. @@ -171,15 +160,11 @@ use support::{ dispatch::Result, }; use sr_primitives::{ - transaction_validity::{ - TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, - TransactionValidity, - }, traits::{ Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, - Saturating, Bounded, SignedExtension, SaturatedConversion, Convert, + Saturating, Bounded, }, - weights::{DispatchInfo, SimpleDispatchInfo, Weight}, + weights::SimpleDispatchInfo, }; use system::{IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; @@ -210,15 +195,6 @@ pub trait Subtrait: system::Trait { /// The fee required to create an account. type CreationFee: Get; - - /// The fee to be paid for making a transaction; the base. - type TransactionBaseFee: Get; - - /// The fee to be paid for making a transaction; the per-byte portion. - type TransactionByteFee: Get; - - /// Convert a weight value into a deductible fee based on the currency type. - type WeightToFee: Convert; } pub trait Trait: system::Trait { @@ -235,9 +211,6 @@ pub trait Trait: system::Trait { /// Handler for when a new account is created. type OnNewAccount: OnNewAccount; - /// Handler for the unbalanced reduction when taking transaction fees. - type TransactionPayment: OnUnbalanced>; - /// Handler for the unbalanced reduction when taking fees associated with balance /// transfer (which may also include account creation). type TransferPayment: OnUnbalanced>; @@ -256,15 +229,6 @@ pub trait Trait: system::Trait { /// The fee required to create an account. type CreationFee: Get; - - /// The fee to be paid for making a transaction; the base. - type TransactionBaseFee: Get; - - /// The fee to be paid for making a transaction; the per-byte portion. - type TransactionByteFee: Get; - - /// Convert a weight value into a deductible fee based on the currency type. - type WeightToFee: Convert; } impl, I: Instance> Subtrait for T { @@ -274,9 +238,6 @@ impl, I: Instance> Subtrait for T { type ExistentialDeposit = T::ExistentialDeposit; type TransferFee = T::TransferFee; type CreationFee = T::CreationFee; - type TransactionBaseFee = T::TransactionBaseFee; - type TransactionByteFee = T::TransactionByteFee; - type WeightToFee = T::WeightToFee; } decl_event!( @@ -414,12 +375,6 @@ decl_module! { /// The fee required to create an account. const CreationFee: T::Balance = T::CreationFee::get(); - /// The fee to be paid for making a transaction; the base. - const TransactionBaseFee: T::Balance = T::TransactionBaseFee::get(); - - /// The fee to be paid for making a transaction; the per-byte portion. - const TransactionByteFee: T::Balance = T::TransactionByteFee::get(); - fn deposit_event() = default; /// Transfer some liquid free balance to another account. @@ -776,7 +731,7 @@ mod imbalances { // This works as long as `increase_total_issuance_by` doesn't use the Imbalance // types (basically for charging fees). // This should eventually be refactored so that the three type items that do -// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// depend on the Imbalance type (TransferPayment, DustRemoval) // are placed in their own SRML module. struct ElevatedTrait, I: Instance>(T, I); impl, I: Instance> Clone for ElevatedTrait { @@ -796,7 +751,6 @@ impl, I: Instance> system::Trait for ElevatedTrait { type AccountId = T::AccountId; type Lookup = T::Lookup; type Header = T::Header; - type WeightMultiplierUpdate = T::WeightMultiplierUpdate; type Event = (); type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; @@ -809,15 +763,11 @@ impl, I: Instance> Trait for ElevatedTrait { type OnFreeBalanceZero = T::OnFreeBalanceZero; type OnNewAccount = T::OnNewAccount; type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = T::ExistentialDeposit; type TransferFee = T::TransferFee; type CreationFee = T::CreationFee; - type TransactionBaseFee = T::TransactionBaseFee; - type TransactionByteFee = T::TransactionByteFee; - type WeightToFee = T::WeightToFee; } impl, I: Instance> Currency for Module @@ -1194,91 +1144,6 @@ where } } -/// Require the transactor pay for themselves and maybe include a tip to gain additional priority -/// in the queue. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct TakeFees, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance); - -impl, I: Instance> TakeFees { - /// utility constructor. Used only in client/factory code. - pub fn from(fee: T::Balance) -> Self { - Self(fee) - } - - /// Compute the final fee value for a particular transaction. - /// - /// The final fee is composed of: - /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. - /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike - /// size-fee, this is not input dependent and reflects the _complexity_ of the execution - /// and the time it consumes. - /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed - /// transactions can have a tip. - fn compute_fee(len: usize, info: DispatchInfo, tip: T::Balance) -> T::Balance { - let len_fee = if info.pay_length_fee() { - let len = T::Balance::from(len as u32); - let base = T::TransactionBaseFee::get(); - let per_byte = T::TransactionByteFee::get(); - base.saturating_add(per_byte.saturating_mul(len)) - } else { - Zero::zero() - }; - - let weight_fee = { - // cap the weight to the maximum defined in runtime, otherwise it will be the `Bounded` - // maximum of its data type, which is not desired. - let capped_weight = info.weight.min(::MaximumBlockWeight::get()); - let weight_update = >::next_weight_multiplier(); - let adjusted_weight = weight_update.apply_to(capped_weight); - T::WeightToFee::convert(adjusted_weight) - }; - - len_fee.saturating_add(weight_fee).saturating_add(tip) - } -} - -#[cfg(feature = "std")] -impl, I: Instance> rstd::fmt::Debug for TakeFees { - fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { - self.0.fmt(f) - } -} - -impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { - type AccountId = T::AccountId; - type Call = T::Call; - type AdditionalSigned = (); - type Pre = (); - fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) } - - fn validate( - &self, - who: &Self::AccountId, - _call: &Self::Call, - info: DispatchInfo, - len: usize, - ) -> TransactionValidity { - // pay any fees. - let fee = Self::compute_fee(len, info, self.0); - let imbalance = match >::withdraw( - who, - fee, - WithdrawReason::TransactionPayment, - ExistenceRequirement::KeepAlive, - ) { - Ok(imbalance) => imbalance, - Err(_) => return InvalidTransaction::Payment.into(), - }; - T::TransactionPayment::on_unbalanced(imbalance); - - let mut r = ValidTransaction::default(); - // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which - // will be a bit more than setting the priority to tip. For now, this is enough. - r.priority = fee.saturated_into::(); - Ok(r) - } -} - impl, I: Instance> IsDeadAccount for Module where T::Balance: MaybeSerializeDebug diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index a909795178913..600d0e6fb7879 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -18,7 +18,7 @@ #![cfg(test)] -use sr_primitives::{Perbill, traits::{Convert, IdentityLookup}, testing::Header, +use sr_primitives::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header, weights::{DispatchInfo, Weight}}; use primitives::H256; use runtime_io; @@ -35,10 +35,6 @@ thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static TRANSFER_FEE: RefCell = RefCell::new(0); static CREATION_FEE: RefCell = RefCell::new(0); - static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); - static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); - static TRANSACTION_WEIGHT_FEE: RefCell = RefCell::new(1); - static WEIGHT_TO_FEE: RefCell = RefCell::new(1); } pub struct ExistentialDeposit; @@ -56,23 +52,6 @@ impl Get for CreationFee { fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } } -pub struct TransactionBaseFee; -impl Get for TransactionBaseFee { - fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) } -} - -pub struct TransactionByteFee; -impl Get for TransactionByteFee { - fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) } -} - -pub struct WeightToFee(u64); -impl Convert for WeightToFee { - fn convert(t: Weight) -> u64 { - WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64)) - } -} - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Runtime; @@ -92,7 +71,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -100,26 +78,31 @@ impl system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); } +parameter_types! { + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 1; +} +impl transaction_payment::Trait for Runtime { + type Currency = Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); +} impl Trait for Runtime { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = WeightToFee; } pub struct ExtBuilder { - transaction_base_fee: u64, - transaction_byte_fee: u64, - weight_to_fee: u64, existential_deposit: u64, transfer_fee: u64, creation_fee: u64, @@ -129,9 +112,6 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { - transaction_base_fee: 0, - transaction_byte_fee: 0, - weight_to_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, @@ -141,12 +121,6 @@ impl Default for ExtBuilder { } } impl ExtBuilder { - pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64, weight_fee: u64) -> Self { - self.transaction_base_fee = base_fee; - self.transaction_byte_fee = byte_fee; - self.weight_to_fee = weight_fee; - self - } pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { self.existential_deposit = existential_deposit; self @@ -175,9 +149,6 @@ impl ExtBuilder { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); - TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.transaction_base_fee); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.transaction_byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); } pub fn build(self) -> runtime_io::TestExternalities { self.set_associated_consts(); @@ -211,7 +182,6 @@ impl ExtBuilder { pub type System = system::Module; pub type Balances = Module; - pub const CALL: &::Call = &(); /// create a transaction info struct from weight. Handy to avoid building the whole struct. diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 0b119d3ae6508..839ac67991c83 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -20,12 +20,13 @@ use super::*; use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight, CALL}; +use sr_primitives::traits::SignedExtension; use support::{ assert_noop, assert_ok, assert_err, traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, Currency, ReservableCurrency} }; -use sr_primitives::weights::DispatchClass; +use transaction_payment::ChargeTransactionPayment; use system::RawOrigin; const ID_1: LockIdentifier = *b"1 "; @@ -115,7 +116,6 @@ fn lock_reasons_should_work() { ExtBuilder::default() .existential_deposit(1) .monied(true) - .transaction_fees(0, 1, 0) .build() .execute_with(|| { Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); @@ -125,8 +125,8 @@ fn lock_reasons_should_work() { ); assert_ok!(>::reserve(&1, 1)); // NOTE: this causes a fee payment. - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(1), + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), &1, CALL, info_from_weight(1), @@ -139,8 +139,8 @@ fn lock_reasons_should_work() { >::reserve(&1, 1), "account liquidity restrictions prevent withdrawal" ); - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(1), + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), &1, CALL, info_from_weight(1), @@ -150,8 +150,8 @@ fn lock_reasons_should_work() { Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); assert_ok!(>::transfer(&1, &2, 1)); assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::pre_dispatch( - TakeFees::from(1), + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), &1, CALL, info_from_weight(1), @@ -736,89 +736,6 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { }); } -#[test] -fn signed_extension_take_fees_work() { - ExtBuilder::default() - .existential_deposit(10) - .transaction_fees(10, 1, 5) - .monied(true) - .build() - .execute_with(|| { - let len = 10; - assert!( - TakeFees::::from(0) - .pre_dispatch(&1, CALL, info_from_weight(5), len) - .is_ok() - ); - assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); - assert!( - TakeFees::::from(5 /* tipped */) - .pre_dispatch(&1, CALL, info_from_weight(3), len) - .is_ok() - ); - assert_eq!(Balances::free_balance(&1), 100 - 20 - 25 - 20 - 5 - 15); - }); -} - -#[test] -fn signed_extension_take_fees_is_bounded() { - ExtBuilder::default() - .existential_deposit(1000) - .transaction_fees(0, 0, 1) - .monied(true) - .build() - .execute_with(|| { - use sr_primitives::weights::Weight; - - // maximum weight possible - assert!( - TakeFees::::from(0) - .pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10) - .is_ok() - ); - // fee will be proportional to what is the actual maximum weight in the runtime. - assert_eq!( - Balances::free_balance(&1), - (10000 - ::MaximumBlockWeight::get()) as u64 - ); - }); -} - -#[test] -fn signed_extension_allows_free_transactions() { - ExtBuilder::default() - .transaction_fees(100, 1, 1) - .monied(false) - .build() - .execute_with(|| { - // 1 ain't have a penny. - assert_eq!(Balances::free_balance(&1), 0); - - // like a FreeOperational - let operational_transaction = DispatchInfo { - weight: 0, - class: DispatchClass::Operational - }; - let len = 100; - assert!( - TakeFees::::from(0) - .validate(&1, CALL, operational_transaction , len) - .is_ok() - ); - - // like a FreeNormal - let free_transaction = DispatchInfo { - weight: 0, - class: DispatchClass::Normal - }; - assert!( - TakeFees::::from(0) - .validate(&1, CALL, free_transaction , len) - .is_err() - ); - }); -} - #[test] fn burn_must_work() { ExtBuilder::default().monied(true).build().execute_with(|| { diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index 2de6243dd6a9b..edfc4bf46f9b1 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -406,7 +406,6 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index 678fc65d769cb..3d8a455ad7b83 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -442,7 +442,7 @@ where } /// The default dispatch fee computor computes the fee in the same way that -/// the implementation of `TakeFees` for the Balances module does. Note that this only takes a fixed +/// the implementation of `ChargeTransactionPayment` for the Balances module does. Note that this only takes a fixed /// fee based on size. Unlike the balances module, weight-fee is applied. pub struct DefaultDispatchFeeComputor(PhantomData); impl ComputeDispatchFee<::Call, BalanceOf> for DefaultDispatchFeeComputor { diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index d6de2ce3bd6f1..d3b52c334ba3c 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -96,8 +96,6 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: u32 = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; - pub const BalancesTransactionBaseFee: u64 = 0; - pub const BalancesTransactionByteFee: u64 = 0; pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl system::Trait for Test { @@ -110,7 +108,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -123,15 +120,11 @@ impl balances::Trait for Test { type OnFreeBalanceZero = Contract; type OnNewAccount = (); type Event = MetaEvent; - type TransactionPayment = (); type DustRemoval = (); type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = BalancesTransactionBaseFee; - type TransactionByteFee = BalancesTransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const MinimumPeriod: u64 = 1; diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index e65cf9ace19ca..195202c8734a4 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -114,7 +114,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = Event; type Error = Error; type BlockHashCount = BlockHashCount; @@ -127,24 +126,18 @@ mod tests { pub const ExistentialDeposit: u64 = 0; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 1; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = u64; type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type Error = Error; type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const LaunchPeriod: u64 = 1; diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 2af301df1ca7f..b7e7dac6f9502 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -1015,7 +1015,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -1027,23 +1026,17 @@ mod tests { 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 Test { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const LaunchPeriod: u64 = 2; diff --git a/srml/elections-phragmen/src/lib.rs b/srml/elections-phragmen/src/lib.rs index 6ec79a537c7c5..fdc88ce9e0441 100644 --- a/srml/elections-phragmen/src/lib.rs +++ b/srml/elections-phragmen/src/lib.rs @@ -616,7 +616,6 @@ mod tests { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -628,8 +627,6 @@ mod tests { pub const ExistentialDeposit: u64 = 1; pub const TransferFee: u64 = 0; pub const CreationFee: u64 = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { @@ -637,15 +634,11 @@ mod tests { type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { diff --git a/srml/elections/src/mock.rs b/srml/elections/src/mock.rs index d48a0fd3fda10..77b13d74a139f 100644 --- a/srml/elections/src/mock.rs +++ b/srml/elections/src/mock.rs @@ -47,7 +47,6 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = Event; - type WeightMultiplierUpdate = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; @@ -59,23 +58,17 @@ 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 Test { type Balance = u64; type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = Event; - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 0766468efa007..a6a075d8cb68a 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -667,7 +667,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -679,23 +678,17 @@ mod tests { 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 Test { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } impl Trait for Test { type Event = (); diff --git a/srml/executive/Cargo.toml b/srml/executive/Cargo.toml index a87eed40b75df..92191a7aac4db 100644 --- a/srml/executive/Cargo.toml +++ b/srml/executive/Cargo.toml @@ -18,6 +18,7 @@ hex-literal = "0.2.1" primitives = { package = "substrate-primitives", path = "../../core/primitives" } srml-indices = { path = "../indices" } balances = { package = "srml-balances", path = "../balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../transaction-payment" } [features] default = ["std"] diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 23c645f6ef499..401f07d2069e9 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -347,7 +347,6 @@ mod tests { type Header = Header; type Event = MetaEvent; type BlockHashCount = BlockHashCount; - type WeightMultiplierUpdate = (); type MaximumBlockWeight = MaximumBlockWeight; type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; @@ -357,23 +356,30 @@ mod tests { 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; + } + + parameter_types! { + pub const TransactionBaseFee: u64 = 10; + pub const TransactionByteFee: u64 = 0; + } + impl transaction_payment::Trait for Runtime { + type Currency = Balances; + type OnTransactionPayment = (); type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); } impl ValidateUnsigned for Runtime { @@ -391,7 +397,7 @@ mod tests { system::CheckEra, system::CheckNonce, system::CheckWeight, - balances::TakeFees + transaction_payment::ChargeTransactionPayment ); type TestXt = sr_primitives::testing::TestXt; type Executive = super::Executive, system::ChainContext, Runtime, ()>; @@ -401,7 +407,7 @@ mod tests { system::CheckEra::from(Era::Immortal), system::CheckNonce::from(nonce), system::CheckWeight::new(), - balances::TakeFees::from(fee) + transaction_payment::ChargeTransactionPayment::from(fee) ) } @@ -450,7 +456,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("a6378d7fdd31029d13718d54bdff10a370e75cc624aaf94a90e7e7d4a24e0bcc").into(), + state_root: hex!("f0d1d66255c2e5b40580eb8b93ddbe732491478487f85e358e1d167d369e398e").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index ba893c42d0b5e..1106a17a58cc1 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -298,7 +298,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs index 6791ee5785263..ca49e2f1cdd53 100644 --- a/srml/generic-asset/src/lib.rs +++ b/srml/generic-asset/src/lib.rs @@ -1056,7 +1056,6 @@ impl system::Trait for ElevatedTrait { type MaximumBlockWeight = T::MaximumBlockWeight; type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; - type WeightMultiplierUpdate = (); type BlockHashCount = T::BlockHashCount; type Version = T::Version; } diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs index c0f9f154b8349..57b13760fa03d 100644 --- a/srml/generic-asset/src/mock.rs +++ b/srml/generic-asset/src/mock.rs @@ -56,7 +56,6 @@ impl system::Trait for Test { type Lookup = IdentityLookup; type Header = Header; type Event = TestEvent; - type WeightMultiplierUpdate = (); type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 8d585e4b4679e..fcacbade20490 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -57,7 +57,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = TestEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/im-online/src/mock.rs b/srml/im-online/src/mock.rs index c2869136dc886..e50e7779e9fcd 100644 --- a/srml/im-online/src/mock.rs +++ b/srml/im-online/src/mock.rs @@ -111,7 +111,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index 59d84279a9d5a..427fd87c47e7d 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -81,7 +81,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = Indices; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/membership/src/lib.rs b/srml/membership/src/lib.rs index 0cd3b0af66197..5d2001b32d57b 100644 --- a/srml/membership/src/lib.rs +++ b/srml/membership/src/lib.rs @@ -224,7 +224,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/offences/src/mock.rs b/srml/offences/src/mock.rs index 01891ce32b43c..f9c79390819e0 100644 --- a/srml/offences/src/mock.rs +++ b/srml/offences/src/mock.rs @@ -78,7 +78,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = TestEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/randomness-collective-flip/src/lib.rs b/srml/randomness-collective-flip/src/lib.rs index 8dedf6f570d59..f6261b6a03d47 100644 --- a/srml/randomness-collective-flip/src/lib.rs +++ b/srml/randomness-collective-flip/src/lib.rs @@ -182,7 +182,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/scored-pool/src/mock.rs b/srml/scored-pool/src/mock.rs index 92a16bf25d6f6..394f486a8bec3 100644 --- a/srml/scored-pool/src/mock.rs +++ b/srml/scored-pool/src/mock.rs @@ -52,8 +52,6 @@ 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 system::Trait for Test { @@ -66,7 +64,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -80,15 +77,11 @@ impl balances::Trait for Test { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } thread_local! { diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index 736d3fa4da920..d680fdc96b015 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -168,7 +168,6 @@ impl system::Trait for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 43bd00e3e0218..41a2ad4802097 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -118,7 +118,6 @@ impl system::Trait for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -129,23 +128,17 @@ impl system::Trait for Test { parameter_types! { pub const TransferFee: Balance = 0; pub const CreationFee: Balance = 0; - pub const TransactionBaseFee: u64 = 0; - pub const TransactionByteFee: u64 = 0; } impl balances::Trait for Test { type Balance = Balance; type OnFreeBalanceZero = Staking; type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const Period: BlockNumber = 1; diff --git a/srml/system/benches/bench.rs b/srml/system/benches/bench.rs index 1b18b55fd2893..6da02cf9c8cea 100644 --- a/srml/system/benches/bench.rs +++ b/srml/system/benches/bench.rs @@ -68,7 +68,6 @@ impl system::Trait for Runtime { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = Event; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 71c99af3b3212..caa6d573b0ea8 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -97,13 +97,13 @@ use rstd::marker::PhantomData; use sr_version::RuntimeVersion; use sr_primitives::{ generic::{self, Era}, Perbill, ApplyError, ApplyOutcome, DispatchError, - weights::{Weight, DispatchInfo, DispatchClass, WeightMultiplier, SimpleDispatchInfo}, + weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo}, transaction_validity::{ ValidTransaction, TransactionPriority, TransactionLongevity, TransactionValidityError, InvalidTransaction, TransactionValidity, }, traits::{ - self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Convert, Lookup, LookupError, + self, CheckEqual, SimpleArithmetic, Zero, SignedExtension, Lookup, LookupError, SimpleBitOps, Hash, Member, MaybeDisplay, EnsureOrigin, SaturatedConversion, MaybeSerializeDebugButNotDeserialize, MaybeSerializeDebug, StaticLookup, One, Bounded, }, @@ -188,14 +188,6 @@ pub trait Trait: 'static + Eq + Clone { /// (e.g. Indices module) may provide more functional/efficient alternatives. type Lookup: StaticLookup; - /// 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, @@ -381,9 +373,6 @@ decl_storage! { AllExtrinsicsWeight: Option; /// Total length (in bytes) for all extrinsics put together, for the current block. AllExtrinsicsLen: Option; - /// 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). @@ -612,17 +601,6 @@ impl Module { AllExtrinsicsLen::get().unwrap_or_default() } - /// Update the next weight multiplier. - /// - /// This should be called at then end of each block, before `all_extrinsics_weight` is cleared. - 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, @@ -645,7 +623,6 @@ impl Module { /// Remove temporary "environment" entries in storage. pub fn finalize() -> T::Header { ExtrinsicCount::kill(); - Self::update_weight_multiplier(); AllExtrinsicsWeight::kill(); AllExtrinsicsLen::kill(); @@ -1118,7 +1095,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = u16; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index cdacb8a391316..35546575c5aee 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -348,7 +348,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; diff --git a/srml/transaction-payment/Cargo.toml b/srml/transaction-payment/Cargo.toml new file mode 100644 index 0000000000000..7f8eddab19d98 --- /dev/null +++ b/srml/transaction-payment/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "srml-transaction-payment" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +support = { package = "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" } +primitives = { package = "substrate-primitives", path = "../../core/primitives" } +balances = { package = "srml-balances", path = "../balances" } + +[features] +default = ["std"] +std = [ + "codec/std", + "rstd/std", + "sr-primitives/std", + "support/std", + "system/std", +] diff --git a/srml/transaction-payment/src/lib.rs b/srml/transaction-payment/src/lib.rs new file mode 100644 index 0000000000000..f2e815fcd40ab --- /dev/null +++ b/srml/transaction-payment/src/lib.rs @@ -0,0 +1,455 @@ +// 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 . + +//! # Transaction Payment Module +//! +//! This module provides the basic logic needed to pay the absolute minimum amount needed for a +//! transaction to be included. This includes: +//! - _weight fee_: A fee proportional to amount of weight a transaction consumes. +//! - _length fee_: A fee proportional to the encoded length of the transaction. +//! - _tip_: An optional tip. Tip increases the priority of the transaction, giving it a higher +//! chance to be included by the transaction queue. +//! +//! Additionally, this module allows one to configure: +//! - The mapping between one unit of weight to one unit of fee via [`WeightToFee`]. +//! - A means of updating the fee for the next block, via defining a multiplier, based on the +//! final state of the chain at the end of the previous block. This can be configured via +//! [`FeeMultiplierUpdate`] + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::prelude::*; +use codec::{Encode, Decode}; +use support::{ + decl_storage, decl_module, + traits::{Currency, Get, OnUnbalanced, ExistenceRequirement, WithdrawReason}, +}; +use sr_primitives::{ + Fixed64, + transaction_validity::{ + TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, + TransactionValidity, + }, + traits::{Zero, Saturating, SignedExtension, SaturatedConversion, Convert}, + weights::{Weight, DispatchInfo}, +}; + +type Multiplier = Fixed64; +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = + <::Currency as Currency<::AccountId>>::NegativeImbalance; + +pub trait Trait: system::Trait { + /// The currency type in which fees will be paid. + type Currency: Currency; + + /// Handler for the unbalanced reduction when taking transaction fees. + type OnTransactionPayment: OnUnbalanced>; + + /// The fee to be paid for making a transaction; the base. + type TransactionBaseFee: Get>; + + /// The fee to be paid for making a transaction; the per-byte portion. + type TransactionByteFee: Get>; + + /// Convert a weight value into a deductible fee based on the currency type. + type WeightToFee: Convert>; + + /// Update the multiplier of the next block, based on the previous block's weight. + // TODO: maybe this does not need previous weight and can just read it + type FeeMultiplierUpdate: Convert<(Weight, Multiplier), Multiplier>; +} + +decl_storage! { + trait Store for Module as Balances { + NextFeeMultiplier get(next_fee_multiplier): Multiplier = Multiplier::from_parts(0); + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// The fee to be paid for making a transaction; the base. + const TransactionBaseFee: BalanceOf = T::TransactionBaseFee::get(); + + /// The fee to be paid for making a transaction; the per-byte portion. + const TransactionByteFee: BalanceOf = T::TransactionByteFee::get(); + + fn on_finalize() { + let current_weight = >::all_extrinsics_weight(); + NextFeeMultiplier::mutate(|fm| { + *fm = T::FeeMultiplierUpdate::convert((current_weight, *fm)) + }); + } + } +} + +impl Module {} + +/// Require the transactor pay for themselves and maybe include a tip to gain additional priority +/// in the queue. +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct ChargeTransactionPayment(#[codec(compact)] BalanceOf); + +impl ChargeTransactionPayment { + /// utility constructor. Used only in client/factory code. + pub fn from(fee: BalanceOf) -> Self { + Self(fee) + } + + /// Compute the final fee value for a particular transaction. + /// + /// The final fee is composed of: + /// - _length-fee_: This is the amount paid merely to pay for size of the transaction. + /// - _weight-fee_: This amount is computed based on the weight of the transaction. Unlike + /// size-fee, this is not input dependent and reflects the _complexity_ of the execution + /// and the time it consumes. + /// - (optional) _tip_: if included in the transaction, it will be added on top. Only signed + /// transactions can have a tip. + fn compute_fee(len: usize, info: DispatchInfo, tip: BalanceOf) -> BalanceOf { + let len_fee = if info.pay_length_fee() { + let len = >::from(len as u32); + let base = T::TransactionBaseFee::get(); + let per_byte = T::TransactionByteFee::get(); + base.saturating_add(per_byte.saturating_mul(len)) + } else { + Zero::zero() + }; + + let weight_fee = { + // cap the weight to the maximum defined in runtime, otherwise it will be the `Bounded` + // maximum of its data type, which is not desired. + let capped_weight = info.weight.min(::MaximumBlockWeight::get()); + T::WeightToFee::convert(capped_weight) + }; + + // everything except for tip + let basic_fee = len_fee.saturating_add(weight_fee); + let fee_update = NextFeeMultiplier::get(); + let adjusted_fee = fee_update.saturated_multiply_accumulate(basic_fee); + + adjusted_fee.saturating_add(tip) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for ChargeTransactionPayment { + fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result { + write!(f, "ChargeTransactionPayment<{:?}>", self.0) + } +} + +impl SignedExtension for ChargeTransactionPayment + where BalanceOf: Send + Sync +{ + type AccountId = T::AccountId; + type Call = T::Call; + type AdditionalSigned = (); + type Pre = (); + fn additional_signed(&self) -> rstd::result::Result<(), TransactionValidityError> { Ok(()) } + + fn validate( + &self, + who: &Self::AccountId, + _call: &Self::Call, + info: DispatchInfo, + len: usize, + ) -> TransactionValidity { + // pay any fees. + let fee = Self::compute_fee(len, info, self.0); + let imbalance = match T::Currency::withdraw( + who, + fee, + WithdrawReason::TransactionPayment, + ExistenceRequirement::KeepAlive, + ) { + Ok(imbalance) => imbalance, + Err(_) => return InvalidTransaction::Payment.into(), + }; + T::OnTransactionPayment::on_unbalanced(imbalance); + + let mut r = ValidTransaction::default(); + // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which + // will be a bit more than setting the priority to tip. For now, this is enough. + r.priority = fee.saturated_into::(); + Ok(r) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use support::{parameter_types, impl_outer_origin}; + use primitives::H256; + use sr_primitives::{ + Perbill, + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + weights::DispatchClass, + }; + use rstd::cell::RefCell; + + const CALL: &::Call = &(); + + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Runtime; + + impl_outer_origin!{ + pub enum Origin for Runtime {} + } + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + } + + parameter_types! { + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; + pub const ExistentialDeposit: u64 = 0; + } + + impl balances::Trait for Runtime { + type Balance = u64; + type OnFreeBalanceZero = (); + type OnNewAccount = (); + type Event = (); + type TransferPayment = (); + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + } + + thread_local! { + static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); + static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); + static WEIGHT_TO_FEE: RefCell = RefCell::new(1); + } + + pub struct TransactionBaseFee; + impl Get for TransactionBaseFee { + fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) } + } + + pub struct TransactionByteFee; + impl Get for TransactionByteFee { + fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) } + } + + pub struct WeightToFee(u64); + impl Convert for WeightToFee { + fn convert(t: Weight) -> u64 { + WEIGHT_TO_FEE.with(|v| *v.borrow() * (t as u64)) + } + } + + impl Trait for Runtime { + type Currency = balances::Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = WeightToFee; + type FeeMultiplierUpdate = (); + } + + type Balances = balances::Module; + + pub struct ExtBuilder { + balance_factor: u64, + base_fee: u64, + byte_fee: u64, + weight_to_fee: u64 + } + + impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + base_fee: 0, + byte_fee: 1, + weight_to_fee: 1, + } + } + } + + impl ExtBuilder { + pub fn fees(mut self, base: u64, byte: u64, weight: u64) -> Self { + self.base_fee = base; + self.byte_fee = byte; + self.weight_to_fee = weight; + self + } + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + fn set_constants(&self) { + TRANSACTION_BASE_FEE.with(|v| *v.borrow_mut() = self.base_fee); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> runtime_io::TestExternalities { + self.set_constants(); + let mut t = system::GenesisConfig::default().build_storage::().unwrap(); + balances::GenesisConfig:: { + balances: vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor) + ], + vesting: vec![], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + } + + /// create a transaction info struct from weight. Handy to avoid building the whole struct. + pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, ..Default::default() } + } + + #[test] + fn signed_extension_transaction_payment_work() { + ExtBuilder::default() + .balance_factor(10) // 100 + .fees(5, 1, 1) // 5 fixed, 1 per byte, 1 per weight + .build() + .execute_with(|| + { + let len = 10; + assert!( + ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, info_from_weight(5), len) + .is_ok() + ); + assert_eq!(Balances::free_balance(&1), 100 - 5 - 5 - 10); + + assert!( + ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, info_from_weight(3), len) + .is_ok() + ); + assert_eq!(Balances::free_balance(&2), 200 - 5 - 10 - 3 - 5); + }); + } + + #[test] + fn signed_extension_transaction_payment_is_bounded() { + ExtBuilder::default() + .balance_factor(1000) + .fees(0, 0, 1) + .build() + .execute_with(|| + { + use sr_primitives::weights::Weight; + + // maximum weight possible + assert!( + ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10) + .is_ok() + ); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::MaximumBlockWeight::get()) as u64 + ); + }); + } + + #[test] + fn signed_extension_allows_free_transactions() { + ExtBuilder::default() + .fees(100, 1, 1) + .balance_factor(0) + .build() + .execute_with(|| + { + // 1 ain't have a penny. + assert_eq!(Balances::free_balance(&1), 0); + + // like a FreeOperational + let operational_transaction = DispatchInfo { + weight: 0, + class: DispatchClass::Operational + }; + let len = 100; + assert!( + ChargeTransactionPayment::::from(0) + .validate(&1, CALL, operational_transaction , len) + .is_ok() + ); + + // like a FreeNormal + let free_transaction = DispatchInfo { + weight: 0, + class: DispatchClass::Normal + }; + assert!( + ChargeTransactionPayment::::from(0) + .validate(&1, CALL, free_transaction , len) + .is_err() + ); + }); + } + + #[test] + fn signed_ext_length_fee_is_also_updated_per_congestion() { + ExtBuilder::default() + .fees(5, 1, 1) + .balance_factor(10) + .build() + .execute_with(|| + { + // all fees should be x1.5 + NextFeeMultiplier::put(Fixed64::from_rational(1, 2)); + let len = 10; + + assert!( + ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, info_from_weight(3), len) + .is_ok() + ); + assert_eq!(Balances::free_balance(&1), 100 - 10 - (5 + 10 + 3) * 3 / 2); + }) + } +} + + diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index d5b3e6e55f97a..936e4bd93de6f 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -383,7 +383,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -395,23 +394,17 @@ mod tests { 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 Test { type Balance = u64; type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); diff --git a/srml/utility/src/lib.rs b/srml/utility/src/lib.rs index 52675d24fc6f3..d69e260ff9aac 100644 --- a/srml/utility/src/lib.rs +++ b/srml/utility/src/lib.rs @@ -99,7 +99,6 @@ mod tests { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type WeightMultiplierUpdate = (); type Event = (); type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; @@ -111,23 +110,17 @@ mod tests { 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 Test { type Balance = u64; type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); - type TransactionPayment = (); type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type TransferFee = TransferFee; type CreationFee = CreationFee; - type TransactionBaseFee = TransactionBaseFee; - type TransactionByteFee = TransactionByteFee; - type WeightToFee = (); } impl Trait for Test { type Event = (); diff --git a/subkey/Cargo.toml b/subkey/Cargo.toml index d901b59d0b01d..e1c3ef64d26d4 100644 --- a/subkey/Cargo.toml +++ b/subkey/Cargo.toml @@ -19,6 +19,7 @@ hex-literal = "0.2.1" codec = { package = "parity-scale-codec", version = "1.0.0" } system = { package = "srml-system", path = "../srml/system" } balances = { package = "srml-balances", path = "../srml/balances" } +transaction-payment = { package = "srml-transaction-payment", path = "../srml/transaction-payment" } [features] bench = [] diff --git a/subkey/src/main.rs b/subkey/src/main.rs index 973502b3d6810..2596513ab28b5 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -363,7 +363,7 @@ fn create_extrinsic( system::CheckEra::::from(Era::Immortal), system::CheckNonce::::from(i), system::CheckWeight::::new(), - balances::TakeFees::::from(f), + transaction_payment::ChargeTransactionPayment::::from(f), Default::default(), ) };