mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 15:51:12 +00:00
refactor: Transaction-Payment module (#3816)
* Initial draft that compiles * Extract payment stuff from balances * Extract multiplier update stuff from system * Some fixes. * Update len-fee as well * some review comments. * Remove todo * bump
This commit is contained in:
Generated
+22
@@ -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)",
|
||||
|
||||
@@ -106,6 +106,7 @@ members = [
|
||||
"srml/system/rpc",
|
||||
"srml/timestamp",
|
||||
"srml/treasury",
|
||||
"srml/transaction-payment",
|
||||
"srml/utility",
|
||||
"node/cli",
|
||||
"node/executor",
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +382,6 @@ impl srml_system::Trait for Runtime {
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -166,18 +166,17 @@ impl system::Trait for Runtime {
|
||||
type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
/// The ubiquitous event type.
|
||||
type Event = Event;
|
||||
/// Update weight (to fee) multiplier per-block.
|
||||
type WeightMultiplierUpdate = ();
|
||||
/// The ubiquitous origin type.
|
||||
type Origin = Origin;
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
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<Runtime>;
|
||||
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<T>},
|
||||
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<T>},
|
||||
@@ -293,7 +301,7 @@ pub type SignedExtra = (
|
||||
system::CheckEra<Runtime>,
|
||||
system::CheckNonce<Runtime>,
|
||||
system::CheckWeight<Runtime>,
|
||||
balances::TakeFees<Runtime>
|
||||
transaction_payment::ChargeTransactionPayment<Runtime>
|
||||
);
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
|
||||
|
||||
@@ -100,7 +100,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -56,7 +56,7 @@ impl<Number> FactoryState<Number> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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<E: Encode>(extrinsic: &E) -> Balance {
|
||||
fn transfer_fee<E: Encode>(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 = <system::Module<Runtime>>::next_weight_multiplier().apply_to(weight);
|
||||
let weight_fee = <Runtime as balances::Trait>::WeightToFee::convert(weight);
|
||||
length_fee + weight_fee + TransferFee::get()
|
||||
let weight_fee = <Runtime as transaction_payment::Trait>::WeightToFee::convert(weight);
|
||||
|
||||
fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + TransferFee::get()
|
||||
}
|
||||
|
||||
fn default_transfer_call() -> balances::Call<Runtime> {
|
||||
@@ -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::<Blake2Hasher>::new_with_code(COMPACT_CODE, (map![
|
||||
<balances::FreeBalance<Runtime>>::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);
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Weight, Balance> 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 = <Runtime as balances::Trait>::WeightToFee::convert(wm.apply_to(tx_weight));
|
||||
let fee = <Runtime as transaction_payment::Trait>::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()));
|
||||
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| {
|
||||
WeightMultiplierUpdateHandler::convert((i, WeightMultiplier::default()));
|
||||
});
|
||||
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
|
||||
));
|
||||
|
||||
@@ -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<BlockNumber, BlakeTwo256>;
|
||||
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<Runtime, UncheckedExtrinsic> for Runtim
|
||||
system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
|
||||
system::CheckNonce::<Runtime>::from(index),
|
||||
system::CheckWeight::<Runtime>::new(),
|
||||
balances::TakeFees::<Runtime>::from(tip),
|
||||
transaction_payment::ChargeTransactionPayment::<Runtime>::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<T>},
|
||||
Democracy: democracy::{Module, Call, Storage, Config, Event<T>},
|
||||
@@ -540,7 +548,7 @@ pub type SignedExtra = (
|
||||
system::CheckEra<Runtime>,
|
||||
system::CheckNonce<Runtime>,
|
||||
system::CheckWeight<Runtime>,
|
||||
balances::TakeFees<Runtime>,
|
||||
transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
contracts::CheckBlockGasLimit<Runtime>,
|
||||
);
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -271,7 +271,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -54,7 +54,6 @@ impl system::Trait for Test {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -204,7 +204,6 @@ mod tests {
|
||||
type AccountId = AuthorityId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -442,7 +442,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -62,7 +62,6 @@ impl system::Trait for Test {
|
||||
type AccountId = DummyValidatorId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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<I: Instance = DefaultInstance>: system::Trait {
|
||||
|
||||
/// The fee required to create an account.
|
||||
type CreationFee: Get<Self::Balance>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
type TransactionBaseFee: Get<Self::Balance>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the per-byte portion.
|
||||
type TransactionByteFee: Get<Self::Balance>;
|
||||
|
||||
/// Convert a weight value into a deductible fee based on the currency type.
|
||||
type WeightToFee: Convert<Weight, Self::Balance>;
|
||||
}
|
||||
|
||||
pub trait Trait<I: Instance = DefaultInstance>: system::Trait {
|
||||
@@ -235,9 +211,6 @@ pub trait Trait<I: Instance = DefaultInstance>: system::Trait {
|
||||
/// Handler for when a new account is created.
|
||||
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
||||
|
||||
/// Handler for the unbalanced reduction when taking transaction fees.
|
||||
type TransactionPayment: OnUnbalanced<NegativeImbalance<Self, I>>;
|
||||
|
||||
/// Handler for the unbalanced reduction when taking fees associated with balance
|
||||
/// transfer (which may also include account creation).
|
||||
type TransferPayment: OnUnbalanced<NegativeImbalance<Self, I>>;
|
||||
@@ -256,15 +229,6 @@ pub trait Trait<I: Instance = DefaultInstance>: system::Trait {
|
||||
|
||||
/// The fee required to create an account.
|
||||
type CreationFee: Get<Self::Balance>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
type TransactionBaseFee: Get<Self::Balance>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the per-byte portion.
|
||||
type TransactionByteFee: Get<Self::Balance>;
|
||||
|
||||
/// Convert a weight value into a deductible fee based on the currency type.
|
||||
type WeightToFee: Convert<Weight, Self::Balance>;
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> Subtrait<I> for T {
|
||||
@@ -274,9 +238,6 @@ impl<T: Trait<I>, I: Instance> Subtrait<I> 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<T: Subtrait<I>, I: Instance>(T, I);
|
||||
impl<T: Subtrait<I>, I: Instance> Clone for ElevatedTrait<T, I> {
|
||||
@@ -796,7 +751,6 @@ impl<T: Subtrait<I>, I: Instance> system::Trait for ElevatedTrait<T, I> {
|
||||
type AccountId = T::AccountId;
|
||||
type Lookup = T::Lookup;
|
||||
type Header = T::Header;
|
||||
type WeightMultiplierUpdate = T::WeightMultiplierUpdate;
|
||||
type Event = ();
|
||||
type BlockHashCount = T::BlockHashCount;
|
||||
type MaximumBlockWeight = T::MaximumBlockWeight;
|
||||
@@ -809,15 +763,11 @@ impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
|
||||
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<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I>
|
||||
@@ -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<T: Trait<I>, I: Instance = DefaultInstance>(#[codec(compact)] T::Balance);
|
||||
|
||||
impl<T: Trait<I>, I: Instance> TakeFees<T, I> {
|
||||
/// utility constructor. Used only in client/factory code.
|
||||
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(<T as system::Trait>::MaximumBlockWeight::get());
|
||||
let weight_update = <system::Module<T>>::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<T: Trait<I>, I: Instance> rstd::fmt::Debug for TakeFees<T, I> {
|
||||
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance + Clone + Eq> SignedExtension for TakeFees<T, I> {
|
||||
type AccountId = T::AccountId;
|
||||
type 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 <Module<T, I>>::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::<TransactionPriority>();
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> IsDeadAccount<T::AccountId> for Module<T, I>
|
||||
where
|
||||
T::Balance: MaybeSerializeDebug
|
||||
|
||||
@@ -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<u64> = RefCell::new(0);
|
||||
static TRANSFER_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static CREATION_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BASE_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(1);
|
||||
static TRANSACTION_WEIGHT_FEE: RefCell<u64> = RefCell::new(1);
|
||||
static WEIGHT_TO_FEE: RefCell<u64> = RefCell::new(1);
|
||||
}
|
||||
|
||||
pub struct ExistentialDeposit;
|
||||
@@ -56,23 +52,6 @@ impl Get<u64> for CreationFee {
|
||||
fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct TransactionBaseFee;
|
||||
impl Get<u64> for TransactionBaseFee {
|
||||
fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct TransactionByteFee;
|
||||
impl Get<u64> for TransactionByteFee {
|
||||
fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct WeightToFee(u64);
|
||||
impl Convert<Weight, u64> 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<Self::AccountId>;
|
||||
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<Runtime>;
|
||||
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<Runtime>;
|
||||
pub type Balances = Module<Runtime>;
|
||||
|
||||
|
||||
pub const CALL: &<Runtime as system::Trait>::Call = &();
|
||||
|
||||
/// create a transaction info struct from weight. Handy to avoid building the whole struct.
|
||||
|
||||
@@ -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!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
|
||||
// NOTE: this causes a fee payment.
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
assert!(<ChargeTransactionPayment<Runtime> as SignedExtension>::pre_dispatch(
|
||||
ChargeTransactionPayment::from(1),
|
||||
&1,
|
||||
CALL,
|
||||
info_from_weight(1),
|
||||
@@ -139,8 +139,8 @@ fn lock_reasons_should_work() {
|
||||
<Balances as ReservableCurrency<_>>::reserve(&1, 1),
|
||||
"account liquidity restrictions prevent withdrawal"
|
||||
);
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
assert!(<ChargeTransactionPayment<Runtime> 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!(<Balances as Currency<_>>::transfer(&1, &2, 1));
|
||||
assert_ok!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
|
||||
assert!(<TakeFees<Runtime> as SignedExtension>::pre_dispatch(
|
||||
TakeFees::from(1),
|
||||
assert!(<ChargeTransactionPayment<Runtime> 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::<Runtime>::from(0)
|
||||
.pre_dispatch(&1, CALL, info_from_weight(5), len)
|
||||
.is_ok()
|
||||
);
|
||||
assert_eq!(Balances::free_balance(&1), 100 - 20 - 25);
|
||||
assert!(
|
||||
TakeFees::<Runtime>::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::<Runtime>::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 - <Runtime as system::Trait>::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::<Runtime>::from(0)
|
||||
.validate(&1, CALL, operational_transaction , len)
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
// like a FreeNormal
|
||||
let free_transaction = DispatchInfo {
|
||||
weight: 0,
|
||||
class: DispatchClass::Normal
|
||||
};
|
||||
assert!(
|
||||
TakeFees::<Runtime>::from(0)
|
||||
.validate(&1, CALL, free_transaction , len)
|
||||
.is_err()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn burn_must_work() {
|
||||
ExtBuilder::default().monied(true).build().execute_with(|| {
|
||||
|
||||
@@ -406,7 +406,6 @@ mod tests {
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = Event;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
|
||||
@@ -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<T: Trait>(PhantomData<T>);
|
||||
impl<T: Trait> ComputeDispatchFee<<T as Trait>::Call, BalanceOf<T>> for DefaultDispatchFeeComputor<T> {
|
||||
|
||||
@@ -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<Self::AccountId>;
|
||||
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;
|
||||
|
||||
@@ -114,7 +114,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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;
|
||||
|
||||
@@ -1015,7 +1015,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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;
|
||||
|
||||
@@ -616,7 +616,6 @@ mod tests {
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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! {
|
||||
|
||||
@@ -47,7 +47,6 @@ impl system::Trait for Test {
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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! {
|
||||
|
||||
@@ -667,7 +667,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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 = ();
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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<Runtime>,
|
||||
system::CheckNonce<Runtime>,
|
||||
system::CheckWeight<Runtime>,
|
||||
balances::TakeFees<Runtime>
|
||||
transaction_payment::ChargeTransactionPayment<Runtime>
|
||||
);
|
||||
type TestXt = sr_primitives::testing::TestXt<Call, SignedExtra>;
|
||||
type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, 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![], },
|
||||
},
|
||||
|
||||
@@ -298,7 +298,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<u64>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -1056,7 +1056,6 @@ impl<T: Subtrait> system::Trait for ElevatedTrait<T> {
|
||||
type MaximumBlockWeight = T::MaximumBlockWeight;
|
||||
type MaximumBlockLength = T::MaximumBlockLength;
|
||||
type AvailableBlockRatio = T::AvailableBlockRatio;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type BlockHashCount = T::BlockHashCount;
|
||||
type Version = T::Version;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ impl system::Trait for Test {
|
||||
type Lookup = IdentityLookup<u64>;
|
||||
type Header = Header;
|
||||
type Event = TestEvent;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
|
||||
@@ -57,7 +57,6 @@ impl system::Trait for Test {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = TestEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -111,7 +111,6 @@ impl system::Trait for Runtime {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -224,7 +224,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -78,7 +78,6 @@ impl system::Trait for Runtime {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = TestEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -182,7 +182,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -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<Self::AccountId>;
|
||||
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! {
|
||||
|
||||
@@ -168,7 +168,6 @@ impl system::Trait for Test {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -118,7 +118,6 @@ impl system::Trait for Test {
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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;
|
||||
|
||||
@@ -68,7 +68,6 @@ impl system::Trait for Runtime {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = Event;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -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<Target = Self::AccountId>;
|
||||
|
||||
/// Handler for updating the weight multiplier at the end of each block.
|
||||
///
|
||||
/// It receives the current block's weight as input and returns the next weight multiplier for next
|
||||
/// block.
|
||||
///
|
||||
/// Note that passing `()` will keep the value constant.
|
||||
type WeightMultiplierUpdate: Convert<(Weight, WeightMultiplier), WeightMultiplier>;
|
||||
|
||||
/// The block header.
|
||||
type Header: Parameter + traits::Header<
|
||||
Number = Self::BlockNumber,
|
||||
@@ -381,9 +373,6 @@ decl_storage! {
|
||||
AllExtrinsicsWeight: Option<Weight>;
|
||||
/// Total length (in bytes) for all extrinsics put together, for the current block.
|
||||
AllExtrinsicsLen: Option<u32>;
|
||||
/// The next weight multiplier. This should be updated at the end of each block based on the
|
||||
/// saturation level (weight).
|
||||
pub NextWeightMultiplier get(next_weight_multiplier): WeightMultiplier = Default::default();
|
||||
/// 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<T: Trait> Module<T> {
|
||||
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<T: Trait> Module<T> {
|
||||
/// 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<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = u16;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -348,7 +348,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type WeightMultiplierUpdate = ();
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "srml-transaction-payment"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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",
|
||||
]
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! # 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<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
type NegativeImbalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
|
||||
|
||||
pub trait Trait: system::Trait {
|
||||
/// The currency type in which fees will be paid.
|
||||
type Currency: Currency<Self::AccountId>;
|
||||
|
||||
/// Handler for the unbalanced reduction when taking transaction fees.
|
||||
type OnTransactionPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
type TransactionBaseFee: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the per-byte portion.
|
||||
type TransactionByteFee: Get<BalanceOf<Self>>;
|
||||
|
||||
/// Convert a weight value into a deductible fee based on the currency type.
|
||||
type WeightToFee: Convert<Weight, BalanceOf<Self>>;
|
||||
|
||||
/// 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<T: Trait> as Balances {
|
||||
NextFeeMultiplier get(next_fee_multiplier): Multiplier = Multiplier::from_parts(0);
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
const TransactionBaseFee: BalanceOf<T> = T::TransactionBaseFee::get();
|
||||
|
||||
/// The fee to be paid for making a transaction; the per-byte portion.
|
||||
const TransactionByteFee: BalanceOf<T> = T::TransactionByteFee::get();
|
||||
|
||||
fn on_finalize() {
|
||||
let current_weight = <system::Module<T>>::all_extrinsics_weight();
|
||||
NextFeeMultiplier::mutate(|fm| {
|
||||
*fm = T::FeeMultiplierUpdate::convert((current_weight, *fm))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {}
|
||||
|
||||
/// 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<T: Trait + Send + Sync>(#[codec(compact)] BalanceOf<T>);
|
||||
|
||||
impl<T: Trait + Send + Sync> ChargeTransactionPayment<T> {
|
||||
/// utility constructor. Used only in client/factory code.
|
||||
pub fn from(fee: BalanceOf<T>) -> 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<T>) -> BalanceOf<T> {
|
||||
let len_fee = if info.pay_length_fee() {
|
||||
let len = <BalanceOf<T>>::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(<T as system::Trait>::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<T: Trait + Send + Sync> rstd::fmt::Debug for ChargeTransactionPayment<T> {
|
||||
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
|
||||
write!(f, "ChargeTransactionPayment<{:?}>", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T>
|
||||
where BalanceOf<T>: 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::<TransactionPriority>();
|
||||
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: &<Runtime as system::Trait>::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<Self::AccountId>;
|
||||
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<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(1);
|
||||
static WEIGHT_TO_FEE: RefCell<u64> = RefCell::new(1);
|
||||
}
|
||||
|
||||
pub struct TransactionBaseFee;
|
||||
impl Get<u64> for TransactionBaseFee {
|
||||
fn get() -> u64 { TRANSACTION_BASE_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct TransactionByteFee;
|
||||
impl Get<u64> for TransactionByteFee {
|
||||
fn get() -> u64 { TRANSACTION_BYTE_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct WeightToFee(u64);
|
||||
impl Convert<Weight, u64> 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<Runtime>;
|
||||
type OnTransactionPayment = ();
|
||||
type TransactionBaseFee = TransactionBaseFee;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type WeightToFee = WeightToFee;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
type Balances = balances::Module<Runtime>;
|
||||
|
||||
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::<Runtime>().unwrap();
|
||||
balances::GenesisConfig::<Runtime> {
|
||||
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::<Runtime>::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::<Runtime>::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::<Runtime>::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 - <Runtime as system::Trait>::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::<Runtime>::from(0)
|
||||
.validate(&1, CALL, operational_transaction , len)
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
// like a FreeNormal
|
||||
let free_transaction = DispatchInfo {
|
||||
weight: 0,
|
||||
class: DispatchClass::Normal
|
||||
};
|
||||
assert!(
|
||||
ChargeTransactionPayment::<Runtime>::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::<Runtime>::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);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -383,7 +383,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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);
|
||||
|
||||
@@ -99,7 +99,6 @@ mod tests {
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
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 = ();
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -363,7 +363,7 @@ fn create_extrinsic(
|
||||
system::CheckEra::<Runtime>::from(Era::Immortal),
|
||||
system::CheckNonce::<Runtime>::from(i),
|
||||
system::CheckWeight::<Runtime>::new(),
|
||||
balances::TakeFees::<Runtime>::from(f),
|
||||
transaction_payment::ChargeTransactionPayment::<Runtime>::from(f),
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user