Transaction Fee Module (#1648)

* wip

* Split bytes fee charging and charging by amount into different traits.

* Move to edition 2018.

* Implemented charge fee traits for fees module.

* Implemented 'on_finalise' for fee module.

* Updated fees finalize impl.

* Renaming and documentation update.

* Added overflow & underflow check for fee calculation.

* Added mock and unit tests for fee module.

* More unit tests for fees module.

* Fixed srml-executive unit tests.

* Remove transaction base/bytes fee from balances module, fix unit tests.

* fix compile error

* Fixed unit test.

* Minor fixes.

* Bump spec version.

* Bump spec version.

* Updated fees module and runtime wasm.

* Fees module code style improvement; updated runtime wasm.

* Bump spec and impl version.
This commit is contained in:
Xiliang Chen
2019-02-15 23:21:38 +13:00
committed by Bastian Köcher
parent 6a6c3155a6
commit fafffdb771
31 changed files with 672 additions and 90 deletions
+21
View File
@@ -1927,6 +1927,7 @@ dependencies = [
"srml-balances 0.1.0",
"srml-consensus 0.1.0",
"srml-contract 0.1.0",
"srml-fees 0.1.0",
"srml-grandpa 0.1.0",
"srml-indices 0.1.0",
"srml-session 0.1.0",
@@ -1981,6 +1982,7 @@ dependencies = [
"srml-council 0.1.0",
"srml-democracy 0.1.0",
"srml-executive 0.1.0",
"srml-fees 0.1.0",
"srml-grandpa 0.1.0",
"srml-indices 0.1.0",
"srml-session 0.1.0",
@@ -2045,6 +2047,7 @@ dependencies = [
"srml-balances 0.1.0",
"srml-consensus 0.1.0",
"srml-executive 0.1.0",
"srml-fees 0.1.0",
"srml-indices 0.1.0",
"srml-sudo 0.1.0",
"srml-support 0.1.0",
@@ -3177,6 +3180,7 @@ dependencies = [
"sr-std 0.1.0",
"srml-balances 0.1.0",
"srml-consensus 0.1.0",
"srml-fees 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"srml-timestamp 0.1.0",
@@ -3249,12 +3253,29 @@ dependencies = [
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"srml-balances 0.1.0",
"srml-fees 0.1.0",
"srml-indices 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]]
name = "srml-fees"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]]
name = "srml-grandpa"
version = "0.1.0"
+1
View File
@@ -65,6 +65,7 @@ members = [
"srml/democracy",
"srml/example",
"srml/executive",
"srml/fees",
"srml/grandpa",
"srml/indices",
"srml/metadata",
+49 -7
View File
@@ -126,15 +126,57 @@ pub trait BlockNumberToHash {
}
}
/// Simple payment making trait, operating on a single generic `AccountId` type.
pub trait MakePayment<AccountId> {
/// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length
/// `encoded_len` bytes. Return true iff the payment was successful.
fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>;
/// Charge bytes fee trait
pub trait ChargeBytesFee<AccountId> {
/// Charge fees from `transactor` for an extrinsic (transaction) of encoded length
/// `encoded_len` bytes. Return Ok iff the payment was successful.
fn charge_base_bytes_fee(transactor: &AccountId, encoded_len: usize) -> Result<(), &'static str>;
}
impl<T> MakePayment<T> for () {
fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) }
/// Charge fee trait
pub trait ChargeFee<AccountId>: ChargeBytesFee<AccountId> {
/// The type of fee amount.
type Amount;
/// Charge `amount` of fees from `transactor`. Return Ok iff the payment was successful.
fn charge_fee(transactor: &AccountId, amount: Self::Amount) -> Result<(), &'static str>;
/// Refund `amount` of previous charged fees from `transactor`. Return Ok iff the refund was successful.
fn refund_fee(transactor: &AccountId, amount: Self::Amount) -> Result<(), &'static str>;
}
/// Transfer fungible asset trait
pub trait TransferAsset<AccountId> {
/// The type of asset amount.
type Amount;
/// Transfer asset from `from` account to `to` account with `amount` of asset.
fn transfer(from: &AccountId, to: &AccountId, amount: Self::Amount) -> Result<(), &'static str>;
/// Remove asset from `who` account by deducing `amount` in the account balances.
fn remove_from(who: &AccountId, amount: Self::Amount) -> Result<(), &'static str>;
/// Add asset to `who` account by increasing `amount` in the account balances.
fn add_to(who: &AccountId, amount: Self::Amount) -> Result<(), &'static str>;
}
impl<T> ChargeBytesFee<T> for () {
fn charge_base_bytes_fee(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) }
}
impl<T> ChargeFee<T> for () {
type Amount = ();
fn charge_fee(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
fn refund_fee(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
}
impl<T> TransferAsset<T> for () {
type Amount = ();
fn transfer(_: &T, _: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
fn remove_from(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
fn add_to(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
}
/// Extensible conversion trait. Generic over both source and destination types.
@@ -16,6 +16,7 @@ version = { package = "sr-version", path = "../../core/sr-version", default_feat
support = { package = "srml-support", path = "../../srml/support", default_features = false }
primitives = { package = "substrate-primitives", path = "../../core/primitives", default_features = false }
balances = { package = "srml-balances", path = "../../srml/balances", default_features = false }
fees = { package = "srml-fees", path = "../../srml/fees", default_features = false }
consensus = { package = "srml-consensus", path = "../../srml/consensus", default_features = false }
aura = { package = "srml-aura", path = "../../srml/aura", default_features = false }
executive = { package = "srml-executive", path = "../../srml/executive", default_features = false }
@@ -38,6 +39,7 @@ std = [
"runtime-io/std",
"support/std",
"balances/std",
"fees/std",
"executive/std",
"aura/std",
"indices/std",
+8 -1
View File
@@ -164,6 +164,12 @@ impl balances::Trait for Runtime {
type Event = Event;
}
impl fees::Trait for Runtime {
type Amount = u128;
type TransferAsset = Balances;
type Event = Event;
}
impl sudo::Trait for Runtime {
/// The uniquitous event type.
type Event = Event;
@@ -188,6 +194,7 @@ construct_runtime!(
Indices: indices,
Balances: balances,
Sudo: sudo,
Fees: fees::{Module, Storage, Config<T>, Event<T>},
// Used for the module template in `./template.rs`
TemplateModule: template::{Module, Call, Storage, Event<T>},
}
@@ -208,7 +215,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<Address,
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Nonce, Call>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, Context, Balances, AllModules>;
pub type Executive = executive::Executive<Runtime, Block, Context, Fees, AllModules>;
// Implement our runtime API endpoints. This is just a bunch of proxying.
impl_runtime_apis! {
+17
View File
@@ -655,6 +655,7 @@ dependencies = [
"srml-balances 0.1.0",
"srml-consensus 0.1.0",
"srml-executive 0.1.0",
"srml-fees 0.1.0",
"srml-indices 0.1.0",
"srml-sudo 0.1.0",
"srml-support 0.1.0",
@@ -1333,6 +1334,22 @@ dependencies = [
"srml-system 0.1.0",
]
[[package]]
name = "srml-fees"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]]
name = "srml-indices"
version = "0.1.0"
+5 -3
View File
@@ -1,7 +1,7 @@
use primitives::{Ed25519AuthorityId, ed25519};
use node_template_runtime::{
AccountId, GenesisConfig, ConsensusConfig, TimestampConfig, BalancesConfig,
SudoConfig, IndicesConfig
SudoConfig, IndicesConfig, FeesConfig,
};
use substrate_service;
@@ -90,8 +90,6 @@ fn testnet_genesis(initial_authorities: Vec<Ed25519AuthorityId>, endowed_account
ids: endowed_accounts.clone(),
}),
balances: Some(BalancesConfig {
transaction_base_fee: 1,
transaction_byte_fee: 0,
existential_deposit: 500,
transfer_fee: 0,
creation_fee: 0,
@@ -101,5 +99,9 @@ fn testnet_genesis(initial_authorities: Vec<Ed25519AuthorityId>, endowed_account
sudo: Some(SudoConfig {
key: root_key,
}),
fees: Some(FeesConfig {
transaction_base_fee: 1,
transaction_byte_fee: 0,
})
}
}
+11 -7
View File
@@ -20,7 +20,7 @@ use primitives::{Ed25519AuthorityId, ed25519};
use node_primitives::AccountId;
use node_runtime::{ConsensusConfig, CouncilSeatsConfig, CouncilVotingConfig, DemocracyConfig,
SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig,
SudoConfig, ContractConfig, GrandpaConfig, IndicesConfig, Permill, Perbill};
SudoConfig, ContractConfig, GrandpaConfig, IndicesConfig, FeesConfig, Permill, Perbill};
pub use node_runtime::GenesisConfig;
use substrate_service;
use hex_literal::{hex, hex_impl};
@@ -64,8 +64,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
system: None,
balances: Some(BalancesConfig {
balances: endowed_accounts.iter().map(|&k| (k, 10_000_000 * DOLLARS)).collect(),
transaction_base_fee: 1 * CENTS,
transaction_byte_fee: 10 * MILLICENTS,
existential_deposit: 1 * DOLLARS,
transfer_fee: 1 * CENTS,
creation_fee: 1 * CENTS,
@@ -139,7 +137,11 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
}),
grandpa: Some(GrandpaConfig {
authorities: initial_authorities.clone().into_iter().map(|k| (k, 1)).collect(),
})
}),
fees: Some(FeesConfig {
transaction_base_fee: 1 * CENTS,
transaction_byte_fee: 10 * MILLICENTS,
}),
}
}
@@ -192,8 +194,6 @@ pub fn testnet_genesis(
ids: endowed_accounts.iter().map(|x| x.0.into()).collect(),
}),
balances: Some(BalancesConfig {
transaction_base_fee: 1,
transaction_byte_fee: 0,
existential_deposit: 500,
transfer_fee: 0,
creation_fee: 0,
@@ -267,7 +267,11 @@ pub fn testnet_genesis(
}),
grandpa: Some(GrandpaConfig {
authorities: initial_authorities.clone().into_iter().map(|k| (k, 1)).collect(),
})
}),
fees: Some(FeesConfig {
transaction_base_fee: 1,
transaction_byte_fee: 0,
}),
}
}
+1
View File
@@ -31,6 +31,7 @@ treasury = { package = "srml-treasury", path = "../../srml/treasury" }
contract = { package = "srml-contract", path = "../../srml/contract" }
grandpa = { package = "srml-grandpa", path = "../../srml/grandpa" }
indices = { package = "srml-indices", path = "../../srml/indices" }
fees = { package = "srml-fees", path = "../../srml/fees" }
wabt = "~0.7.4"
[features]
+35 -21
View File
@@ -46,7 +46,7 @@ mod tests {
use system::{EventRecord, Phase};
use node_runtime::{Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances,
BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, System,
SystemConfig, GrandpaConfig, IndicesConfig, Event, Log};
SystemConfig, GrandpaConfig, IndicesConfig, FeesConfig, Event, Log};
use wabt;
use hex_literal::{hex, hex_impl};
use primitives::map;
@@ -112,13 +112,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = executor().call::<_, NeverNativeValue, fn() -> _>(
@@ -145,13 +145,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = executor().call::<_, NeverNativeValue, fn() -> _>(
@@ -178,13 +178,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = executor().call::<_, NeverNativeValue, fn() -> _>(
@@ -215,13 +215,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = executor().call::<_, NeverNativeValue, fn() -> _>(
@@ -266,8 +266,6 @@ mod tests {
(alice(), 111),
(charlie(), 100_000_000),
],
transaction_base_fee: 1,
transaction_byte_fee: 0,
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
@@ -305,6 +303,10 @@ mod tests {
(Keyring::Charlie.to_raw_public().into(), 1),
],
}),
fees: Some(FeesConfig {
transaction_base_fee: 1,
transaction_byte_fee: 0,
}),
}.build_storage().unwrap().0)
}
@@ -512,6 +514,10 @@ mod tests {
EventRecord {
phase: Phase::Finalization,
event: Event::treasury(treasury::RawEvent::Rollover(0))
},
EventRecord {
phase: Phase::Finalization,
event: Event::fees(fees::RawEvent::Charged(1, 1))
}
]);
});
@@ -589,6 +595,14 @@ mod tests {
EventRecord {
phase: Phase::Finalization,
event: Event::treasury(treasury::RawEvent::Rollover(0))
},
EventRecord {
phase: Phase::Finalization,
event: Event::fees(fees::RawEvent::Charged(1, 1))
},
EventRecord {
phase: Phase::Finalization,
event: Event::fees(fees::RawEvent::Charged(2, 1))
}
]);
});
@@ -800,13 +814,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(foreign_code, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![70u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_initialise_block", &vec![].and(&from_block_number(1u64)));
@@ -822,13 +836,13 @@ mod tests {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(foreign_code, map![
twox_128(&<balances::FreeBalance<Runtime>>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TotalIssuance<Runtime>>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
twox_128(<balances::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::ExistentialDeposit<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::CreationFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<balances::TransferFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<indices::NextEnumSet<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32]
twox_128(&<system::BlockHash<Runtime>>::key_for(0)).to_vec() => vec![0u8; 32],
twox_128(<fees::TransactionBaseFee<Runtime>>::key()).to_vec() => vec![0u8; 16],
twox_128(<fees::TransactionByteFee<Runtime>>::key()).to_vec() => vec![0u8; 16]
]);
let r = WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_initialise_block", &vec![].and(&from_block_number(1u64)));
+2
View File
@@ -31,6 +31,7 @@ timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default
treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false }
sudo = { package = "srml-sudo", path = "../../srml/sudo", default-features = false }
srml-upgrade-key = { path = "../../srml/upgrade-key", default-features = false }
fees = { package = "srml-fees", path = "../../srml/fees", default-features = false }
node-primitives = { path = "../primitives", default-features = false }
consensus_aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default-features = false }
rustc-hex = { version = "2.0", optional = true }
@@ -61,6 +62,7 @@ std = [
"treasury/std",
"sudo/std",
"srml-upgrade-key/std",
"fees/std",
"version/std",
"node-primitives/std",
"serde/std",
+9 -2
View File
@@ -60,7 +60,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 28,
spec_version: 29,
impl_version: 29,
apis: RUNTIME_API_VERSIONS,
};
@@ -107,6 +107,12 @@ impl balances::Trait for Runtime {
type Event = Event;
}
impl fees::Trait for Runtime {
type Event = Event;
type Amount = Balance;
type TransferAsset = Balances;
}
impl consensus::Trait for Runtime {
type Log = Log;
type SessionKey = SessionKey;
@@ -210,6 +216,7 @@ construct_runtime!(
Treasury: treasury,
Contract: contract::{Module, Call, Storage, Config<T>, Event<T>},
Sudo: sudo,
Fees: fees::{Module, Storage, Config<T>, Event<T>},
}
);
@@ -228,7 +235,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic<Address,
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Balances, AllModules>;
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Fees, AllModules>;
impl_runtime_apis! {
impl client_api::Core<Block> for Runtime {
+18
View File
@@ -651,6 +651,7 @@ dependencies = [
"srml-council 0.1.0",
"srml-democracy 0.1.0",
"srml-executive 0.1.0",
"srml-fees 0.1.0",
"srml-grandpa 0.1.0",
"srml-indices 0.1.0",
"srml-session 0.1.0",
@@ -1349,6 +1350,7 @@ dependencies = [
"sr-sandbox 0.1.0",
"sr-std 0.1.0",
"srml-balances 0.1.0",
"srml-fees 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"srml-timestamp 0.1.0",
@@ -1402,6 +1404,22 @@ dependencies = [
"srml-system 0.1.0",
]
[[package]]
name = "srml-fees"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.81 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"srml-support 0.1.0",
"srml-system 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]]
name = "srml-grandpa"
version = "0.1.0"
+23 -21
View File
@@ -28,11 +28,11 @@ use rstd::prelude::*;
use rstd::{cmp, result};
use parity_codec::Codec;
use parity_codec_derive::{Encode, Decode};
use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module};
use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module, ensure};
use srml_support::traits::{UpdateBalanceOutcome, Currency, EnsureAccountLiquid, OnFreeBalanceZero};
use srml_support::dispatch::Result;
use primitives::traits::{Zero, SimpleArithmetic, MakePayment,
As, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug};
use primitives::traits::{Zero, SimpleArithmetic,
As, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug, TransferAsset};
use system::{IsDeadAccount, OnNewAccount, ensure_signed};
mod mock;
@@ -122,7 +122,7 @@ decl_storage! {
let per_block = balance / length;
let offset = begin * per_block + balance;
(who.clone(), VestingSchedule { offset, per_block })
})
}).collect::<Vec<_>>()
@@ -154,14 +154,6 @@ decl_storage! {
/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
pub ReservedBalance get(reserved_balance): map T::AccountId => T::Balance;
// Payment stuff.
/// The fee to be paid for making a transaction; the base.
pub TransactionBaseFee get(transaction_base_fee) config(): T::Balance;
/// The fee to be paid for making a transaction; the per-byte portion.
pub TransactionByteFee get(transaction_byte_fee) config(): T::Balance;
}
add_extra_genesis {
config(balances): Vec<(T::AccountId, T::Balance)>;
@@ -505,15 +497,25 @@ where
}
}
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
let b = Self::free_balance(transactor);
let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * <T::Balance as As<u64>>::sa(encoded_len as u64);
if b < transaction_fee + Self::existential_deposit() {
return Err("not enough funds for transaction fee");
}
Self::set_free_balance(transactor, b - transaction_fee);
Self::decrease_total_stake_by(transaction_fee);
impl<T: Trait> TransferAsset<T::AccountId> for Module<T> {
type Amount = T::Balance;
fn transfer(from: &T::AccountId, to: &T::AccountId, amount: T::Balance) -> Result {
Self::make_transfer(from, to, amount)
}
fn remove_from(who: &T::AccountId, value: T::Balance) -> Result {
T::EnsureAccountLiquid::ensure_account_liquid(who)?;
let b = Self::free_balance(who);
ensure!(b >= value, "account has too few funds");
Self::set_free_balance(who, b - value);
Self::decrease_total_stake_by(value);
Ok(())
}
fn add_to(who: &T::AccountId, value: T::Balance) -> Result {
Self::set_free_balance_creating(who, Self::free_balance(who) + value);
Self::increase_total_stake_by(value);
Ok(())
}
}
-2
View File
@@ -106,8 +106,6 @@ impl ExtBuilder {
} else {
vec![(10, balance_factor), (20, balance_factor)]
},
transaction_base_fee: 0,
transaction_byte_fee: 0,
existential_deposit: self.existential_deposit,
transfer_fee: self.transfer_fee,
creation_fee: self.creation_fee,
+2
View File
@@ -20,6 +20,7 @@ srml-support = { path = "../support", default-features = false }
system = { package = "srml-system", path = "../system", default-features = false }
balances = { package = "srml-balances", path = "../balances", default-features = false }
timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false }
fees = { package = "srml-fees", path = "../fees", default-features = false }
[dev-dependencies]
wabt = "~0.7.4"
@@ -45,4 +46,5 @@ std = [
"timestamp/std",
"parity-wasm/std",
"pwasm-utils/std",
"fees/std",
]
+6 -5
View File
@@ -78,6 +78,7 @@ use srml_support::traits::OnFreeBalanceZero;
use system::{ensure_signed, RawOrigin};
use runtime_io::{blake2_256, twox_128};
use timestamp;
use fees;
pub type CodeHash<T> = <T as system::Trait>::Hash;
@@ -91,7 +92,7 @@ pub trait ComputeDispatchFee<Call, Balance> {
fn compute_dispatch_fee(call: &Call) -> Balance;
}
pub trait Trait: balances::Trait + timestamp::Trait {
pub trait Trait: fees::Trait + balances::Trait + timestamp::Trait {
/// The outer call dispatch type.
type Call: Parameter + Dispatchable<Origin=<Self as system::Trait>::Origin>;
@@ -135,14 +136,14 @@ where
}
/// The default dispatch fee computor computes the fee in the same way that
/// implementation of `MakePayment` for balances module does.
/// implementation of `ChargeBytesFee` for fees module does.
pub struct DefaultDispatchFeeComputor<T: Trait>(PhantomData<T>);
impl<T: Trait> ComputeDispatchFee<T::Call, T::Balance> for DefaultDispatchFeeComputor<T> {
fn compute_dispatch_fee(call: &T::Call) -> T::Balance {
let encoded_len = parity_codec::Encode::encode(&call).len();
let base_fee = <balances::Module<T>>::transaction_base_fee();
let byte_fee = <balances::Module<T>>::transaction_byte_fee();
base_fee + byte_fee * <T::Balance as As<u64>>::sa(encoded_len as u64)
let base_fee = <fees::Module<T>>::transaction_base_fee();
let byte_fee = <fees::Module<T>>::transaction_byte_fee();
<T::Balance as As<u64>>::sa(base_fee.as_() + byte_fee.as_() * encoded_len as u64)
}
}
+7 -3
View File
@@ -27,6 +27,7 @@ use runtime_io;
use srml_support::{StorageMap, StorageDoubleMap, assert_ok, impl_outer_event, impl_outer_dispatch, impl_outer_origin};
use substrate_primitives::{Blake2Hasher};
use system::{self, Phase, EventRecord};
use fees;
use {wabt, balances, consensus};
use hex_literal::*;
use assert_matches::assert_matches;
@@ -44,7 +45,7 @@ mod contract {
}
impl_outer_event! {
pub enum MetaEvent for Test {
balances<T>, contract<T>,
balances<T>, contract<T>, fees<T>,
}
}
impl_outer_origin! {
@@ -88,6 +89,11 @@ impl consensus::Trait for Test {
type SessionKey = UintAuthorityId;
type InherentOfflineReport = ();
}
impl fees::Trait for Test {
type Event = MetaEvent;
type Amount = u64;
type TransferAsset = Balances;
}
impl Trait for Test {
type Call = Call;
type Gas = u64;
@@ -165,8 +171,6 @@ impl ExtBuilder {
t.extend(
balances::GenesisConfig::<Test> {
balances: vec![],
transaction_base_fee: 0,
transaction_byte_fee: 0,
existential_deposit: self.existential_deposit,
transfer_fee: self.transfer_fee,
creation_fee: self.creation_fee,
-2
View File
@@ -100,8 +100,6 @@ mod tests {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
t.extend(balances::GenesisConfig::<Test>{
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
transaction_base_fee: 0,
transaction_byte_fee: 0,
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
-2
View File
@@ -486,8 +486,6 @@ mod tests {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
t.extend(balances::GenesisConfig::<Test>{
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
transaction_base_fee: 0,
transaction_byte_fee: 0,
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
+1
View File
@@ -18,6 +18,7 @@ hex-literal = "0.1.0"
substrate-primitives = { path = "../../core/primitives" }
srml-indices = { path = "../indices" }
balances = { package = "srml-balances", path = "../balances" }
fees = { package = "srml-fees", path = "../fees" }
parity-codec-derive = { version = "3.0", default-features = false }
[features]
+19 -10
View File
@@ -18,11 +18,12 @@
#![cfg_attr(not(feature = "std"), no_std)]
use rstd::prelude::*;
use rstd::marker::PhantomData;
use rstd::result;
use primitives::traits::{self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise,
OnInitialise, MakePayment, Hash, As, Digest};
OnInitialise, ChargeBytesFee, Hash, As, Digest};
use srml_support::Dispatchable;
use parity_codec::{Codec, Encode};
use system::extrinsics_root;
@@ -58,7 +59,7 @@ impl<
Context: Default,
System: system::Trait,
Block: traits::Block<Header=System::Header, Hash=System::Hash>,
Payment: MakePayment<System::AccountId>,
Payment: ChargeBytesFee<System::AccountId>,
AllModules: OnInitialise<System::BlockNumber> + OnFinalise<System::BlockNumber>,
> Executive<System, Block, Context, Payment, AllModules> where
Block::Extrinsic: Checkable<Context> + Codec,
@@ -166,7 +167,7 @@ impl<
) }
// pay any fees.
Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?;
Payment::charge_base_bytes_fee(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?;
// AUDIT: Under no circumstances may this function panic from here onwards.
@@ -238,7 +239,7 @@ impl<
if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) {
// pay any fees.
if Payment::make_payment(sender, encoded_len).is_err() {
if Payment::charge_base_bytes_fee(sender, encoded_len).is_err() {
return TransactionValidity::Invalid(ApplyError::CantPay as i8)
}
@@ -284,6 +285,7 @@ mod tests {
use primitives::testing::{Digest, DigestItem, Header, Block};
use srml_support::{traits::Currency, impl_outer_origin, impl_outer_event};
use system;
use fees;
use hex_literal::{hex, hex_impl};
impl_outer_origin! {
@@ -293,7 +295,7 @@ mod tests {
impl_outer_event!{
pub enum MetaEvent for Runtime {
balances<T>,
balances<T>, fees<T>,
}
}
@@ -320,22 +322,29 @@ mod tests {
type EnsureAccountLiquid = ();
type Event = MetaEvent;
}
impl fees::Trait for Runtime {
type Event = MetaEvent;
type Amount = u64;
type TransferAsset = balances::Module<Runtime>;
}
type TestXt = primitives::testing::TestXt<Call<Runtime>>;
type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, balances::Module<Runtime>, ()>;
type Executive = super::Executive<Runtime, Block<TestXt>, system::ChainContext<Runtime>, fees::Module<Runtime>, ()>;
#[test]
fn balance_transfer_dispatch_works() {
let mut t = system::GenesisConfig::<Runtime>::default().build_storage().unwrap().0;
t.extend(balances::GenesisConfig::<Runtime> {
balances: vec![(1, 111)],
transaction_base_fee: 10,
transaction_byte_fee: 0,
existential_deposit: 0,
transfer_fee: 0,
creation_fee: 0,
vesting: vec![],
}.build_storage().unwrap().0);
t.extend(fees::GenesisConfig::<Runtime> {
transaction_base_fee: 10,
transaction_byte_fee: 0,
}.build_storage().unwrap().0);
let xt = primitives::testing::TestXt(Some(1), 0, Call::transfer(2, 69));
let mut t = runtime_io::TestExternalities::<Blake2Hasher>::new(t);
with_externalities(&mut t, || {
@@ -360,7 +369,7 @@ mod tests {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(),
state_root: hex!("6651861f40a8f42c033b3e937cb3513e6dbaf4be6bafb1561a19f884be3f58dd").into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
@@ -394,7 +403,7 @@ mod tests {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(),
state_root: hex!("6651861f40a8f42c033b3e937cb3513e6dbaf4be6bafb1561a19f884be3f58dd").into(),
extrinsics_root: [0u8; 32].into(),
digest: Digest { logs: vec![], },
},
+31
View File
@@ -0,0 +1,31 @@
[package]
name = "srml-fees"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
hex-literal = "0.1.0"
serde = { version = "1.0", default-features = false }
parity-codec = { version = "3.0", default-features = false }
parity-codec-derive = { version = "3.0", default-features = false }
primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false }
rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false }
runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false }
srml-support = { package = "srml-support", path = "../support", default-features = false }
system = { package = "srml-system", path = "../system", default-features = false }
[features]
default = ["std"]
std = [
"serde/std",
"parity-codec/std",
"parity-codec-derive/std",
"primitives/std",
"rstd/std",
"runtime_io/std",
"runtime_primitives/std",
"srml-support/std",
"system/std",
]
+116
View File
@@ -0,0 +1,116 @@
// 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/>.
//! Handles all transaction fee related operations
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
use srml_support::{dispatch::Result, Parameter, StorageMap, decl_event, decl_storage, decl_module};
use runtime_primitives::traits::{
As, Member, SimpleArithmetic, ChargeBytesFee, ChargeFee,
TransferAsset, CheckedAdd, CheckedSub, CheckedMul, Zero
};
use system;
mod mock;
mod tests;
pub trait Trait: system::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
/// The unit for fee amount
type Amount: Member + Parameter + SimpleArithmetic + Default + Copy + As<u64>;
/// A function does the asset transfer between accounts
type TransferAsset: TransferAsset<Self::AccountId, Amount = Self::Amount>;
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
fn deposit_event<T>() = default;
fn on_finalise() {
let extrinsic_count = <system::Module<T>>::extrinsic_count();
(0..extrinsic_count).for_each(|index| {
// Deposit `Charged` event if some amount of fee charged.
let fee = <CurrentTransactionFee<T>>::take(index);
if !fee.is_zero() {
Self::deposit_event(RawEvent::Charged(index, fee));
}
});
}
}
}
decl_event!(
pub enum Event<T> where <T as Trait>::Amount {
/// Fee charged (extrinsic_index, fee_amount)
Charged(u32, Amount),
}
);
decl_storage! {
trait Store for Module<T: Trait> as Fees {
/// The fee to be paid for making a transaction; the base.
pub TransactionBaseFee get(transaction_base_fee) config(): T::Amount;
/// The fee to be paid for making a transaction; the per-byte portion.
pub TransactionByteFee get(transaction_byte_fee) config(): T::Amount;
/// The `extrinsic_index => accumulated_fees` map, containing records to
/// track the overall charged fees for each transaction.
///
/// All records should be removed at finalise stage.
CurrentTransactionFee get(current_transaction_fee): map u32 => T::Amount;
}
}
impl<T: Trait> ChargeBytesFee<T::AccountId> for Module<T> {
fn charge_base_bytes_fee(transactor: &T::AccountId, encoded_len: usize) -> Result {
let bytes_fee = Self::transaction_byte_fee().checked_mul(
&<T::Amount as As<u64>>::sa(encoded_len as u64)
).ok_or_else(|| "bytes fee overflow")?;
let overall = Self::transaction_base_fee().checked_add(&bytes_fee).ok_or_else(|| "bytes fee overflow")?;
Self::charge_fee(transactor, overall)
}
}
impl<T: Trait> ChargeFee<T::AccountId> for Module<T> {
type Amount = T::Amount;
fn charge_fee(transactor: &T::AccountId, amount: T::Amount) -> Result {
let extrinsic_index = <system::Module<T>>::extrinsic_index().ok_or_else(|| "no extrinsic index found")?;
let current_fee = Self::current_transaction_fee(extrinsic_index);
let new_fee = current_fee.checked_add(&amount).ok_or_else(|| "fee got overflow after charge")?;
T::TransferAsset::remove_from(transactor, amount)?;
<CurrentTransactionFee<T>>::insert(extrinsic_index, new_fee);
Ok(())
}
fn refund_fee(transactor: &T::AccountId, amount: T::Amount) -> Result {
let extrinsic_index = <system::Module<T>>::extrinsic_index().ok_or_else(|| "no extrinsic index found")?;
let current_fee = Self::current_transaction_fee(extrinsic_index);
let new_fee = current_fee.checked_sub(&amount).ok_or_else(|| "fee got underflow after refund")?;
T::TransferAsset::add_to(transactor, amount)?;
<CurrentTransactionFee<T>>::insert(extrinsic_index, new_fee);
Ok(())
}
}
+109
View File
@@ -0,0 +1,109 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Test utilities
#![cfg(test)]
use runtime_primitives::BuildStorage;
use runtime_primitives::{
traits::{IdentityLookup, BlakeTwo256, TransferAsset},
testing::{Digest, DigestItem, Header},
};
use primitives::{H256, Blake2Hasher};
use runtime_io;
use srml_support::{impl_outer_origin, impl_outer_event};
use crate::{GenesisConfig, Module, Trait, system};
impl_outer_origin!{
pub enum Origin for Test {}
}
mod fees {
pub use crate::Event;
}
impl_outer_event!{
pub enum TestEvent for Test {
fees<T>,
}
}
pub struct TransferAssetMock;
impl<AccountId> TransferAsset<AccountId> for TransferAssetMock {
type Amount = u64;
fn transfer(_: &AccountId, _: &AccountId, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
fn remove_from(_: &AccountId, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
fn add_to(_: &AccountId, _: Self::Amount) -> Result<(), &'static str> { Ok(()) }
}
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Test;
impl system::Trait for Test {
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type Event = TestEvent;
type Log = DigestItem;
}
impl Trait for Test {
type Amount = u64;
type Event =TestEvent;
type TransferAsset = TransferAssetMock;
}
pub type System = system::Module<Test>;
pub type Fees = Module<Test>;
pub struct ExtBuilder {
transaction_base_fee: u64,
transaction_byte_fee: u64,
}
impl Default for ExtBuilder {
fn default() -> Self {
Self {
transaction_base_fee: 0,
transaction_byte_fee: 0,
}
}
}
impl ExtBuilder {
pub fn transaction_base_fee(mut self, transaction_base_fee: u64) -> Self {
self.transaction_base_fee = transaction_base_fee;
self
}
pub fn transaction_byte_fee(mut self, transaction_byte_fee: u64) -> Self {
self.transaction_byte_fee = transaction_byte_fee;
self
}
pub fn build(self) -> runtime_io::TestExternalities<Blake2Hasher> {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
t.extend(GenesisConfig::<Test> {
transaction_base_fee: self.transaction_base_fee,
transaction_byte_fee: self.transaction_byte_fee,
}.build_storage().unwrap().0);
t.into()
}
}
+174
View File
@@ -0,0 +1,174 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Tests for the module.
#![cfg(test)]
use super::*;
use runtime_io::with_externalities;
use runtime_primitives::traits::{OnFinalise};
use system::{EventRecord, Phase};
use mock::{Fees, System, ExtBuilder};
use srml_support::{assert_ok, assert_err};
#[test]
fn charge_base_bytes_fee_should_work() {
with_externalities(
&mut ExtBuilder::default()
.transaction_base_fee(3)
.transaction_byte_fee(5)
.build(),
|| {
System::set_extrinsic_index(0);
assert_ok!(Fees::charge_base_bytes_fee(&0, 7));
assert_eq!(Fees::current_transaction_fee(0), 3 + 5 * 7);
System::set_extrinsic_index(1);
assert_ok!(Fees::charge_base_bytes_fee(&0, 11));
assert_eq!(Fees::current_transaction_fee(1), 3 + 5 * 11);
System::set_extrinsic_index(3);
assert_ok!(Fees::charge_base_bytes_fee(&0, 13));
assert_eq!(Fees::current_transaction_fee(3), 3 + 5 * 13);
}
);
}
#[test]
fn charge_base_bytes_fee_should_not_work_if_bytes_fee_overflow() {
// bytes fee overflows.
with_externalities(
&mut ExtBuilder::default()
.transaction_base_fee(0)
.transaction_byte_fee(u64::max_value())
.build(),
|| {
System::set_extrinsic_index(0);
assert_err!(
Fees::charge_base_bytes_fee(&0, 2),
"bytes fee overflow"
);
}
);
}
#[test]
fn charge_base_bytes_fee_should_not_work_if_overall_fee_overflow() {
// bytes fee doesn't overflow, but overall fee (bytes_fee + base_fee) does
with_externalities(
&mut ExtBuilder::default()
.transaction_base_fee(u64::max_value())
.transaction_byte_fee(1)
.build(),
|| {
System::set_extrinsic_index(0);
assert_err!(
Fees::charge_base_bytes_fee(&0, 1),
"bytes fee overflow"
);
}
);
}
#[test]
fn charge_fee_should_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
System::set_extrinsic_index(0);
assert_ok!(Fees::charge_fee(&0, 2));
assert_ok!(Fees::charge_fee(&0, 3));
assert_eq!(Fees::current_transaction_fee(0), 2 + 3);
System::set_extrinsic_index(2);
assert_ok!(Fees::charge_fee(&0, 5));
assert_ok!(Fees::charge_fee(&0, 7));
assert_eq!(Fees::current_transaction_fee(2), 5 + 7);
});
}
#[test]
fn charge_fee_when_overflow_should_not_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
System::set_extrinsic_index(0);
assert_ok!(Fees::charge_fee(&0, u64::max_value()));
assert_err!(Fees::charge_fee(&0, 1), "fee got overflow after charge");
});
}
#[test]
fn refund_fee_should_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
System::set_extrinsic_index(0);
assert_ok!(Fees::charge_fee(&0, 5));
assert_ok!(Fees::refund_fee(&0, 3));
assert_eq!(Fees::current_transaction_fee(0), 5 - 3);
});
}
#[test]
fn refund_fee_when_underflow_should_not_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
System::set_extrinsic_index(0);
assert_err!(Fees::refund_fee(&0, 1), "fee got underflow after refund");
});
}
#[test]
fn on_finalise_should_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
// charge fees in extrinsic index 3
System::set_extrinsic_index(3);
assert_ok!(Fees::charge_fee(&0, 1));
System::note_applied_extrinsic(&Ok(()), 1);
// charge fees in extrinsic index 5
System::set_extrinsic_index(5);
assert_ok!(Fees::charge_fee(&0, 1));
System::note_applied_extrinsic(&Ok(()), 1);
System::note_finished_extrinsics();
// `current_transaction_fee`, `extrinsic_count` should be as expected.
assert_eq!(Fees::current_transaction_fee(3), 1);
assert_eq!(Fees::current_transaction_fee(5), 1);
assert_eq!(System::extrinsic_count(), 5 + 1);
<Fees as OnFinalise<u64>>::on_finalise(1);
// When finalised, `CurrentTransactionFee` records should be cleared.
assert_eq!(Fees::current_transaction_fee(3), 0);
assert_eq!(Fees::current_transaction_fee(5), 0);
// When finalised, if any fee charged in a extrinsic, a `Charged` event should be deposited
// for it.
let fee_charged_events: Vec<EventRecord<mock::TestEvent>> = System::events()
.into_iter()
.filter(|e| match e.event {
mock::TestEvent::fees(RawEvent::Charged(_, _)) => return true,
_ => return false,
})
.collect();
assert_eq!(fee_charged_events, vec![
EventRecord {
phase: Phase::Finalization,
event: RawEvent::Charged(3, 1).into(),
},
EventRecord {
phase: Phase::Finalization,
event: RawEvent::Charged(5, 1).into(),
},
]);
});
}
-2
View File
@@ -104,8 +104,6 @@ pub fn new_test_ext(
} else {
vec![(10, balance_factor), (20, balance_factor)]
},
transaction_base_fee: 0,
transaction_byte_fee: 0,
existential_deposit: ext_deposit,
transfer_fee: 0,
creation_fee: 0,
+5
View File
@@ -273,6 +273,11 @@ impl<T: Trait> Module<T> {
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX)
}
/// Gets extrinsics count.
pub fn extrinsic_count() -> u32 {
<ExtrinsicCount<T>>::get().unwrap_or_default()
}
/// Gets a total length of all executed extrinsics.
pub fn all_extrinsics_len() -> u32 {
<AllExtrinsicsLen<T>>::get().unwrap_or_default()
-2
View File
@@ -303,8 +303,6 @@ mod tests {
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
t.extend(balances::GenesisConfig::<Test>{
balances: vec![(0, 100), (1, 99), (2, 1)],
transaction_base_fee: 0,
transaction_byte_fee: 0,
transfer_fee: 0,
creation_fee: 0,
existential_deposit: 0,