diff --git a/Cargo.lock b/Cargo.lock index 34309759af206..84ef183d9d649 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 3d27fa72d54e0..98e3a439d918d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ members = [ "srml/democracy", "srml/example", "srml/executive", + "srml/fees", "srml/grandpa", "srml/indices", "srml/metadata", diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index 2e956c855b455..5e12d571ae201 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -126,15 +126,57 @@ pub trait BlockNumberToHash { } } -/// Simple payment making trait, operating on a single generic `AccountId` type. -pub trait MakePayment { - /// 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 { + /// 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 MakePayment for () { - fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } +/// Charge fee trait +pub trait ChargeFee: ChargeBytesFee { + /// 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 { + /// 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 ChargeBytesFee for () { + fn charge_base_bytes_fee(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } +} + +impl ChargeFee for () { + type Amount = (); + + fn charge_fee(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) } + fn refund_fee(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) } +} + +impl TransferAsset 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. diff --git a/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index f7f569548e0db..9bf282dec8778 100644 Binary files a/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 00ec0eb367148..99cb977b44e66 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -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", diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 079f11918f707..39a18593f1e00 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -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, Event}, // Used for the module template in `./template.rs` TemplateModule: template::{Module, Call, Storage, Event}, } @@ -208,7 +215,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; +pub type Executive = executive::Executive; // Implement our runtime API endpoints. This is just a bunch of proxying. impl_runtime_apis! { diff --git a/node-template/runtime/wasm/Cargo.lock b/node-template/runtime/wasm/Cargo.lock index e383e5df678ef..905538fa8fbfd 100644 --- a/node-template/runtime/wasm/Cargo.lock +++ b/node-template/runtime/wasm/Cargo.lock @@ -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" diff --git a/node-template/src/chain_spec.rs b/node-template/src/chain_spec.rs index 6b1370b102d69..b9f61e45f546b 100644 --- a/node-template/src/chain_spec.rs +++ b/node-template/src/chain_spec.rs @@ -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, 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, endowed_account sudo: Some(SudoConfig { key: root_key, }), + fees: Some(FeesConfig { + transaction_base_fee: 1, + transaction_byte_fee: 0, + }) } } diff --git a/node/cli/src/chain_spec.rs b/node/cli/src/chain_spec.rs index 878ffb2b10b14..cb693f2b1d2f2 100644 --- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -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, + }), } } diff --git a/node/executor/Cargo.toml b/node/executor/Cargo.toml index ac99c3c5f05f8..501c8a7b54f56 100644 --- a/node/executor/Cargo.toml +++ b/node/executor/Cargo.toml @@ -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] diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 5810756c17f0b..2edd2b7966df9 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -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::::new_with_code(BLOATY_CODE, map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![70u8; 16], + twox_128(>::key()).to_vec() => vec![0u8; 16] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -145,13 +145,13 @@ mod tests { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![70u8; 16], + twox_128(>::key()).to_vec() => vec![0u8; 16] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -178,13 +178,13 @@ mod tests { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![0u8; 16], + twox_128(>::key()).to_vec() => vec![0u8; 16] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -215,13 +215,13 @@ mod tests { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![0u8; 16], + twox_128(>::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::::new_with_code(foreign_code, map![ twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![70u8; 16], + twox_128(>::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::::new_with_code(foreign_code, map![ twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], twox_128(>::key()).to_vec() => vec![0u8; 16], - twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] + twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32], + twox_128(>::key()).to_vec() => vec![0u8; 16], + twox_128(>::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))); diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index de16dbf48573d..1c62c328947ae 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -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", diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index dd1ae3b272d73..2a3b8d873fd6b 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -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, Event}, Sudo: sudo, + Fees: fees::{Module, Storage, Config, Event}, } ); @@ -228,7 +235,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive, Balances, AllModules>; +pub type Executive = executive::Executive, Fees, AllModules>; impl_runtime_apis! { impl client_api::Core for Runtime { diff --git a/node/runtime/wasm/Cargo.lock b/node/runtime/wasm/Cargo.lock index 21c895238471a..d740d57ac6ad6 100644 --- a/node/runtime/wasm/Cargo.lock +++ b/node/runtime/wasm/Cargo.lock @@ -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" diff --git a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 58d2541f73009..3eb99c7be3ccf 100644 Binary files a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index e2edf5e3e4e52..554f92e868d62 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -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::>() @@ -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 MakePayment for Module { - 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() * >::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 TransferAsset for Module { + 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(()) } } diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 14892313f9a77..6dcff18c6eb49 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -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, diff --git a/srml/contract/Cargo.toml b/srml/contract/Cargo.toml index 291d65ba35602..1a5b5eb06916b 100644 --- a/srml/contract/Cargo.toml +++ b/srml/contract/Cargo.toml @@ -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", ] diff --git a/srml/contract/src/lib.rs b/srml/contract/src/lib.rs index b5f5f2c00eb06..c92c08ef85d1e 100644 --- a/srml/contract/src/lib.rs +++ b/srml/contract/src/lib.rs @@ -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 = ::Hash; @@ -91,7 +92,7 @@ pub trait ComputeDispatchFee { 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>; @@ -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(PhantomData); impl ComputeDispatchFee for DefaultDispatchFeeComputor { fn compute_dispatch_fee(call: &T::Call) -> T::Balance { let encoded_len = parity_codec::Encode::encode(&call).len(); - let base_fee = >::transaction_base_fee(); - let byte_fee = >::transaction_byte_fee(); - base_fee + byte_fee * >::sa(encoded_len as u64) + let base_fee = >::transaction_base_fee(); + let byte_fee = >::transaction_byte_fee(); + >::sa(base_fee.as_() + byte_fee.as_() * encoded_len as u64) } } diff --git a/srml/contract/src/tests.rs b/srml/contract/src/tests.rs index 80efd3785d65a..6febc2dec475b 100644 --- a/srml/contract/src/tests.rs +++ b/srml/contract/src/tests.rs @@ -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, contract, + balances, contract, fees, } } 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:: { 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, diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index d92b0d7178699..20c95b2bd9ff4 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -100,8 +100,6 @@ mod tests { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig::{ 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, diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 6cd9c07979e41..5045a0ca3d8b3 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -486,8 +486,6 @@ mod tests { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig::{ 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, diff --git a/srml/executive/Cargo.toml b/srml/executive/Cargo.toml index bc7e8616fb9ae..836816a14c781 100644 --- a/srml/executive/Cargo.toml +++ b/srml/executive/Cargo.toml @@ -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] diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 04c44a53d3fce..dfc369174fefe 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -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, - Payment: MakePayment, + Payment: ChargeBytesFee, AllModules: OnInitialise + OnFinalise, > Executive where Block::Extrinsic: Checkable + 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, + balances, fees, } } @@ -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; + } type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive, system::ChainContext, balances::Module, ()>; + type Executive = super::Executive, system::ChainContext, fees::Module, ()>; #[test] fn balance_transfer_dispatch_works() { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig:: { 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:: { + 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::::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![], }, }, diff --git a/srml/fees/Cargo.toml b/srml/fees/Cargo.toml new file mode 100644 index 0000000000000..d3883e09b0bec --- /dev/null +++ b/srml/fees/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "srml-fees" +version = "0.1.0" +authors = ["Parity Technologies "] +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", +] diff --git a/srml/fees/src/lib.rs b/srml/fees/src/lib.rs new file mode 100644 index 0000000000000..a86ea68cdb46e --- /dev/null +++ b/srml/fees/src/lib.rs @@ -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 . + +//! 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> + Into<::Event>; + + /// The unit for fee amount + type Amount: Member + Parameter + SimpleArithmetic + Default + Copy + As; + + /// A function does the asset transfer between accounts + type TransferAsset: TransferAsset; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + + fn on_finalise() { + let extrinsic_count = >::extrinsic_count(); + (0..extrinsic_count).for_each(|index| { + // Deposit `Charged` event if some amount of fee charged. + let fee = >::take(index); + if !fee.is_zero() { + Self::deposit_event(RawEvent::Charged(index, fee)); + } + }); + } + } +} + +decl_event!( + pub enum Event where ::Amount { + /// Fee charged (extrinsic_index, fee_amount) + Charged(u32, Amount), + } +); + +decl_storage! { + trait Store for Module 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 ChargeBytesFee for Module { + fn charge_base_bytes_fee(transactor: &T::AccountId, encoded_len: usize) -> Result { + let bytes_fee = Self::transaction_byte_fee().checked_mul( + &>::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 ChargeFee for Module { + type Amount = T::Amount; + + fn charge_fee(transactor: &T::AccountId, amount: T::Amount) -> Result { + let extrinsic_index = >::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)?; + + >::insert(extrinsic_index, new_fee); + Ok(()) + } + + fn refund_fee(transactor: &T::AccountId, amount: T::Amount) -> Result { + let extrinsic_index = >::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)?; + + >::insert(extrinsic_index, new_fee); + Ok(()) + } +} diff --git a/srml/fees/src/mock.rs b/srml/fees/src/mock.rs new file mode 100644 index 0000000000000..46058e192182b --- /dev/null +++ b/srml/fees/src/mock.rs @@ -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 . + +//! 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, + } +} + +pub struct TransferAssetMock; + +impl TransferAsset 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; + 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; +pub type Fees = Module; + +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 { + let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; + t.extend(GenesisConfig:: { + transaction_base_fee: self.transaction_base_fee, + transaction_byte_fee: self.transaction_byte_fee, + }.build_storage().unwrap().0); + t.into() + } +} diff --git a/srml/fees/src/tests.rs b/srml/fees/src/tests.rs new file mode 100644 index 0000000000000..a1c96570629e8 --- /dev/null +++ b/srml/fees/src/tests.rs @@ -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 . + +//! 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); + + >::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> = 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(), + }, + ]); + }); +} diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 5fc411e609385..a76c0fed85aad 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -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, diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 2f3ea10457a48..8f3fd1df8cbaf 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -273,6 +273,11 @@ impl Module { storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX) } + /// Gets extrinsics count. + pub fn extrinsic_count() -> u32 { + >::get().unwrap_or_default() + } + /// Gets a total length of all executed extrinsics. pub fn all_extrinsics_len() -> u32 { >::get().unwrap_or_default() diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index a4fd741bb3a3e..ad861ac7ece83 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -303,8 +303,6 @@ mod tests { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig::{ 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,