diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 959d50cbb7..2301ef9100 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1908,7 +1908,6 @@ 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", @@ -1962,7 +1961,6 @@ dependencies = [ "srml-council 0.1.0", "srml-democracy 0.1.0", "srml-executive 0.1.0", - "srml-fees 0.1.0", "srml-finality-tracker 0.1.0", "srml-grandpa 0.1.0", "srml-indices 0.1.0", @@ -2025,7 +2023,6 @@ 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", @@ -3154,7 +3151,6 @@ 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", @@ -3226,29 +3222,12 @@ 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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.1.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-finality-tracker" version = "0.1.0" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 826dc3f1fb..4366458832 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -67,7 +67,6 @@ members = [ "srml/democracy", "srml/example", "srml/executive", - "srml/fees", "srml/finality-tracker", "srml/grandpa", "srml/indices", diff --git a/substrate/core/keyring/src/ed25519.rs b/substrate/core/keyring/src/ed25519.rs index c414e4bf43..f36d8fd485 100644 --- a/substrate/core/keyring/src/ed25519.rs +++ b/substrate/core/keyring/src/ed25519.rs @@ -18,7 +18,7 @@ use std::{collections::HashMap, ops::Deref}; use lazy_static::lazy_static; -use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as _Pair, H256}; +use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, H256}; pub use substrate_primitives::ed25519; /// Set of test accounts. @@ -162,7 +162,7 @@ impl Deref for Keyring { #[cfg(test)] mod tests { use super::*; - use substrate_primitives::{ed25519::Pair, Pair as _Pair}; + use substrate_primitives::{ed25519::Pair, Pair as PairT}; #[test] fn should_work() { diff --git a/substrate/core/keyring/src/sr25519.rs b/substrate/core/keyring/src/sr25519.rs index 17404ad1c6..1d3342d86d 100644 --- a/substrate/core/keyring/src/sr25519.rs +++ b/substrate/core/keyring/src/sr25519.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::ops::Deref; use lazy_static::lazy_static; -use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as _Pair, H256}; +use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, H256}; pub use substrate_primitives::sr25519; /// Set of test accounts. @@ -167,7 +167,7 @@ impl Deref for Keyring { #[cfg(test)] mod tests { use super::*; - use substrate_primitives::{sr25519::Pair, Pair as _Pair}; + use substrate_primitives::{sr25519::Pair, Pair as PairT}; #[test] fn should_work() { diff --git a/substrate/core/keystore/src/lib.rs b/substrate/core/keystore/src/lib.rs index f0ab573cd6..59c1a65cfb 100644 --- a/substrate/core/keystore/src/lib.rs +++ b/substrate/core/keystore/src/lib.rs @@ -28,7 +28,7 @@ use std::io::{self, Write}; use error_chain::{bail, error_chain, error_chain_processing, impl_error_chain_processed, impl_extract_backtrace, impl_error_chain_kind}; -use substrate_primitives::{ed25519::{Pair, Public}, Pair as _Pair}; +use substrate_primitives::{ed25519::{Pair, Public}, Pair as PairT}; pub use crypto::KEY_ITERATIONS; diff --git a/substrate/core/primitives/src/ed25519.rs b/substrate/core/primitives/src/ed25519.rs index 586a6fe13c..8b55300df4 100644 --- a/substrate/core/primitives/src/ed25519.rs +++ b/substrate/core/primitives/src/ed25519.rs @@ -523,7 +523,7 @@ impl Pair { mod test { use super::*; use hex_literal::{hex, hex_impl}; - use crate::{Pair as _Pair, crypto::DEV_PHRASE}; + use crate::{Pair as PairT, crypto::DEV_PHRASE}; #[test] fn default_phrase_should_be_used() { diff --git a/substrate/core/primitives/src/sr25519.rs b/substrate/core/primitives/src/sr25519.rs index f6e135ebef..ee1727b79a 100644 --- a/substrate/core/primitives/src/sr25519.rs +++ b/substrate/core/primitives/src/sr25519.rs @@ -501,7 +501,7 @@ impl Pair { #[cfg(test)] mod test { use super::*; - use crate::{Pair as _Pair, crypto::{Ss58Codec, DEV_PHRASE, DEV_ADDRESS}}; + use crate::{Pair as PairT, crypto::{Ss58Codec, DEV_PHRASE, DEV_ADDRESS}}; use hex_literal::{hex, hex_impl}; #[test] diff --git a/substrate/core/sr-primitives/src/lib.rs b/substrate/core/sr-primitives/src/lib.rs index 6df4bdeb78..efaf4146fc 100644 --- a/substrate/core/sr-primitives/src/lib.rs +++ b/substrate/core/sr-primitives/src/lib.rs @@ -30,7 +30,7 @@ pub use serde_derive; pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; use rstd::prelude::*; -use substrate_primitives::{ed25519, sr25519, hash::{H256, H512}}; +use substrate_primitives::{ed25519, sr25519, hash::H512}; use codec::{Encode, Decode}; #[cfg(feature = "std")] @@ -379,16 +379,17 @@ impl Verify for MultiSignature { #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub struct AnySignature(H512); -/// Public key for any known crypto algorithm. -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Default, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub struct AnySigner(H256); - impl Verify for AnySignature { - type Signer = AnySigner; - fn verify>(&self, mut msg: L, signer: &AnySigner) -> bool { - runtime_io::sr25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0.as_bytes()) || - runtime_io::ed25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0.as_bytes()) + type Signer = sr25519::Public; + fn verify>(&self, mut msg: L, signer: &sr25519::Public) -> bool { + runtime_io::sr25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0) || + runtime_io::ed25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0) + } +} + +impl From for AnySignature { + fn from(s: sr25519::Signature) -> AnySignature { + AnySignature(s.0.into()) } } diff --git a/substrate/core/test-runtime/src/system.rs b/substrate/core/test-runtime/src/system.rs index 7c76fdd72b..d48fd153da 100644 --- a/substrate/core/test-runtime/src/system.rs +++ b/substrate/core/test-runtime/src/system.rs @@ -305,7 +305,7 @@ mod tests { use runtime_io::{with_externalities, twox_128, TestExternalities}; use parity_codec::{Joiner, KeyedVec}; use substrate_test_client::{AuthorityKeyring, AccountKeyring}; - use crate::{Header, Extrinsic, Transfer}; + use crate::{Header, Transfer}; use primitives::{Blake2Hasher, map}; use primitives::storage::well_known_keys; use substrate_executor::WasmExecutor; diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index 970620522b..863fe747ab 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -1227,6 +1227,23 @@ dependencies = [ "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schnorrkel" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" @@ -1672,7 +1689,7 @@ dependencies = [ "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)", + "schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2334,6 +2351,7 @@ dependencies = [ "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)" = "" +"checksum schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a700659388785588c75b197cecda0f23c7112a9281ef703e8ffc651061ce014c" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 6c7778e9ff..c4df3f06b5 100644 Binary files a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node-template/runtime/Cargo.toml b/substrate/node-template/runtime/Cargo.toml index d6cb7ea7f2..8155865730 100644 --- a/substrate/node-template/runtime/Cargo.toml +++ b/substrate/node-template/runtime/Cargo.toml @@ -15,7 +15,6 @@ 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 } @@ -37,7 +36,6 @@ std = [ "runtime-io/std", "support/std", "balances/std", - "fees/std", "executive/std", "aura/std", "indices/std", diff --git a/substrate/node-template/runtime/src/lib.rs b/substrate/node-template/runtime/src/lib.rs index f46f8df249..888a9b0dc4 100644 --- a/substrate/node-template/runtime/src/lib.rs +++ b/substrate/node-template/runtime/src/lib.rs @@ -169,11 +169,10 @@ impl balances::Trait for Runtime { type OnNewAccount = Indices; /// The uniquitous event type. type Event = Event; -} -impl fees::Trait for Runtime { - type TransferAsset = Balances; - type Event = Event; + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); } impl sudo::Trait for Runtime { @@ -200,7 +199,6 @@ 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}, } @@ -221,7 +219,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/substrate/node-template/runtime/wasm/Cargo.lock b/substrate/node-template/runtime/wasm/Cargo.lock index 07c52b00a7..48c9801535 100644 --- a/substrate/node-template/runtime/wasm/Cargo.lock +++ b/substrate/node-template/runtime/wasm/Cargo.lock @@ -783,7 +783,6 @@ 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", @@ -1261,6 +1260,23 @@ dependencies = [ "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schnorrkel" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" @@ -1518,22 +1534,6 @@ dependencies = [ "srml-system 0.1.0", ] -[[package]] -name = "srml-fees" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (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" @@ -1853,7 +1853,7 @@ dependencies = [ "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)", + "schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2485,6 +2485,7 @@ dependencies = [ "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)" = "" +"checksum schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a700659388785588c75b197cecda0f23c7112a9281ef703e8ffc651061ce014c" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/substrate/node-template/src/chain_spec.rs b/substrate/node-template/src/chain_spec.rs index 780d33be65..a81ca5fd84 100644 --- a/substrate/node-template/src/chain_spec.rs +++ b/substrate/node-template/src/chain_spec.rs @@ -1,7 +1,7 @@ use primitives::{ed25519, Pair}; use node_template_runtime::{ AccountId, GenesisConfig, ConsensusConfig, TimestampConfig, BalancesConfig, - SudoConfig, IndicesConfig, FeesConfig, + SudoConfig, IndicesConfig, }; use substrate_service; @@ -104,6 +104,8 @@ fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec< ids: endowed_accounts.clone(), }), balances: Some(BalancesConfig { + transaction_base_fee: 1, + transaction_byte_fee: 0, existential_deposit: 500, transfer_fee: 0, creation_fee: 0, @@ -113,9 +115,5 @@ fn testnet_genesis(initial_authorities: Vec, endowed_accounts: Vec< sudo: Some(SudoConfig { key: root_key, }), - fees: Some(FeesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - }) } } diff --git a/substrate/node-template/src/service.rs b/substrate/node-template/src/service.rs index 1bb97256ae..cffc30217b 100644 --- a/substrate/node-template/src/service.rs +++ b/substrate/node-template/src/service.rs @@ -15,7 +15,7 @@ use basic_authorship::ProposerFactory; use node_executor; use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra}; use substrate_client as client; -use primitives::{ed25519::Pair, Pair as _Pair}; +use primitives::{ed25519::Pair, Pair as PairT}; use inherents::InherentDataProviders; use network::construct_simple_protocol; use substrate_executor::native_executor_instance; diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index 53980de033..927e5595d1 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -20,7 +20,7 @@ use primitives::{ed25519::Public as AuthorityId, ed25519, sr25519, Pair, crypto: use node_primitives::AccountId; use node_runtime::{ConsensusConfig, CouncilSeatsConfig, CouncilVotingConfig, DemocracyConfig, SessionConfig, StakingConfig, StakerStatus, TimestampConfig, BalancesConfig, TreasuryConfig, - SudoConfig, ContractConfig, GrandpaConfig, IndicesConfig, FeesConfig, Permill, Perbill}; + SudoConfig, ContractConfig, GrandpaConfig, IndicesConfig, Permill, Perbill}; pub use node_runtime::GenesisConfig; use substrate_service; use hex_literal::{hex, hex_impl}; @@ -82,6 +82,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { }), system: None, balances: Some(BalancesConfig { + transaction_base_fee: 1 * CENTS, + transaction_byte_fee: 10 * MILLICENTS, balances: endowed_accounts.iter().cloned() .map(|k| (k, ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) @@ -163,10 +165,6 @@ fn staging_testnet_config_genesis() -> GenesisConfig { grandpa: Some(GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), - fees: Some(FeesConfig { - transaction_base_fee: 1 * CENTS, - transaction_byte_fee: 10 * MILLICENTS, - }), } } @@ -244,6 +242,8 @@ pub fn testnet_genesis( ids: endowed_accounts.clone(), }), balances: Some(BalancesConfig { + transaction_base_fee: 1, + transaction_byte_fee: 0, existential_deposit: 500, transfer_fee: 0, creation_fee: 0, @@ -319,10 +319,6 @@ pub fn testnet_genesis( grandpa: Some(GrandpaConfig { authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), - fees: Some(FeesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - }), } } diff --git a/substrate/node/cli/src/service.rs b/substrate/node/cli/src/service.rs index dd03b25bb9..e2c1c67236 100644 --- a/substrate/node/cli/src/service.rs +++ b/substrate/node/cli/src/service.rs @@ -25,7 +25,7 @@ use client; use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra}; use grandpa; use node_executor; -use primitives::{Pair as _Pair, ed25519::Pair}; +use primitives::{Pair as PairT, ed25519::Pair}; use node_primitives::Block; use node_runtime::{GenesisConfig, RuntimeApi}; use substrate_service::{ diff --git a/substrate/node/executor/Cargo.toml b/substrate/node/executor/Cargo.toml index 3e0f618bc7..572aa9dbcf 100644 --- a/substrate/node/executor/Cargo.toml +++ b/substrate/node/executor/Cargo.toml @@ -30,7 +30,6 @@ 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/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index 84b2de336e..1a96b8067f 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -31,7 +31,7 @@ mod tests { use super::Executor; use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; - use keyring::{AccountKeyring, AuthorityKeyring}; + use keyring::{AuthorityKeyring, AccountKeyring}; use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency}; use state_machine::{CodeExecutor, Externalities, TestExternalities}; use primitives::{twox_128, Blake2Hasher, ChangesTrieConfiguration, NeverNativeValue, @@ -44,7 +44,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, FeesConfig, Event, Log}; + SystemConfig, GrandpaConfig, IndicesConfig, Event, Log}; use wabt; use primitives::map; @@ -126,8 +126,8 @@ mod tests { 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()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + twox_128(>::key()).to_vec() => vec![70u8; 16], + twox_128(>::key()).to_vec() => vec![0u8; 16] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -159,8 +159,8 @@ mod tests { 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()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + twox_128(>::key()).to_vec() => vec![70u8; 16], + twox_128(>::key()).to_vec() => vec![0u8; 16] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -192,8 +192,8 @@ mod tests { 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()).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] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -229,8 +229,8 @@ mod tests { 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()).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] ]); let r = executor().call::<_, NeverNativeValue, fn() -> _>( @@ -271,6 +271,8 @@ mod tests { ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], }), balances: Some(BalancesConfig { + transaction_base_fee: 1, + transaction_byte_fee: 0, balances: vec![ (alice(), 111), (bob(), 100), @@ -321,10 +323,6 @@ mod tests { grandpa: Some(GrandpaConfig { authorities: vec![], }), - fees: Some(FeesConfig { - transaction_base_fee: 1, - transaction_byte_fee: 0, - }), }.build_storage().unwrap().0) } @@ -524,10 +522,6 @@ mod tests { phase: Phase::Finalization, event: Event::treasury(treasury::RawEvent::Rollover(0)) }, - EventRecord { - phase: Phase::Finalization, - event: Event::fees(fees::RawEvent::Charged(1, 1)) - } ]); }); @@ -604,14 +598,6 @@ mod tests { 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)) - } ]); }); } @@ -832,8 +818,8 @@ mod tests { 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()).to_vec() => vec![70u8; 16], - twox_128(>::key()).to_vec() => vec![0u8; 16] + 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))); @@ -854,8 +840,8 @@ mod tests { 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()).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] ]); let r = WasmExecutor::new().call(&mut t, 8, COMPACT_CODE, "Core_initialise_block", &vec![].and(&from_block_number(1u64))); diff --git a/substrate/node/primitives/src/lib.rs b/substrate/node/primitives/src/lib.rs index 8c23e5d32b..0d8906c47d 100644 --- a/substrate/node/primitives/src/lib.rs +++ b/substrate/node/primitives/src/lib.rs @@ -22,14 +22,14 @@ #![cfg_attr(not(feature = "std"), feature(alloc))] use runtime_primitives::{ - generic, traits::{Verify, BlakeTwo256}, OpaqueExtrinsic + generic, traits::{Verify, BlakeTwo256}, OpaqueExtrinsic, AnySignature }; /// An index to a block. pub type BlockNumber = u64; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. -pub type Signature = primitives::sr25519::Signature; +pub type Signature = AnySignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index 6387fbe5b3..f72a525846 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -30,7 +30,6 @@ system = { package = "srml-system", path = "../../srml/system", default-features timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default-features = false } treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false } sudo = { package = "srml-sudo", path = "../../srml/sudo", 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 } @@ -60,7 +59,6 @@ std = [ "timestamp/std", "treasury/std", "sudo/std", - "fees/std", "version/std", "node-primitives/std", "serde", diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index a54d14dea8..6ef50ef61f 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -58,8 +58,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 37, - impl_version: 41, + spec_version: 38, + impl_version: 38, apis: RUNTIME_API_VERSIONS, }; @@ -102,11 +102,9 @@ impl balances::Trait for Runtime { type OnFreeBalanceZero = ((Staking, Contract), Session); type OnNewAccount = Indices; type Event = Event; -} - -impl fees::Trait for Runtime { - type Event = Event; - type TransferAsset = Balances; + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); } impl consensus::Trait for Runtime { @@ -133,6 +131,8 @@ impl staking::Trait for Runtime { type Currency = balances::Module; type OnRewardMinted = Treasury; type Event = Event; + type Slash = (); + type Reward = (); } impl democracy::Trait for Runtime { @@ -143,6 +143,8 @@ impl democracy::Trait for Runtime { impl council::Trait for Runtime { type Event = Event; + type BadPresentation = (); + type BadReaper = (); } impl council::voting::Trait for Runtime { @@ -160,6 +162,8 @@ impl treasury::Trait for Runtime { type ApproveOrigin = council_motions::EnsureMembers<_4>; type RejectOrigin = council_motions::EnsureMembers<_2>; type Event = Event; + type MintedForSpending = (); + type ProposalRejection = (); } impl contract::Trait for Runtime { @@ -168,6 +172,7 @@ impl contract::Trait for Runtime { type Gas = u64; type DetermineContractAddress = contract::SimpleAddressDeterminator; type ComputeDispatchFee = contract::DefaultDispatchFeeComputor; + type GasPayment = (); } impl sudo::Trait for Runtime { @@ -209,7 +214,6 @@ construct_runtime!( Treasury: treasury, Contract: contract::{Module, Call, Storage, Config, Event}, Sudo: sudo, - Fees: fees::{Module, Storage, Config, Event}, } ); @@ -228,7 +232,7 @@ pub type UncheckedExtrinsic = generic::UncheckedMortalCompactExtrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive, Fees, AllModules>; +pub type Executive = executive::Executive, Balances, AllModules>; impl_runtime_apis! { impl client_api::Core for Runtime { diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index 16b34a083b..570d1cb16c 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -801,7 +801,6 @@ dependencies = [ "srml-council 0.1.0", "srml-democracy 0.1.0", "srml-executive 0.1.0", - "srml-fees 0.1.0", "srml-finality-tracker 0.1.0", "srml-grandpa 0.1.0", "srml-indices 0.1.0", @@ -1295,6 +1294,23 @@ dependencies = [ "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "schnorrkel" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "merlin 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha3 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "0.3.3" @@ -1563,7 +1579,6 @@ 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", @@ -1616,22 +1631,6 @@ dependencies = [ "srml-system 0.1.0", ] -[[package]] -name = "srml-fees" -version = "0.1.0" -dependencies = [ - "hex-literal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec-derive 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (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-finality-tracker" version = "0.1.0" @@ -2010,7 +2009,7 @@ dependencies = [ "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)", + "schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2643,6 +2642,7 @@ dependencies = [ "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347" "checksum schnorrkel 0.0.0 (git+https://github.com/w3f/schnorrkel)" = "" +"checksum schnorrkel 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a700659388785588c75b197cecda0f23c7112a9281ef703e8ffc651061ce014c" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index e41cd76b9c..22b953faab 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/balances/src/lib.rs b/substrate/srml/balances/src/lib.rs index ff0493f35f..8eee7ad130 100644 --- a/substrate/srml/balances/src/lib.rs +++ b/substrate/srml/balances/src/lib.rs @@ -27,20 +27,36 @@ use rstd::prelude::*; use rstd::{cmp, result}; use parity_codec::{Codec, Encode, Decode}; -use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module, ensure}; +use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage, decl_module}; use srml_support::traits::{ - UpdateBalanceOutcome, Currency, OnFreeBalanceZero, TransferAsset, - WithdrawReason, WithdrawReasons, ArithmeticType, LockIdentifier, LockableCurrency + UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced, + WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, + Imbalance, SignedImbalance }; use srml_support::dispatch::Result; use primitives::traits::{ - Zero, SimpleArithmetic, As, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug + Zero, SimpleArithmetic, As, StaticLookup, Member, CheckedAdd, CheckedSub, + MaybeSerializeDebug, Saturating }; use system::{IsDeadAccount, OnNewAccount, ensure_signed}; mod mock; mod tests; +pub trait Subtrait: system::Trait { + /// The balance of an account. + type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As + As + MaybeSerializeDebug; + + /// A function which is invoked when the free-balance has fallen below the existential deposit and + /// has been reduced to zero. + /// + /// Gives a chance to clean up resources associated with the given account. + type OnFreeBalanceZero: OnFreeBalanceZero; + + /// Handler for when a new account is created. + type OnNewAccount: OnNewAccount; +} + pub trait Trait: system::Trait { /// The balance of an account. type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As + As + MaybeSerializeDebug; @@ -54,12 +70,24 @@ pub trait Trait: system::Trait { /// Handler for when a new account is created. type OnNewAccount: OnNewAccount; + /// Handler for the unbalanced reduction when taking transaction fees. + type TransactionPayment: OnUnbalanced>; + + /// Handler for the unbalanced reduction when taking fees associated with balance + /// transfer (which may also include account creation). + type TransferPayment: OnUnbalanced>; + + /// Handler for the unbalanced reduction when removing a dust account. + type DustRemoval: OnUnbalanced>; + /// The overarching event type. type Event: From> + Into<::Event>; } -impl ArithmeticType for Module { - type Type = ::Balance; +impl, I: Instance> Subtrait for T { + type Balance = T::Balance; + type OnFreeBalanceZero = T::OnFreeBalanceZero; + type OnNewAccount = T::OnNewAccount; } decl_event!( @@ -118,6 +146,10 @@ decl_storage! { pub TransferFee get(transfer_fee) config(): T::Balance; /// The fee required to create an account. At least as big as ReclaimRebate. pub CreationFee get(creation_fee) config(): T::Balance; + /// 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; /// Information regarding the vesting of a given account. pub Vesting get(vesting) build(|config: &GenesisConfig| { @@ -190,7 +222,7 @@ decl_module! { ) { let transactor = ensure_signed(origin)?; let dest = T::Lookup::lookup(dest)?; - Self::make_transfer(&transactor, &dest, value)?; + >::transfer(&transactor, &dest, value)?; } /// Set the balances of a given account. @@ -209,7 +241,9 @@ decl_module! { // For funding methods, see Currency trait impl, I: Instance> Module { - /// Get the amount that is currently being vested and cannot be transfered out of this account. + // PUBLIC IMMUTABLES + + /// Get the amount that is currently being vested and cannot be transferred out of this account. pub fn vesting_balance(who: &T::AccountId) -> T::Balance { if let Some(v) = Self::vesting(who) { Self::free_balance(who).min(v.locked_at(>::block_number())) @@ -218,11 +252,16 @@ impl, I: Instance> Module { } } + // PRIVATE MUTABLES + /// Set the free balance of an account to some new value. /// - /// Will enforce ExistentialDeposit law, anulling the account as needed. + /// Will enforce ExistentialDeposit law, annulling the account as needed. /// In that case it will return `AccountKilled`. - pub fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_reserved_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { if balance < Self::existential_deposit() { >::insert(who, balance); Self::on_reserved_too_low(who); @@ -234,13 +273,16 @@ impl, I: Instance> Module { } /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit - /// law anulling the account as needed. + /// law annulling the account as needed. /// /// Doesn't do any preparatory work for creating a new account, so should only be used when it /// is known that the account already exists. /// /// Returns if the account was successfully updated or update has led to killing of the account. - pub fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { + /// + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { // Commented out for no - but consider it instructive. // assert!(!Self::total_balance(who).is_zero()); if balance < Self::existential_deposit() { @@ -253,92 +295,35 @@ impl, I: Instance> Module { } } - /// Set the free balance on an account to some new value. - /// - /// Same as [`set_free_balance`], but will create a new account. - /// - /// Returns if the account was successfully updated or update has led to killing of the account. - /// - /// [`set_free_balance`]: #method.set_free_balance - pub fn set_free_balance_creating(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - let ed = >::existential_deposit(); - // If the balance is too low, then the account is reaped. - // NOTE: There are two balances for every account: `reserved_balance` and - // `free_balance`. This contract subsystem only cares about the latter: whenever - // the term "balance" is used *here* it should be assumed to mean "free balance" - // in the rest of the module. - // Free balance can never be less than ED. If that happens, it gets reduced to zero - // and the account information relevant to this subsystem is deleted (i.e. the - // account is reaped). - // NOTE: This is orthogonal to the `Bondage` value that an account has, a high - // value of which makes even the `free_balance` unspendable. - if balance < ed { - Self::set_free_balance(who, balance); - UpdateBalanceOutcome::AccountKilled - } else { - if !>::exists(who) { - Self::new_account(&who, balance); - } - Self::set_free_balance(who, balance); - - UpdateBalanceOutcome::Updated - } - } - - /// Transfer some liquid free balance to another staker. - pub fn make_transfer(transactor: &T::AccountId, dest: &T::AccountId, value: T::Balance) -> Result { - let from_balance = Self::free_balance(transactor); - let to_balance = Self::free_balance(dest); - let would_create = to_balance.is_zero(); - let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; - let liability = match value.checked_add(&fee) { - Some(l) => l, - None => return Err("got overflow after adding a fee to value"), - }; - - let new_from_balance = match from_balance.checked_sub(&liability) { - None => return Err("balance too low to send value"), - Some(b) => b, - }; - if would_create && value < Self::existential_deposit() { - return Err("value too low to create account"); - } - Self::ensure_account_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; - - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - let new_to_balance = match to_balance.checked_add(&value) { - Some(b) => b, - None => return Err("destination balance too high to receive value"), - }; - - if transactor != dest { - Self::set_free_balance(transactor, new_from_balance); - Self::decrease_total_stake_by(fee); - Self::set_free_balance_creating(dest, new_to_balance); - Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee)); - } - - Ok(()) - } - /// Register a new account (with existential balance). + /// + /// This just calls appropriate hooks. It doesn't (necessarily) make any state changes. fn new_account(who: &T::AccountId, balance: T::Balance) { T::OnNewAccount::on_new_account(&who); Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone())); } + /// Unregister an account. + /// + /// This just removes the nonce and leaves an event. fn reap_account(who: &T::AccountId) { >::remove(who); Self::deposit_event(RawEvent::ReapedAccount(who.clone())); } - /// Kill an account's free portion. + /// Account's free balance has dropped below existential deposit. Kill its + /// free side and the account completely if its reserved size is already dead. + /// + /// Will maintain total issuance. fn on_free_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::free_balance(who)); - >::remove(who); + let dust = >::take(who); >::remove(who); + // underflow should never happen, but if it does, there's not much we can do about it. + if !dust.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance(dust)); + } + T::OnFreeBalanceZero::on_free_balance_zero(who); if Self::reserved_balance(who).is_zero() { @@ -346,34 +331,204 @@ impl, I: Instance> Module { } } - /// Kill an account's reserved portion. + /// Account's reserved balance has dropped below existential deposit. Kill its + /// reserved side and the account completely if its free size is already dead. + /// + /// Will maintain total issuance. fn on_reserved_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::reserved_balance(who)); - >::remove(who); + let dust = >::take(who); + + // underflow should never happen, but it if does, there's nothing to be done here. + if !dust.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance(dust)); + } if Self::free_balance(who).is_zero() { Self::reap_account(who); } } +} - /// Increase TotalIssuance by Value. - pub fn increase_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_issuance().checked_add(&value) { - >::put(v); +/// Opaque, move-only struct with private fields that serves as a token denoting that +/// funds have been created without any equal and opposite accounting. +#[must_use] +pub struct PositiveImbalance, I: Instance=DefaultInstance>(T::Balance); + +/// Opaque, move-only struct with private fields that serves as a token denoting that +/// funds have been destroyed without any equal and opposite accounting. +#[must_use] +pub struct NegativeImbalance, I: Instance=DefaultInstance>(T::Balance); + +impl, I: Instance> Imbalance for PositiveImbalance { + type Opposite = NegativeImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) } } - /// Decrease TotalIssuance by Value. - pub fn decrease_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_issuance().checked_sub(&value) { - >::put(v); + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + (Self(first), Self(second)) + } + fn merge(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0) + } + fn offset(self, other: Self::Opposite) -> result::Result { + if self.0 >= other.0 { + Ok(Self(self.0 - other.0)) + } else { + Err(NegativeImbalance(other.0 - self.0)) } } + fn peek(&self) -> T::Balance { + self.0.clone() + } +} - /// Returns `Ok` iff the account is able to make a withdrawal of the given amount - /// for the given reason. - /// - /// `Err(...)` with the reason why not otherwise. - pub fn ensure_account_can_withdraw( +impl, I: Instance> Imbalance for NegativeImbalance { + type Opposite = PositiveImbalance; + + fn zero() -> Self { + Self(Zero::zero()) + } + fn drop_zero(self) -> result::Result<(), Self> { + if self.0.is_zero() { + Ok(()) + } else { + Err(self) + } + } + fn split(self, amount: T::Balance) -> (Self, Self) { + let first = self.0.min(amount); + let second = self.0 - first; + (Self(first), Self(second)) + } + fn merge(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + fn subsume(&mut self, other: Self) { + self.0 = self.0.saturating_add(other.0) + } + fn offset(self, other: Self::Opposite) -> result::Result { + if self.0 >= other.0 { + Ok(Self(self.0 - other.0)) + } else { + Err(PositiveImbalance(other.0 - self.0)) + } + } + fn peek(&self) -> T::Balance { + self.0.clone() + } +} + +// TODO: #2052 +// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by` +// using only the Subtrait (which defines only the types that are not dependent +// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a +// circular dependency with Trait having some types be dependent on PositiveImbalance +// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus +// its type declaration). +// This works as long as `increase_total_issuance_by` doesn't use the Imbalance +// types (basically for charging fees). +// This should eventually be refactored so that the three type items that do +// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// are placed in their own SRML module. +struct ElevatedTrait, I: Instance>(T, I); +impl, I: Instance> Clone for ElevatedTrait { + fn clone(&self) -> Self { unimplemented!() } +} +impl, I: Instance> PartialEq for ElevatedTrait { + fn eq(&self, _: &Self) -> bool { unimplemented!() } +} +impl, I: Instance> Eq for ElevatedTrait {} +impl, I: Instance> system::Trait for ElevatedTrait { + type Origin = T::Origin; + type Index = T::Index; + type BlockNumber = T::BlockNumber; + type Hash = T::Hash; + type Hashing = T::Hashing; + type Digest = T::Digest; + type AccountId = T::AccountId; + type Lookup = T::Lookup; + type Header = T::Header; + type Event = (); + type Log = T::Log; +} +impl, I: Instance> Trait for ElevatedTrait { + type Balance = T::Balance; + type OnFreeBalanceZero = T::OnFreeBalanceZero; + type OnNewAccount = T::OnNewAccount; + type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); +} + +impl, I: Instance> Drop for PositiveImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + , I>>::mutate(|v| *v = v.saturating_add(self.0)); + } +} + +impl, I: Instance> Drop for NegativeImbalance { + /// Basic drop handler will just square up the total issuance. + fn drop(&mut self) { + , I>>::mutate(|v| *v = v.saturating_sub(self.0)); + } +} + +impl, I: Instance> Currency for Module +where + T::Balance: MaybeSerializeDebug +{ + type Balance = T::Balance; + type PositiveImbalance = PositiveImbalance; + type NegativeImbalance = NegativeImbalance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::free_balance(who) + Self::reserved_balance(who) + } + + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + Self::free_balance(who) >= value + } + + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + Self::free_balance(who) + .checked_sub(&value) + .map_or(false, |new_balance| + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok() + ) + } + + fn total_issuance() -> Self::Balance { + >::get() + } + + fn minimum_balance() -> Self::Balance { + Self::existential_deposit() + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + >::get(who) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + >::get(who) + } + + fn ensure_can_withdraw( who: &T::AccountId, _amount: T::Balance, reason: WithdrawReason, @@ -397,69 +552,137 @@ impl, I: Instance> Module { Err("account liquidity restrictions prevent withdrawal") } } -} -impl, I: Instance> Currency for Module -where - T::Balance: MaybeSerializeDebug -{ - type Balance = T::Balance; + fn transfer(transactor: &T::AccountId, dest: &T::AccountId, value: Self::Balance) -> Result { + let from_balance = Self::free_balance(transactor); + let to_balance = Self::free_balance(dest); + let would_create = to_balance.is_zero(); + let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let liability = match value.checked_add(&fee) { + Some(l) => l, + None => return Err("got overflow after adding a fee to value"), + }; - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::free_balance(who) + Self::reserved_balance(who) + let new_from_balance = match from_balance.checked_sub(&liability) { + None => return Err("balance too low to send value"), + Some(b) => b, + }; + if would_create && value < Self::existential_deposit() { + return Err("value too low to create account"); + } + Self::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; + + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + let new_to_balance = match to_balance.checked_add(&value) { + Some(b) => b, + None => return Err("destination balance too high to receive value"), + }; + + if transactor != dest { + Self::set_free_balance(transactor, new_from_balance); + if !>::exists(dest) { + Self::new_account(dest, new_to_balance); + } + Self::set_free_balance(dest, new_to_balance); + T::TransferPayment::on_unbalanced(NegativeImbalance(fee)); + Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee)); + } + + Ok(()) } - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - Self::free_balance(who) >= value - } - - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - Self::free_balance(who) - .checked_sub(&value) - .map_or(false, |new_balance| - Self::ensure_account_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok() - ) - } - - fn total_issuance() -> Self::Balance { - >::get() - } - - fn minimum_balance() -> Self::Balance { - Self::existential_deposit() - } - - fn free_balance(who: &T::AccountId) -> Self::Balance { - >::get(who) - } - - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - >::get(who) - } - - fn slash(who: &T::AccountId, value: Self::Balance) -> Option { - let free_balance = Self::free_balance(who); - let free_slash = cmp::min(free_balance, value); - Self::set_free_balance(who, free_balance - free_slash); - Self::decrease_total_stake_by(free_slash); - if free_slash < value { - Self::slash_reserved(who, value - free_slash) + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reason: WithdrawReason, + liveness: ExistenceRequirement, + ) -> result::Result { + if let Some(new_balance) = Self::free_balance(who).checked_sub(&value) { + if liveness == ExistenceRequirement::KeepAlive && new_balance < Self::existential_deposit() { + return Err("payment would kill account") + } + Self::ensure_can_withdraw(who, value, reason, new_balance)?; + Self::set_free_balance(who, new_balance); + Ok(NegativeImbalance(value)) } else { - None + Err("too few free funds in account") } } - fn reward(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + fn slash( + who: &T::AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance) { + let free_balance = Self::free_balance(who); + let free_slash = cmp::min(free_balance, value); + Self::set_free_balance(who, free_balance - free_slash); + let remaining_slash = value - free_slash; + if !remaining_slash.is_zero() { + let reserved_balance = Self::reserved_balance(who); + let reserved_slash = cmp::min(reserved_balance, remaining_slash); + Self::set_reserved_balance(who, reserved_balance - reserved_slash); + (NegativeImbalance(free_slash + reserved_slash), remaining_slash - reserved_slash) + } else { + (NegativeImbalance(value), Zero::zero()) + } + } + + fn deposit_into_existing( + who: &T::AccountId, + value: Self::Balance + ) -> result::Result { if Self::total_balance(who).is_zero() { return Err("beneficiary account must pre-exist"); } Self::set_free_balance(who, Self::free_balance(who) + value); - Self::increase_total_stake_by(value); - Ok(()) + Ok(PositiveImbalance(value)) } - fn increase_free_balance_creating(who: &T::AccountId, value: Self::Balance) -> UpdateBalanceOutcome { - Self::set_free_balance_creating(who, Self::free_balance(who) + value) + fn deposit_creating( + who: &T::AccountId, + value: Self::Balance, + ) -> Self::PositiveImbalance { + let (imbalance, _) = Self::ensure_free_balance_is(who, Self::free_balance(who) + value); + if let SignedImbalance::Positive(p) = imbalance { + p + } else { + // Impossible, but be defensive. + Self::PositiveImbalance::zero() + } + } + + fn ensure_free_balance_is(who: &T::AccountId, balance: T::Balance) -> ( + SignedImbalance, + UpdateBalanceOutcome + ) { + let original = Self::free_balance(who); + let imbalance = if original <= balance { + SignedImbalance::Positive(PositiveImbalance(balance - original)) + } else { + SignedImbalance::Negative(NegativeImbalance(original - balance)) + }; + // If the balance is too low, then the account is reaped. + // NOTE: There are two balances for every account: `reserved_balance` and + // `free_balance`. This contract subsystem only cares about the latter: whenever + // the term "balance" is used *here* it should be assumed to mean "free balance" + // in the rest of the module. + // Free balance can never be less than ED. If that happens, it gets reduced to zero + // and the account information relevant to this subsystem is deleted (i.e. the + // account is reaped). + // NOTE: This is orthogonal to the `Bondage` value that an account has, a high + // value of which makes even the `free_balance` unspendable. + let outcome = if balance < >::existential_deposit() { + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::AccountKilled + } else { + if !>::exists(who) { + Self::new_account(&who, balance); + } + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::Updated + }; + (imbalance, outcome) } fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { @@ -468,41 +691,36 @@ where return Err("not enough free funds") } let new_balance = b - value; - Self::ensure_account_can_withdraw(who, value, WithdrawReason::Reserve, new_balance)?; + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance)?; Self::set_reserved_balance(who, Self::reserved_balance(who) + value); Self::set_free_balance(who, new_balance); Ok(()) } - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Option { + fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { let b = Self::reserved_balance(who); let actual = cmp::min(b, value); Self::set_free_balance(who, Self::free_balance(who) + actual); Self::set_reserved_balance(who, b - actual); - if actual == value { - None - } else { - Some(value - actual) - } + value - actual } - fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> Option { + fn slash_reserved( + who: &T::AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance) { let b = Self::reserved_balance(who); let slash = cmp::min(b, value); + // underflow should never happen, but it if does, there's nothing to be done here. Self::set_reserved_balance(who, b - slash); - Self::decrease_total_stake_by(slash); - if value == slash { - None - } else { - Some(value - slash) - } + (NegativeImbalance(slash), value - slash) } fn repatriate_reserved( slashed: &T::AccountId, beneficiary: &T::AccountId, - value: Self::Balance - ) -> result::Result, &'static str> { + value: Self::Balance, + ) -> result::Result { if Self::total_balance(beneficiary).is_zero() { return Err("beneficiary account must pre-exist"); } @@ -510,11 +728,7 @@ where let slash = cmp::min(b, value); Self::set_free_balance(beneficiary, Self::free_balance(beneficiary) + slash); Self::set_reserved_balance(slashed, b - slash); - if value == slash { - Ok(None) - } else { - Ok(Some(value - slash)) - } + Ok(value - slash) } } @@ -592,26 +806,17 @@ where } } -impl, I: Instance> 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 withdraw(who: &T::AccountId, value: T::Balance, reason: WithdrawReason) -> Result { - let b = Self::free_balance(who); - ensure!(b >= value, "account has too few funds"); - let new_balance = b - value; - Self::ensure_account_can_withdraw(who, value, reason, new_balance)?; - Self::set_free_balance(who, new_balance); - Self::decrease_total_stake_by(value); - Ok(()) - } - - fn deposit(who: &T::AccountId, value: T::Balance) -> Result { - Self::set_free_balance_creating(who, Self::free_balance(who) + value); - Self::increase_total_stake_by(value); +impl MakePayment for Module { + fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { + let encoded_len = >::sa(encoded_len as u64); + let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * encoded_len; + let imbalance = Self::withdraw( + transactor, + transaction_fee, + WithdrawReason::TransactionPayment, + ExistenceRequirement::KeepAlive + )?; + T::TransactionPayment::on_unbalanced(imbalance); Ok(()) } } @@ -624,3 +829,4 @@ where Self::total_balance(who).is_zero() } } + diff --git a/substrate/srml/balances/src/mock.rs b/substrate/srml/balances/src/mock.rs index 55403a12aa..adec340bf4 100644 --- a/substrate/srml/balances/src/mock.rs +++ b/substrate/srml/balances/src/mock.rs @@ -50,9 +50,14 @@ impl Trait for Runtime { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); } pub struct ExtBuilder { + transaction_base_fee: u64, + transaction_byte_fee: u64, existential_deposit: u64, transfer_fee: u64, creation_fee: u64, @@ -62,6 +67,8 @@ pub struct ExtBuilder { impl Default for ExtBuilder { fn default() -> Self { Self { + transaction_base_fee: 0, + transaction_byte_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, @@ -84,8 +91,16 @@ impl ExtBuilder { self.creation_fee = creation_fee; self } + pub fn transaction_fees(mut self, base_fee: u64, byte_fee: u64) -> Self { + self.transaction_base_fee = base_fee; + self.transaction_byte_fee = byte_fee; + self + } pub fn monied(mut self, monied: bool) -> Self { self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } self } pub fn vesting(mut self, vesting: bool) -> Self { @@ -94,16 +109,13 @@ impl ExtBuilder { } pub fn build(self) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; - let balance_factor = if self.existential_deposit > 0 { - 256 - } else { - 1 - }; t.extend(GenesisConfig:: { + transaction_base_fee: self.transaction_base_fee, + transaction_byte_fee: self.transaction_byte_fee, balances: if self.monied { - vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] + vec![(1, 10 * self.existential_deposit), (2, 20 * self.existential_deposit), (3, 30 * self.existential_deposit), (4, 40 * self.existential_deposit)] } else { - vec![(10, balance_factor), (20, balance_factor)] + vec![] }, existential_deposit: self.existential_deposit, transfer_fee: self.transfer_fee, diff --git a/substrate/srml/balances/src/tests.rs b/substrate/srml/balances/src/tests.rs index 7fded7068a..802f184aca 100644 --- a/substrate/srml/balances/src/tests.rs +++ b/substrate/srml/balances/src/tests.rs @@ -23,7 +23,7 @@ use mock::{Balances, ExtBuilder, Runtime, System}; use runtime_io::with_externalities; use srml_support::{ assert_noop, assert_ok, assert_err, - traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, Currency, TransferAsset} + traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, Currency, MakePayment} }; const ID_1: LockIdentifier = *b"1 "; @@ -32,123 +32,123 @@ const ID_3: LockIdentifier = *b"3 "; #[test] fn basic_locking_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { assert_eq!(Balances::free_balance(&1), 10); Balances::set_lock(ID_1, &1, 9, u64::max_value(), WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 5), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 5), "account liquidity restrictions prevent withdrawal"); }); } #[test] fn partial_locking_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn lock_removal_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); Balances::remove_lock(ID_1, &1); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn lock_replacement_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, u64::max_value(), u64::max_value(), WithdrawReasons::all()); Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn double_locking_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); Balances::set_lock(ID_2, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn combination_locking_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, u64::max_value(), 0, WithdrawReasons::none()); Balances::set_lock(ID_2, &1, 0, u64::max_value(), WithdrawReasons::none()); Balances::set_lock(ID_3, &1, 0, 0, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn lock_value_extension_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 5, u64::max_value(), WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); Balances::extend_lock(ID_1, &1, 2, u64::max_value(), WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); Balances::extend_lock(ID_1, &1, 8, u64::max_value(), WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 3), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 3), "account liquidity restrictions prevent withdrawal"); }); } #[test] fn lock_reasons_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).transaction_fees(0, 1).build(), || { Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into()); - assert_noop!(>::transfer(&1, &2, 1), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 1), "account liquidity restrictions prevent withdrawal"); assert_ok!(>::reserve(&1, 1)); - assert_ok!(>::withdraw(&1, 1, WithdrawReason::TransactionPayment)); + assert_ok!(>::make_payment(&1, 1)); Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); assert_noop!(>::reserve(&1, 1), "account liquidity restrictions prevent withdrawal"); - assert_ok!(>::withdraw(&1, 1, WithdrawReason::TransactionPayment)); + assert_ok!(>::make_payment(&1, 1)); Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into()); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); assert_ok!(>::reserve(&1, 1)); - assert_noop!(>::withdraw(&1, 1, WithdrawReason::TransactionPayment), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::make_payment(&1, 1), "account liquidity restrictions prevent withdrawal"); }); } #[test] fn lock_block_number_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 1), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 1), "account liquidity restrictions prevent withdrawal"); System::set_block_number(2); - assert_ok!(>::transfer(&1, &2, 1)); + assert_ok!(>::transfer(&1, &2, 1)); }); } #[test] fn lock_block_number_extension_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 10, 2, WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); Balances::extend_lock(ID_1, &1, 10, 1, WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); System::set_block_number(2); Balances::extend_lock(ID_1, &1, 10, 8, WithdrawReasons::all()); - assert_noop!(>::transfer(&1, &2, 3), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 3), "account liquidity restrictions prevent withdrawal"); }); } #[test] fn lock_reasons_extension_should_work() { - with_externalities(&mut ExtBuilder::default().monied(true).build(), || { + with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).build(), || { Balances::set_lock(ID_1, &1, 10, 10, WithdrawReason::Transfer.into()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReasons::none()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); Balances::extend_lock(ID_1, &1, 10, 10, WithdrawReason::Reserve.into()); - assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); + assert_noop!(>::transfer(&1, &2, 6), "account liquidity restrictions prevent withdrawal"); }); } @@ -168,7 +168,7 @@ fn default_indexing_on_new_accounts_should_not_work2() { "value too low to create account" ); assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - assert_eq!(Balances::free_balance(&1), 256 * 10); + assert_eq!(Balances::free_balance(&1), 100); }, ); } @@ -196,7 +196,7 @@ fn reserved_balance_should_prevent_reclaim_count() { assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); assert_eq!(Balances::is_dead_account(&5), false); - assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed + assert!(Balances::slash(&2, 256 * 18 + 2).1.is_zero()); // account 2 gets slashed assert_eq!(Balances::total_balance(&2), 0); // "reserve" account reduced to 255 (below ED) so account deleted assert_eq!(System::account_nonce(&2), 0); // nonce zero assert_eq!(Balances::is_dead_account(&2), true); @@ -213,7 +213,7 @@ fn reserved_balance_should_prevent_reclaim_count() { fn reward_should_work() { with_externalities(&mut ExtBuilder::default().monied(true).build(), || { assert_eq!(Balances::total_balance(&1), 10); - assert_ok!(Balances::reward(&1, 10)); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); assert_eq!(Balances::total_balance(&1), 20); assert_eq!(>::get(), 110); }); @@ -223,17 +223,17 @@ fn reward_should_work() { fn dust_account_removal_should_work() { with_externalities( &mut ExtBuilder::default() - .existential_deposit(256 * 10) + .existential_deposit(100) .monied(true) .build(), || { System::inc_account_nonce(&2); assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 256 * 20); + assert_eq!(Balances::total_balance(&2), 2000); - assert_ok!(Balances::transfer(Some(2).into(), 5, 256 * 10 + 1)); // index 1 (account 2) becomes zombie + assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); // index 1 (account 2) becomes zombie assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 256 * 10 + 1); + assert_eq!(Balances::total_balance(&5), 1901); assert_eq!(System::account_nonce(&2), 0); }, ); @@ -243,17 +243,17 @@ fn dust_account_removal_should_work() { fn dust_account_removal_should_work2() { with_externalities( &mut ExtBuilder::default() - .existential_deposit(256 * 10) + .existential_deposit(100) .creation_fee(50) .monied(true) .build(), || { System::inc_account_nonce(&2); assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 256 * 20); - assert_ok!(Balances::transfer(Some(2).into(), 5, 256 * 10)); // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) + assert_eq!(Balances::total_balance(&2), 2000); + assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 256 * 10); + assert_eq!(Balances::total_balance(&5), 1851); assert_eq!(System::account_nonce(&2), 0); }, ); @@ -262,7 +262,7 @@ fn dust_account_removal_should_work2() { #[test] fn balance_works() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 42); + let _ = Balances::deposit_creating(&1, 42); assert_eq!(Balances::free_balance(&1), 42); assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!(Balances::total_balance(&1), 42); @@ -275,8 +275,7 @@ fn balance_works() { #[test] fn balance_transfer_works() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); assert_eq!(Balances::total_balance(&1), 42); assert_eq!(Balances::total_balance(&2), 69); @@ -286,7 +285,7 @@ fn balance_transfer_works() { #[test] fn reserving_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); + let _ = Balances::deposit_creating(&1, 111); assert_eq!(Balances::total_balance(&1), 111); assert_eq!(Balances::free_balance(&1), 111); @@ -303,7 +302,7 @@ fn reserving_balance_should_work() { #[test] fn balance_transfer_when_reserved_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); assert_noop!(Balances::transfer(Some(1).into(), 2, 69), "balance too low to send value"); }); @@ -312,7 +311,7 @@ fn balance_transfer_when_reserved_should_not_work() { #[test] fn deducting_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); assert_eq!(Balances::free_balance(&1), 42); }); @@ -321,7 +320,7 @@ fn deducting_balance_should_work() { #[test] fn refunding_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 42); + let _ = Balances::deposit_creating(&1, 42); Balances::set_reserved_balance(&1, 69); Balances::unreserve(&1, 69); assert_eq!(Balances::free_balance(&1), 111); @@ -332,33 +331,31 @@ fn refunding_balance_should_work() { #[test] fn slashing_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 69)); - assert!(Balances::slash(&1, 69).is_none()); + assert!(Balances::slash(&1, 69).1.is_zero()); assert_eq!(Balances::free_balance(&1), 0); assert_eq!(Balances::reserved_balance(&1), 42); - assert_eq!(>::get(), 44); + assert_eq!(>::get(), 42); }); } #[test] fn slashing_incomplete_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 42); - Balances::increase_total_stake_by(42); + let _ = Balances::deposit_creating(&1, 42); assert_ok!(Balances::reserve(&1, 21)); - assert!(Balances::slash(&1, 69).is_some()); + assert_eq!(Balances::slash(&1, 69).1, 27); assert_eq!(Balances::free_balance(&1), 0); assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 2); + assert_eq!(>::get(), 0); }); } #[test] fn unreserving_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 111)); Balances::unreserve(&1, 42); assert_eq!(Balances::reserved_balance(&1), 69); @@ -369,36 +366,34 @@ fn unreserving_balance_should_work() { #[test] fn slashing_reserved_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 111)); - assert!(Balances::slash_reserved(&1, 42).is_none()); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); assert_eq!(Balances::reserved_balance(&1), 69); assert_eq!(Balances::free_balance(&1), 0); - assert_eq!(>::get(), 71); + assert_eq!(>::get(), 69); }); } #[test] fn slashing_incomplete_reserved_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); - Balances::increase_total_stake_by(111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 42)); - assert!(Balances::slash_reserved(&1, 69).is_some()); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); assert_eq!(Balances::free_balance(&1), 69); assert_eq!(Balances::reserved_balance(&1), 0); - assert_eq!(>::get(), 71); + assert_eq!(>::get(), 69); }); } #[test] fn transferring_reserved_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 110); - Balances::set_free_balance(&2, 1); + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41), None); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41), 0); assert_eq!(Balances::reserved_balance(&1), 69); assert_eq!(Balances::free_balance(&1), 0); assert_eq!(Balances::reserved_balance(&2), 0); @@ -409,7 +404,7 @@ fn transferring_reserved_balance_should_work() { #[test] fn transferring_reserved_balance_to_nonexistent_should_fail() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 111); + let _ = Balances::deposit_creating(&1, 111); assert_ok!(Balances::reserve(&1, 111)); assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist"); }); @@ -418,10 +413,10 @@ fn transferring_reserved_balance_to_nonexistent_should_fail() { #[test] fn transferring_incomplete_reserved_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&1, 110); - Balances::set_free_balance(&2, 1); + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); assert_ok!(Balances::reserve(&1, 41)); - assert!(Balances::repatriate_reserved(&1, &2, 69).unwrap().is_some()); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69), 28); assert_eq!(Balances::reserved_balance(&1), 0); assert_eq!(Balances::free_balance(&1), 69); assert_eq!(Balances::reserved_balance(&2), 0); @@ -450,27 +445,27 @@ fn account_removal_on_free_too_low() { with_externalities( &mut ExtBuilder::default().existential_deposit(100).build(), || { - // Setup two accounts with free balance above the exsistential threshold. - { - Balances::set_free_balance(&1, 110); - Balances::increase_total_stake_by(110); + assert_eq!(>::get(), 0); - Balances::set_free_balance(&2, 110); - Balances::increase_total_stake_by(110); + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); - assert_eq!(>::get(), 732); - } + assert_eq!(Balances::free_balance(&1), 110); + assert_eq!(Balances::free_balance(&2), 110); + assert_eq!(>::get(), 220); // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the exsistential threshold. + // the balance of account 1 will be below the existential threshold. // This should lead to the removal of all balance of this account. assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); // Verify free balance removal of account 1. assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::free_balance(&2), 130); // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(>::get(), 642); + assert_eq!(>::get(), 130); }, ); } @@ -495,7 +490,7 @@ fn transfer_overflow_isnt_exploitable() { fn check_vesting_status() { with_externalities( &mut ExtBuilder::default() - .existential_deposit(10) + .existential_deposit(256) .monied(true) .vesting(true) .build(), @@ -545,10 +540,10 @@ fn unvested_balance_should_not_transfer() { || { assert_eq!(System::block_number(), 1); let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - assert_eq!(Balances::vesting_balance(&1), user1_free_balance - 256); // Account 1 has only 256 units vested at block 1 + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has only 10 units vested at block 1 assert_noop!( - Balances::transfer(Some(1).into(), 2, 256 * 2), + Balances::transfer(Some(1).into(), 2, 11), "vesting balance too high to send value" ); // Account 1 cannot send more than vested amount } @@ -564,13 +559,11 @@ fn vested_balance_should_transfer() { .vesting(true) .build(), || { - System::set_block_number(5); - assert_eq!(System::block_number(), 5); + assert_eq!(System::block_number(), 1); let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 256 * 10); // Account 1 has free balance - - assert_eq!(Balances::vesting_balance(&1), user1_free_balance - 256 * 5); // Account 1 has 256 * 5 units vested at block 5 - assert_ok!(Balances::transfer(Some(1).into(), 2, 256 * 2)); // Account 1 can now send vested value + assert_eq!(user1_free_balance, 100); // Account 1 has free balance + assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has only 10 units vested at block 1 + assert_ok!(Balances::transfer(Some(1).into(), 2, 10)); } ); } @@ -585,12 +578,12 @@ fn extra_balance_should_transfer() { .build(), || { assert_eq!(System::block_number(), 1); - assert_ok!(Balances::transfer(Some(3).into(), 1, 256 * 10)); + assert_ok!(Balances::transfer(Some(3).into(), 1, 100)); let user1_free_balance = Balances::free_balance(&1); - assert_eq!(user1_free_balance, 256 * 20); // Account 1 has 2560 more free balance than normal + assert_eq!(user1_free_balance, 200); // Account 1 has 100 more free balance than normal - assert_eq!(Balances::vesting_balance(&1), 256 * 10 - 256); // Account 1 has 256 units vested at block 1 - assert_ok!(Balances::transfer(Some(1).into(), 2, 256 * 5)); // Account 1 can send extra units gained + assert_eq!(Balances::vesting_balance(&1), 90); // Account 1 has 90 units vested at block 1 + assert_ok!(Balances::transfer(Some(1).into(), 2, 105)); // Account 1 can send extra units gained } ); } diff --git a/substrate/srml/contract/Cargo.toml b/substrate/srml/contract/Cargo.toml index 0d373978b0..cb4ad0ffc1 100644 --- a/substrate/srml/contract/Cargo.toml +++ b/substrate/srml/contract/Cargo.toml @@ -19,7 +19,6 @@ 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" @@ -44,5 +43,4 @@ std = [ "timestamp/std", "parity-wasm/std", "pwasm-utils/std", - "fees/std", ] diff --git a/substrate/srml/contract/src/account_db.rs b/substrate/srml/contract/src/account_db.rs index 52fd80a8a2..2fcddd5d79 100644 --- a/substrate/srml/contract/src/account_db.rs +++ b/substrate/srml/contract/src/account_db.rs @@ -21,7 +21,9 @@ use {balances, system}; use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; -use srml_support::{StorageMap, StorageDoubleMap, traits::UpdateBalanceOutcome}; +use runtime_primitives::traits::Zero; +use srml_support::{StorageMap, StorageDoubleMap, traits::{UpdateBalanceOutcome, + SignedImbalance, Currency, Imbalance}}; pub struct ChangeEntry { balance: Option, @@ -63,11 +65,12 @@ impl AccountDb for DirectAccountDb { balances::Module::::free_balance(account) } fn commit(&mut self, s: ChangeSet) { + let mut total_imbalance = SignedImbalance::zero(); for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance { - if let UpdateBalanceOutcome::AccountKilled = - balances::Module::::set_free_balance_creating(&address, balance) - { + let (imbalance, outcome) = balances::Module::::ensure_free_balance_is(&address, balance); + total_imbalance = total_imbalance.merge(imbalance); + if let UpdateBalanceOutcome::AccountKilled = outcome { // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback // which will make removal of CodeHashOf and StorageOf for this account. // In order to avoid writing over the deleted properties we `continue` here. @@ -89,6 +92,16 @@ impl AccountDb for DirectAccountDb { } } } + + match total_imbalance { + // If we've detected a positive imbalance as a result of our contract-level machinations + // then it's indicative of a buggy contracts system. + // Panicking is far from ideal as it opens up a DoS attack on block validators, however + // it's a less bad option than allowing arbitrary value to be created. + SignedImbalance::Positive(ref p) if !p.peek().is_zero() => + panic!("contract subsystem resulting in positive imbalance!"), + _ => {} + } } } diff --git a/substrate/srml/contract/src/exec.rs b/substrate/srml/contract/src/exec.rs index 0dbe8b3260..8650839bf1 100644 --- a/substrate/srml/contract/src/exec.rs +++ b/substrate/srml/contract/src/exec.rs @@ -20,7 +20,7 @@ use crate::gas::{GasMeter, Token, approx_gas_for_balance}; use rstd::prelude::*; use runtime_primitives::traits::{CheckedAdd, CheckedSub, Zero}; -use srml_support::traits::WithdrawReason; +use srml_support::traits::{WithdrawReason, Currency}; use timestamp; pub type BalanceOf = ::Balance; @@ -520,7 +520,7 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( if would_create && value < ctx.config.existential_deposit { return Err("value too low to create account"); } - >::ensure_account_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; + >::ensure_can_withdraw(transactor, value, WithdrawReason::Transfer, new_from_balance)?; let new_to_balance = match to_balance.checked_add(&value) { Some(b) => b, diff --git a/substrate/srml/contract/src/gas.rs b/substrate/srml/contract/src/gas.rs index fb96ff66e5..205a422871 100644 --- a/substrate/srml/contract/src/gas.rs +++ b/substrate/srml/contract/src/gas.rs @@ -18,7 +18,7 @@ use crate::{GasSpent, Module, Trait}; use balances; use runtime_primitives::BLOCK_FULL; use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero}; -use srml_support::StorageValue; +use srml_support::{StorageValue, traits::{OnUnbalanced, ExistenceRequirement, WithdrawReason, Currency, Imbalance}}; #[cfg(test)] use std::{any::Any, fmt::Debug}; @@ -202,7 +202,7 @@ impl GasMeter { pub fn buy_gas( transactor: &T::AccountId, gas_limit: T::Gas, -) -> Result, &'static str> { +) -> Result<(GasMeter, balances::NegativeImbalance), &'static str> { // Check if the specified amount of gas is available in the current block. // This cannot underflow since `gas_spent` is never greater than `block_gas_limit`. let gas_available = >::block_gas_limit() - >::gas_spent(); @@ -213,40 +213,47 @@ pub fn buy_gas( // Buy the specified amount of gas. let gas_price = >::gas_price(); - let b = >::free_balance(transactor); let cost = >::as_(gas_limit.clone()) .checked_mul(&gas_price) .ok_or("overflow multiplying gas limit by price")?; - let new_balance = b.checked_sub(&cost); - if new_balance < Some(>::existential_deposit()) { - return Err("not enough funds for transaction fee"); - } + let imbalance = >::withdraw( + transactor, + cost, + WithdrawReason::Fee, + ExistenceRequirement::KeepAlive + )?; - >::set_free_balance(transactor, b - cost); - >::decrease_total_stake_by(cost); - Ok(GasMeter { + Ok((GasMeter { limit: gas_limit, gas_left: gas_limit, gas_price, + #[cfg(test)] tokens: Vec::new(), - }) + }, imbalance)) } /// Refund the unused gas. -pub fn refund_unused_gas(transactor: &T::AccountId, gas_meter: GasMeter) { +pub fn refund_unused_gas( + transactor: &T::AccountId, + gas_meter: GasMeter, + imbalance: balances::NegativeImbalance, +) { + let gas_spent = gas_meter.spent(); + let gas_left = gas_meter.gas_left(); + // Increase total spent gas. // This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which // also has T::Gas type. - let gas_spent = >::gas_spent() + gas_meter.spent(); - >::put(gas_spent); + >::mutate(|block_gas_spent| *block_gas_spent += gas_spent); // Refund gas left by the price it was bought. - let b = >::free_balance(transactor); - let refund = >::as_(gas_meter.gas_left) * gas_meter.gas_price; - >::set_free_balance(transactor, b + refund); - >::increase_total_stake_by(refund); + let refund = >::as_(gas_left) * gas_meter.gas_price; + let refund_imbalance = >::deposit_creating(transactor, refund); + if let Ok(imbalance) = imbalance.offset(refund_imbalance) { + T::GasPayment::on_unbalanced(imbalance); + } } /// A little handy utility for converting a value in balance units into approximitate value in gas units diff --git a/substrate/srml/contract/src/lib.rs b/substrate/srml/contract/src/lib.rs index 3f6cbde88f..1a703a5321 100644 --- a/substrate/srml/contract/src/lib.rs +++ b/substrate/srml/contract/src/lib.rs @@ -74,11 +74,10 @@ use parity_codec::{Codec, Encode, Decode}; use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup}; use srml_support::dispatch::{Result, Dispatchable}; use srml_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap, decl_module, decl_event, decl_storage}; -use srml_support::traits::OnFreeBalanceZero; +use srml_support::traits::{OnFreeBalanceZero, OnUnbalanced}; use system::{ensure_signed, RawOrigin}; use runtime_io::{blake2_256, twox_128}; use timestamp; -use fees; pub type CodeHash = ::Hash; @@ -92,7 +91,7 @@ pub trait ComputeDispatchFee { fn compute_dispatch_fee(call: &Call) -> Balance; } -pub trait Trait: fees::Trait + balances::Trait + timestamp::Trait { +pub trait Trait: balances::Trait + timestamp::Trait { /// The outer call dispatch type. type Call: Parameter + Dispatchable::Origin>; @@ -110,6 +109,9 @@ pub trait Trait: fees::Trait + balances::Trait + timestamp::Trait { /// It is recommended (though not required) for this function to return a fee that would be taken /// by executive module for regular dispatch. type ComputeDispatchFee: ComputeDispatchFee::Balance>; + + /// Handler for the unbalanced reduction when making a gas payment. + type GasPayment: OnUnbalanced>; } /// Simple contract address determintator. @@ -136,14 +138,14 @@ where } /// The default dispatch fee computor computes the fee in the same way that -/// implementation of `ChargeBytesFee` for fees module does. +/// implementation of `MakePayment` for balances module does. pub struct DefaultDispatchFeeComputor(PhantomData); impl ComputeDispatchFee for DefaultDispatchFeeComputor { fn compute_dispatch_fee(call: &T::Call) -> T::Balance { let encoded_len = call.using_encoded(|encoded| encoded.len()); - let base_fee = >::transaction_base_fee(); - let byte_fee = >::transaction_byte_fee(); - >::sa(base_fee.as_() + byte_fee.as_() * encoded_len as u64) + let base_fee = >::transaction_base_fee(); + let byte_fee = >::transaction_byte_fee(); + base_fee + byte_fee * >::sa(encoded_len as u64) } } @@ -175,14 +177,14 @@ decl_module! { let origin = ensure_signed(origin)?; let schedule = >::current_schedule(); - let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + let (mut gas_meter, imbalance) = gas::buy_gas::(&origin, gas_limit)?; let result = wasm::save_code::(code, &mut gas_meter, &schedule); if let Ok(code_hash) = result { Self::deposit_event(RawEvent::CodeStored(code_hash)); } - gas::refund_unused_gas::(&origin, gas_meter); + gas::refund_unused_gas::(&origin, gas_meter, imbalance); result.map(|_| ()) } @@ -202,7 +204,7 @@ decl_module! { // // NOTE: it is very important to avoid any state changes before // paying for the gas. - let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + let (mut gas_meter, imbalance) = gas::buy_gas::(&origin, gas_limit)?; let cfg = Config::preload(); let vm = crate::wasm::WasmVm::new(&cfg.schedule); @@ -212,7 +214,7 @@ decl_module! { let result = ctx.call(dest, value, &mut gas_meter, &data, exec::EmptyOutputBuf::new()); if let Ok(_) = result { - // Commit all changes that made it thus far into the persistant storage. + // Commit all changes that made it thus far into the persistent storage. account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); // Then deposit all events produced. @@ -223,7 +225,7 @@ decl_module! { // // NOTE: this should go after the commit to the storage, since the storage changes // can alter the balance of the caller. - gas::refund_unused_gas::(&origin, gas_meter); + gas::refund_unused_gas::(&origin, gas_meter, imbalance); // Dispatch every recorded call with an appropriate origin. ctx.calls.into_iter().for_each(|(who, call)| { @@ -234,7 +236,7 @@ decl_module! { result.map(|_| ()) } - /// Create a new contract, optionally transfering some balance to the created account. + /// Create a new contract, optionally transferring some balance to the created account. /// /// Creation is executed as follows: /// @@ -256,7 +258,7 @@ decl_module! { // // NOTE: it is very important to avoid any state changes before // paying for the gas. - let mut gas_meter = gas::buy_gas::(&origin, gas_limit)?; + let (mut gas_meter, imbalance) = gas::buy_gas::(&origin, gas_limit)?; let cfg = Config::preload(); let vm = crate::wasm::WasmVm::new(&cfg.schedule); @@ -276,7 +278,7 @@ decl_module! { // // NOTE: this should go after the commit to the storage, since the storage changes // can alter the balance of the caller. - gas::refund_unused_gas::(&origin, gas_meter); + gas::refund_unused_gas::(&origin, gas_meter, imbalance); // Dispatch every recorded call with an appropriate origin. ctx.calls.into_iter().for_each(|(who, call)| { diff --git a/substrate/srml/contract/src/tests.rs b/substrate/srml/contract/src/tests.rs index cf280c0f00..a0cefa664e 100644 --- a/substrate/srml/contract/src/tests.rs +++ b/substrate/srml/contract/src/tests.rs @@ -24,10 +24,10 @@ use runtime_primitives::testing::{Digest, DigestItem, H256, Header, UintAuthorit use runtime_primitives::traits::{BlakeTwo256, IdentityLookup}; use runtime_primitives::BuildStorage; use runtime_io; -use srml_support::{StorageMap, StorageDoubleMap, assert_ok, impl_outer_event, impl_outer_dispatch, impl_outer_origin}; -use substrate_primitives::{Blake2Hasher}; +use srml_support::{StorageMap, StorageDoubleMap, assert_ok, impl_outer_event, impl_outer_dispatch, + impl_outer_origin, traits::Currency}; +use substrate_primitives::Blake2Hasher; use system::{self, Phase, EventRecord}; -use fees; use {wabt, balances, consensus}; use hex_literal::*; use assert_matches::assert_matches; @@ -45,7 +45,7 @@ mod contract { } impl_outer_event! { pub enum MetaEvent for Test { - balances, contract, fees, + balances, contract, } } impl_outer_origin! { @@ -78,6 +78,9 @@ impl balances::Trait for Test { type OnFreeBalanceZero = Contract; type OnNewAccount = (); type Event = MetaEvent; + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); } impl timestamp::Trait for Test { type Moment = u64; @@ -88,16 +91,13 @@ impl consensus::Trait for Test { type SessionKey = UintAuthorityId; type InherentOfflineReport = (); } -impl fees::Trait for Test { - type Event = MetaEvent; - type TransferAsset = Balances; -} impl Trait for Test { type Call = Call; type Gas = u64; type DetermineContractAddress = DummyContractAddressFor; type Event = MetaEvent; type ComputeDispatchFee = DummyComputeDispatchFee; + type GasPayment = (); } type Balances = balances::Module; @@ -168,6 +168,8 @@ impl ExtBuilder { .0; t.extend( balances::GenesisConfig:: { + transaction_base_fee: 0, + transaction_byte_fee: 0, balances: vec![], existential_deposit: self.existential_deposit, transfer_fee: self.transfer_fee, @@ -199,8 +201,7 @@ impl ExtBuilder { #[test] fn refunds_unused_gas() { with_externalities(&mut ExtBuilder::default().build(), || { - Balances::set_free_balance(&0, 100_000_000); - Balances::increase_total_stake_by(100_000_000); + Balances::deposit_creating(&0, 100_000_000); assert_ok!(Contract::call( Origin::signed(0), @@ -221,13 +222,11 @@ fn account_removal_removes_storage() { || { // Setup two accounts with free balance above than exsistential threshold. { - Balances::set_free_balance(&1, 110); - Balances::increase_total_stake_by(110); + Balances::deposit_creating(&1, 110); >::insert(&1, &b"foo".to_vec(), b"1".to_vec()); >::insert(&1, &b"bar".to_vec(), b"2".to_vec()); - Balances::set_free_balance(&2, 110); - Balances::increase_total_stake_by(110); + Balances::deposit_creating(&2, 110); >::insert(&2, &b"hello".to_vec(), b"3".to_vec()); >::insert(&2, &b"world".to_vec(), b"4".to_vec()); } @@ -288,8 +287,7 @@ fn instantiate_and_call() { with_externalities( &mut ExtBuilder::default().existential_deposit(100).build(), || { - Balances::set_free_balance(&ALICE, 1_000_000); - Balances::increase_total_stake_by(1_000_000); + Balances::deposit_creating(&ALICE, 1_000_000); assert_ok!(Contract::put_code( Origin::signed(ALICE), @@ -306,6 +304,10 @@ fn instantiate_and_call() { )); assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_RETURN_FROM_START_FN.into())), @@ -359,8 +361,7 @@ fn dispatch_call() { with_externalities( &mut ExtBuilder::default().existential_deposit(50).build(), || { - Balances::set_free_balance(&ALICE, 1_000_000); - Balances::increase_total_stake_by(1_000_000); + Balances::deposit_creating(&ALICE, 1_000_000); assert_ok!(Contract::put_code( Origin::signed(ALICE), @@ -371,6 +372,10 @@ fn dispatch_call() { // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), @@ -394,6 +399,10 @@ fn dispatch_call() { )); assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), diff --git a/substrate/srml/council/src/lib.rs b/substrate/srml/council/src/lib.rs index de64b8c55c..458e6ffd38 100644 --- a/substrate/srml/council/src/lib.rs +++ b/substrate/srml/council/src/lib.rs @@ -77,6 +77,9 @@ mod tests { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = Event; + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); } impl democracy::Trait for Test { type Currency = balances::Module; @@ -85,6 +88,8 @@ mod tests { } impl seats::Trait for Test { type Event = Event; + type BadPresentation = (); + type BadReaper = (); } impl motions::Trait for Test { type Origin = Origin; @@ -98,6 +103,8 @@ mod tests { pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig::{ + transaction_base_fee: 0, + transaction_byte_fee: 0, balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], existential_deposit: 0, transfer_fee: 0, diff --git a/substrate/srml/council/src/seats.rs b/substrate/srml/council/src/seats.rs index c3ce3ad1b1..365214f327 100644 --- a/substrate/srml/council/src/seats.rs +++ b/substrate/srml/council/src/seats.rs @@ -19,7 +19,10 @@ use rstd::prelude::*; use primitives::traits::{Zero, One, As, StaticLookup}; use runtime_io::print; -use srml_support::{StorageValue, StorageMap, dispatch::Result, traits::Currency, decl_storage, decl_event, ensure}; +use srml_support::{ + StorageValue, StorageMap, dispatch::Result, decl_storage, decl_event, ensure, + traits::{Currency, OnUnbalanced} +}; use democracy; use system::{self, ensure_signed}; @@ -77,14 +80,21 @@ use system::{self, ensure_signed}; // after each vote as all but K entries are cleared. newly registering candidates must use cleared // entries before they increase the capacity. -use srml_support::{decl_module, traits::ArithmeticType}; +use srml_support::decl_module; pub type VoteIndex = u32; -type BalanceOf = <::Currency as ArithmeticType>::Type; +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; pub trait Trait: democracy::Trait { type Event: From> + Into<::Event>; + + /// Handler for the unbalanced reduction when slashing a validator. + type BadPresentation: OnUnbalanced>; + + /// Handler for the unbalanced reduction when slashing an invalid reaping attempt. + type BadReaper: OnUnbalanced>; } decl_module! { @@ -101,7 +111,7 @@ decl_module! { ensure!(index == Self::vote_index(), "incorrect vote index"); ensure!(!candidates.len().is_zero(), "amount of candidates to receive approval votes should be non-zero"); // Prevent a vote from voters that provide a list of votes that exceeds the candidates length - // since otherise an attacker may be able to submit a very long list of `votes` that far exceeds + // since otherwise an attacker may be able to submit a very long list of `votes` that far exceeds // the amount of candidates and waste more computation than a reasonable voting bond would cover. ensure!(candidates.len() >= votes.len(), "amount of candidate approval votes cannot exceed amount of candidates"); @@ -167,7 +177,8 @@ decl_module! { T::Currency::repatriate_reserved(&who, &reporter, Self::voting_bond())?; Self::deposit_event(RawEvent::VoterReaped(who, reporter)); } else { - T::Currency::slash_reserved(&reporter, Self::voting_bond()); + let imbalance = T::Currency::slash_reserved(&reporter, Self::voting_bond()).0; + T::BadReaper::on_unbalanced(imbalance); Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); } } @@ -266,7 +277,8 @@ decl_module! { } else { // we can rest assured it will be Ok since we checked `can_slash` earlier; still // better safe than sorry. - let _ = T::Currency::slash(&who, bad_presentation_punishment); + let imbalance = T::Currency::slash(&who, bad_presentation_punishment).0; + T::BadPresentation::on_unbalanced(imbalance); Err(if dupe { "duplicate presentation" } else { "incorrect total" }) } } diff --git a/substrate/srml/democracy/src/lib.rs b/substrate/srml/democracy/src/lib.rs index befbb2082d..eb0502ccd9 100644 --- a/substrate/srml/democracy/src/lib.rs +++ b/substrate/srml/democracy/src/lib.rs @@ -24,7 +24,7 @@ use primitives::traits::{Zero, As, Bounded}; use parity_codec::{Encode, Decode}; use srml_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType, EnumerableStorageMap}; use srml_support::{decl_module, decl_storage, decl_event, ensure}; -use srml_support::traits::{Currency, LockableCurrency, WithdrawReason, ArithmeticType, LockIdentifier}; +use srml_support::traits::{Currency, LockableCurrency, WithdrawReason, LockIdentifier}; use srml_support::dispatch::Result; use system::ensure_signed; @@ -69,10 +69,10 @@ impl Vote { } } -type BalanceOf = <::Currency as ArithmeticType>::Type; +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; pub trait Trait: system::Trait + Sized { - type Currency: ArithmeticType + LockableCurrency<::AccountId, Moment=Self::BlockNumber, Balance=BalanceOf>; + type Currency: LockableCurrency<::AccountId, Moment=Self::BlockNumber>; type Proposal: Parameter + Dispatchable + IsSubType>; @@ -511,6 +511,9 @@ mod tests { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); } impl Trait for Test { type Currency = balances::Module; @@ -525,6 +528,8 @@ mod tests { fn new_test_ext_with_public_delay(public_delay: u64) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig::{ + transaction_base_fee: 0, + transaction_byte_fee: 0, balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], existential_deposit: 0, transfer_fee: 0, diff --git a/substrate/srml/example/src/lib.rs b/substrate/srml/example/src/lib.rs index e6f3cf9264..452e75ccc3 100644 --- a/substrate/srml/example/src/lib.rs +++ b/substrate/srml/example/src/lib.rs @@ -281,6 +281,9 @@ mod tests { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); } impl Trait for Test { type Event = (); diff --git a/substrate/srml/executive/Cargo.toml b/substrate/srml/executive/Cargo.toml index 0165a37bb2..31ca69fa55 100644 --- a/substrate/srml/executive/Cargo.toml +++ b/substrate/srml/executive/Cargo.toml @@ -18,7 +18,6 @@ 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.1" } [features] diff --git a/substrate/srml/executive/src/lib.rs b/substrate/srml/executive/src/lib.rs index b6cc498f79..f2073ff738 100644 --- a/substrate/srml/executive/src/lib.rs +++ b/substrate/srml/executive/src/lib.rs @@ -25,7 +25,7 @@ use primitives::traits::{ self, Header, Zero, One, Checkable, Applyable, CheckEqual, OnFinalise, OnInitialise, Hash, As, Digest, NumberFor, Block as BlockT }; -use srml_support::{Dispatchable, traits::ChargeBytesFee}; +use srml_support::{Dispatchable, traits::MakePayment}; use parity_codec::{Codec, Encode}; use system::extrinsics_root; use primitives::{ApplyOutcome, ApplyError}; @@ -64,7 +64,7 @@ impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: ChargeBytesFee, + Payment: MakePayment, AllModules: OnInitialise + OnFinalise, > ExecuteBlock for Executive where Block::Extrinsic: Checkable + Codec, @@ -85,7 +85,7 @@ impl< System: system::Trait, Block: traits::Block, Context: Default, - Payment: ChargeBytesFee, + Payment: MakePayment, AllModules: OnInitialise + OnFinalise, > Executive where Block::Extrinsic: Checkable + Codec, @@ -214,7 +214,7 @@ impl< ) } // pay any fees. - Payment::charge_base_bytes_fee(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; + Payment::make_payment(sender, encoded_len).map_err(|_| internal::ApplyError::CantPay)?; // AUDIT: Under no circumstances may this function panic from here onwards. @@ -286,7 +286,7 @@ impl< if let (Some(sender), Some(index)) = (xt.sender(), xt.index()) { // pay any fees. - if Payment::charge_base_bytes_fee(sender, encoded_len).is_err() { + if Payment::make_payment(sender, encoded_len).is_err() { return TransactionValidity::Invalid(ApplyError::CantPay as i8) } @@ -332,7 +332,6 @@ 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! { @@ -342,7 +341,7 @@ mod tests { impl_outer_event!{ pub enum MetaEvent for Runtime { - balances, fees, + balances, } } @@ -367,29 +366,26 @@ mod tests { type OnFreeBalanceZero = (); type OnNewAccount = (); type Event = MetaEvent; - } - impl fees::Trait for Runtime { - type Event = MetaEvent; - type TransferAsset = balances::Module; + type TransactionPayment = (); + type DustRemoval = (); + type TransferPayment = (); } type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive, system::ChainContext, fees::Module, ()>; + type Executive = super::Executive, system::ChainContext, balances::Module, ()>; #[test] fn balance_transfer_dispatch_works() { let mut t = system::GenesisConfig::::default().build_storage().unwrap().0; t.extend(balances::GenesisConfig:: { + transaction_base_fee: 10, + transaction_byte_fee: 0, balances: vec![(1, 111)], 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, || { @@ -414,7 +410,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("6651861f40a8f42c033b3e937cb3513e6dbaf4be6bafb1561a19f884be3f58dd").into(), + state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, @@ -448,7 +444,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("6651861f40a8f42c033b3e937cb3513e6dbaf4be6bafb1561a19f884be3f58dd").into(), + state_root: hex!("49cd58a254ccf6abc4a023d9a22dcfc421e385527a250faec69f8ad0d8ed3e48").into(), extrinsics_root: [0u8; 32].into(), digest: Digest { logs: vec![], }, }, diff --git a/substrate/srml/fees/Cargo.toml b/substrate/srml/fees/Cargo.toml deleted file mode 100644 index 2bb4474ce6..0000000000 --- a/substrate/srml/fees/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "srml-fees" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -hex-literal = "0.1.0" -serde = { version = "1.0", optional = true } -parity-codec = { version = "3.2", default-features = false } -parity-codec-derive = { version = "3.1", 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", - "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/substrate/srml/fees/src/lib.rs b/substrate/srml/fees/src/lib.rs deleted file mode 100644 index 09e32db9b0..0000000000 --- a/substrate/srml/fees/src/lib.rs +++ /dev/null @@ -1,117 +0,0 @@ -// 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, StorageMap, decl_event, decl_storage, decl_module, - traits::{ArithmeticType, ChargeBytesFee, ChargeFee, TransferAsset, WithdrawReason} -}; -use runtime_primitives::traits::{ - As, CheckedAdd, CheckedSub, CheckedMul, Zero -}; -use system; - -mod mock; -mod tests; - -type AssetOf = <::TransferAsset as ArithmeticType>::Type; - -pub trait Trait: system::Trait { - /// The overarching event type. - type Event: From> + Into<::Event>; - - /// A function does the asset transfer between accounts - type TransferAsset: ArithmeticType + 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 = AssetOf { - /// 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(): AssetOf; - /// The fee to be paid for making a transaction; the per-byte portion. - pub TransactionByteFee get(transaction_byte_fee) config(): AssetOf; - - /// 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 => AssetOf; - } -} - -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( - & as As>::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 = AssetOf; - - fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> 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::withdraw(transactor, amount, WithdrawReason::TransactionPayment)?; - - >::insert(extrinsic_index, new_fee); - Ok(()) - } - - fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> 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::deposit(transactor, amount)?; - - >::insert(extrinsic_index, new_fee); - Ok(()) - } -} diff --git a/substrate/srml/fees/src/mock.rs b/substrate/srml/fees/src/mock.rs deleted file mode 100644 index 7f6c715f4b..0000000000 --- a/substrate/srml/fees/src/mock.rs +++ /dev/null @@ -1,115 +0,0 @@ -// 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}, - testing::{Digest, DigestItem, Header}, -}; -use primitives::{H256, Blake2Hasher}; -use runtime_io; -use srml_support::{ - impl_outer_origin, impl_outer_event, - traits::{ArithmeticType, TransferAsset, WithdrawReason} -}; -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 withdraw(_: &AccountId, _: Self::Amount, _: WithdrawReason) -> Result<(), &'static str> { Ok(()) } - fn deposit(_: &AccountId, _: Self::Amount) -> Result<(), &'static str> { Ok(()) } -} - -impl ArithmeticType for TransferAssetMock { - type Type = u64; -} - -// 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 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/substrate/srml/fees/src/tests.rs b/substrate/srml/fees/src/tests.rs deleted file mode 100644 index a1c9657062..0000000000 --- a/substrate/srml/fees/src/tests.rs +++ /dev/null @@ -1,174 +0,0 @@ -// 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/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index 4650bff981..151668f691 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -6,8 +6,6 @@ // 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 @@ -27,11 +25,11 @@ use parity_codec::{HasCompact, Encode, Decode}; use srml_support::{StorageValue, StorageMap, EnumerableStorageMap, dispatch::Result}; use srml_support::{decl_module, decl_event, decl_storage, ensure}; use srml_support::traits::{ - Currency, OnDilution, OnFreeBalanceZero, ArithmeticType, - LockIdentifier, LockableCurrency, WithdrawReasons + Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency, WithdrawReasons, + OnUnbalanced, Imbalance }; use session::OnSessionChange; -use primitives::{Perbill}; +use primitives::Perbill; use primitives::traits::{Zero, One, As, StaticLookup, Saturating, Bounded}; #[cfg(feature = "std")] use primitives::{Serialize, Deserialize}; @@ -166,13 +164,14 @@ pub struct Exposure { pub others: Vec>, } -type BalanceOf = <::Currency as ArithmeticType>::Type; +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; +type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; pub trait Trait: system::Trait + session::Trait { /// The staking balance. type Currency: - ArithmeticType + - Currency> + + Currency + LockableCurrency; /// Some tokens minted. @@ -180,6 +179,12 @@ pub trait Trait: system::Trait + session::Trait { /// The overarching event type. type Event: From> + Into<::Event>; + + /// Handler for the unbalanced reduction when slashing a staker. + type Slash: OnUnbalanced>; + + /// Handler for the unbalanced increment when rewarding a staker. + type Reward: OnUnbalanced>; } const STAKING_ID: LockIdentifier = *b"staking "; @@ -540,7 +545,8 @@ impl Module { let slash = slash.min(exposure.total); // The amount we'll slash from the validator's stash directly. let own_slash = exposure.own.min(slash); - let own_slash = own_slash - T::Currency::slash(v, own_slash).unwrap_or_default(); + let (mut imbalance, missing) = T::Currency::slash(v, own_slash); + let own_slash = own_slash - missing; // The amount remaining that we can't slash from the validator, that must be taken from the nominators. let rest_slash = slash - own_slash; if !rest_slash.is_zero() { @@ -549,29 +555,29 @@ impl Module { if !total.is_zero() { let safe_mul_rational = |b| b * rest_slash / total;// FIXME #1572 avoid overflow for i in exposure.others.iter() { - let _ = T::Currency::slash(&i.who, safe_mul_rational(i.value)); // best effort - not much that can be done on fail. + // best effort - not much that can be done on fail. + imbalance.subsume(T::Currency::slash(&i.who, safe_mul_rational(i.value)).0) } } } + T::Slash::on_unbalanced(imbalance); } /// Actually make a payment to a staker. This uses the currency's reward function /// to pay the right payee for the given staker account. - fn make_payout(who: &T::AccountId, amount: BalanceOf) { + fn make_payout(who: &T::AccountId, amount: BalanceOf) -> Option> { match Self::payee(who) { - RewardDestination::Controller => { - let _ = T::Currency::reward(&who, amount); - } - RewardDestination::Stash => { - let _ = Self::ledger(who).map(|l| T::Currency::reward(&l.stash, amount)); - } + RewardDestination::Controller => T::Currency::deposit_into_existing(&who, amount).ok(), + RewardDestination::Stash => Self::ledger(who) + .and_then(|l| T::Currency::deposit_into_existing(&l.stash, amount).ok()), RewardDestination::Staked => - if let Some(mut l) = Self::ledger(who) { + Self::ledger(who).and_then(|mut l| { l.active += amount; l.total += amount; - let _ = T::Currency::reward(&l.stash, amount); + let r = T::Currency::deposit_into_existing(&l.stash, amount).ok(); Self::update_ledger(who, l); - }, + r + }), } } @@ -580,6 +586,7 @@ impl Module { fn reward_validator(who: &T::AccountId, reward: BalanceOf) { let off_the_table = reward.min(Self::validators(who).validator_payment); let reward = reward - off_the_table; + let mut imbalance = >::zero(); let validator_cut = if reward.is_zero() { Zero::zero() } else { @@ -587,11 +594,12 @@ impl Module { let total = exposure.total.max(One::one()); let safe_mul_rational = |b| b * reward / total;// FIXME #1572: avoid overflow for i in &exposure.others { - Self::make_payout(&i.who, safe_mul_rational(i.value)); + imbalance.maybe_subsume(Self::make_payout(&i.who, safe_mul_rational(i.value))); } safe_mul_rational(exposure.own) }; - Self::make_payout(who, validator_cut + off_the_table); + imbalance.maybe_subsume(Self::make_payout(who, validator_cut + off_the_table)); + T::Reward::on_unbalanced(imbalance); } /// Get the reward for the session, assuming it ends with this block. diff --git a/substrate/srml/staking/src/mock.rs b/substrate/srml/staking/src/mock.rs index 6a76f350ef..662396e2f1 100644 --- a/substrate/srml/staking/src/mock.rs +++ b/substrate/srml/staking/src/mock.rs @@ -58,6 +58,9 @@ impl balances::Trait for Test { type OnFreeBalanceZero = Staking; type OnNewAccount = (); type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); } impl session::Trait for Test { type ConvertAccountIdToSessionKey = ConvertUintAuthorityId; @@ -72,6 +75,8 @@ impl Trait for Test { type Currency = balances::Module; type OnRewardMinted = (); type Event = (); + type Slash = (); + type Reward = (); } pub struct ExtBuilder { @@ -193,6 +198,8 @@ impl ExtBuilder { (40, balance_factor), (41, balance_factor * 40) ] }, + transaction_base_fee: 0, + transaction_byte_fee: 0, existential_deposit: self.existential_deposit, transfer_fee: 0, creation_fee: 0, diff --git a/substrate/srml/staking/src/tests.rs b/substrate/srml/staking/src/tests.rs index 7921d7f313..527e82cf97 100644 --- a/substrate/srml/staking/src/tests.rs +++ b/substrate/srml/staking/src/tests.rs @@ -103,7 +103,7 @@ fn invulnerability_should_work() { // Make account 10 invulnerable assert_ok!(Staking::set_invulnerables(vec![10])); // Give account 10 some funds - Balances::set_free_balance(&10, 70); + let _ = Balances::deposit_creating(&10, 69); // There is no slash grace -- slash immediately. assert_eq!(Staking::offline_slash_grace(), 0); // Account 10 has not been slashed @@ -132,7 +132,7 @@ fn offline_should_slash_and_kick() { // Test that an offline validator gets slashed and kicked with_externalities(&mut ExtBuilder::default().build(), || { // Give account 10 some balance - Balances::set_free_balance(&10, 1000); + let _ = Balances::deposit_creating(&10, 999); // Confirm account 10 is a validator assert!(>::exists(&10)); // Validators get slashed immediately @@ -163,7 +163,7 @@ fn offline_grace_should_delay_slashing() { // Tests that with grace, slashing is delayed with_externalities(&mut ExtBuilder::default().build(), || { // Initialize account 10 with balance - Balances::set_free_balance(&10, 70); + let _ = Balances::deposit_creating(&10, 69); // Verify account 10 has balance assert_eq!(Balances::free_balance(&10), 70); @@ -204,8 +204,8 @@ fn max_unstake_threshold_works() { with_externalities(&mut ExtBuilder::default().build(), || { const MAX_UNSTAKE_THRESHOLD: u32 = 10; // Two users with maximum possible balance - Balances::set_free_balance(&10, u64::max_value()); - Balances::set_free_balance(&20, u64::max_value()); + let _ = Balances::deposit_creating(&10, u64::max_value() - 1); + let _ = Balances::deposit_creating(&20, u64::max_value() - 1); // Give them full exposer as a staker >::insert(&10, Exposure { total: u64::max_value(), own: u64::max_value(), others: vec![]}); @@ -253,9 +253,6 @@ fn max_unstake_threshold_works() { fn slashing_does_not_cause_underflow() { // Tests that slashing more than a user has does not underflow with_externalities(&mut ExtBuilder::default().build(), || { - // One user with less than `max_value` will test underflow does not occur - Balances::set_free_balance(&10, 1); - // Verify initial conditions assert_eq!(Balances::free_balance(&10), 1); assert_eq!(Staking::offline_slash_grace(), 0); @@ -449,7 +446,7 @@ fn staking_should_work() { assert_eq!(Staking::bonding_duration(), 2); // put some money in account that we'll use. - for i in 1..5 { Balances::set_free_balance(&i, 1000); } + for i in 1..5 { let _ = Balances::deposit_creating(&i, 1000); } // bond one account pair and state interest in nomination. // this is needed to keep 10 and 20 in the validator list with phragmen @@ -628,9 +625,9 @@ fn nominating_and_rewards_should_work() { // give the man some money let initial_balance = 1000; - for i in 1..5 { Balances::set_free_balance(&i, initial_balance); } - Balances::set_free_balance(&10, initial_balance); - Balances::set_free_balance(&20, initial_balance); + for i in [1, 2, 3, 4, 5, 10, 20].iter() { + let _ = Balances::deposit_creating(i, initial_balance - Balances::total_balance(i)); + } // record their balances. for i in 1..5 { assert_eq!(Balances::total_balance(&i), initial_balance); } @@ -705,8 +702,9 @@ fn nominators_also_get_slashed() { // give the man some money. let initial_balance = 1000; - for i in 1..3 { Balances::set_free_balance(&i, initial_balance); } - Balances::set_free_balance(&10, initial_balance); + for i in [1, 2, 3, 10].iter() { + let _ = Balances::deposit_creating(i, initial_balance - Balances::total_balance(i)); + } // 2 will nominate for 10 let nominator_stake = 500; @@ -843,7 +841,7 @@ fn cannot_transfer_staked_balance() { assert_noop!(Balances::transfer(Origin::signed(11), 20, 1), "account liquidity restrictions prevent withdrawal"); // Give account 11 extra free balance - Balances::set_free_balance(&11, 10000); + let _ = Balances::deposit_creating(&11, 9999); // Confirm that account 11 can now transfer some balance assert_ok!(Balances::transfer(Origin::signed(11), 20, 1)); }); @@ -863,7 +861,7 @@ fn cannot_reserve_staked_balance() { assert_noop!(Balances::reserve(&11, 1), "account liquidity restrictions prevent withdrawal"); // Give account 11 extra free balance - Balances::set_free_balance(&11, 10000); + let _ = Balances::deposit_creating(&11, 9990); // Confirm account 11 can now reserve balance assert_ok!(Balances::reserve(&11, 1)); }); @@ -1041,7 +1039,7 @@ fn bond_extra_works() { assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] })); // Give account 11 some large free balance greater than total - Balances::set_free_balance(&11, 1000000); + let _ = Balances::deposit_creating(&11, 999000); // Check the balance of the stash account assert_eq!(Balances::free_balance(&11), 1000000); @@ -1077,7 +1075,7 @@ fn bond_extra_and_withdraw_unbonded_works() { assert_ok!(Staking::set_bonding_duration(2)); // Give account 11 some large free balance greater than total - Balances::set_free_balance(&11, 1000000); + let _ = Balances::deposit_creating(&11, 999000); // Check the balance of the stash account assert_eq!(Balances::free_balance(&11), 1000000); @@ -1175,8 +1173,8 @@ fn slot_stake_is_least_staked_validator_and_limits_maximum_punishment() { assert_eq!(Staking::stakers(&20).total, 2000); // Give the man some money. - Balances::set_free_balance(&10, 1000); - Balances::set_free_balance(&20, 1000); + let _ = Balances::deposit_creating(&10, 999); + let _ = Balances::deposit_creating(&20, 999); // Confirm initial free balance. assert_eq!(Balances::free_balance(&10), 1000); @@ -1247,7 +1245,7 @@ fn on_free_balance_zero_stash_removes_validator() { assert!(>::exists(&10)); // Reduce free_balance of controller to 0 - Balances::set_free_balance(&10, 0); + Balances::slash(&10, u64::max_value()); // Check total balance of account 10 assert_eq!(Balances::total_balance(&10), 0); @@ -1263,7 +1261,7 @@ fn on_free_balance_zero_stash_removes_validator() { assert!(>::exists(&10)); // Reduce free_balance of stash to 0 - Balances::set_free_balance(&11, 0); + Balances::slash(&11, u64::max_value()); // Check total balance of stash assert_eq!(Balances::total_balance(&11), 0); @@ -1306,7 +1304,7 @@ fn on_free_balance_zero_stash_removes_nominator() { assert!(>::exists(&10)); // Reduce free_balance of controller to 0 - Balances::set_free_balance(&10, 0); + Balances::slash(&10, u64::max_value()); // Check total balance of account 10 assert_eq!(Balances::total_balance(&10), 0); @@ -1321,7 +1319,7 @@ fn on_free_balance_zero_stash_removes_nominator() { assert!(>::exists(&10)); // Reduce free_balance of stash to 0 - Balances::set_free_balance(&11, 0); + Balances::slash(&11, u64::max_value()); // Check total balance of stash assert_eq!(Balances::total_balance(&11), 0); @@ -1382,7 +1380,7 @@ fn phragmen_poc_works() { // bond [2,1](A), [4,3](B), [6,5](C) as the 3 nominators // Give all of them some balance to be able to bond properly. - for i in &[1, 3, 5] { Balances::set_free_balance(i, 50); } + for i in &[1, 3, 5] { let _ = Balances::deposit_creating(i, 50); } // Linking names to the above test: // 10 => X // 20 => Y @@ -1439,7 +1437,7 @@ fn phragmen_election_works() { // bond [2,1](A), [4,3](B), as 2 nominators // Give all of them some balance to be able to bond properly. - for i in &[1, 3] { Balances::set_free_balance(i, 50); } + for i in &[1, 3] { let _ = Balances::deposit_creating(i, 50); } assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20])); @@ -1499,7 +1497,7 @@ fn switching_roles() { assert_eq!(Session::validators(), vec![20, 10]); // put some money in account that we'll use. - for i in 1..7 { Balances::set_free_balance(&i, 5000); } + for i in 1..7 { let _ = Balances::deposit_creating(&i, 5000); } // add 2 nominators assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default())); @@ -1567,7 +1565,7 @@ fn wrong_vote_is_null() { assert_eq!(Session::validators(), vec![40, 30, 20, 10]); // put some money in account that we'll use. - for i in 1..3 { Balances::set_free_balance(&i, 5000); } + for i in 1..3 { let _ = Balances::deposit_creating(&i, 5000); } // add 1 nominators assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default())); diff --git a/substrate/srml/support/src/traits.rs b/substrate/srml/support/src/traits.rs index fbce46eac0..30f441fd21 100644 --- a/substrate/srml/support/src/traits.rs +++ b/substrate/srml/support/src/traits.rs @@ -61,14 +61,184 @@ pub enum UpdateBalanceOutcome { AccountKilled, } -pub trait ArithmeticType { - type Type: SimpleArithmetic + As + As + Codec + Copy + MaybeSerializeDebug + Default; +/// Simple trait designed for hooking into a transaction payment. +/// +/// It operates over a single generic `AccountId` type. +pub trait MakePayment { + /// Make transaction payment from `who` for an extrinsic of encoded length + /// `encoded_len` bytes. Return `Ok` iff the payment was successful. + fn make_payment(who: &AccountId, encoded_len: usize) -> Result<(), &'static str>; +} + +impl MakePayment for () { + fn make_payment(_: &T, _: usize) -> Result<(), &'static str> { Ok(()) } +} + +/// Handler for when some currency "account" decreased in balance for +/// some reason. +/// +/// The only reason at present for an increase would be for validator rewards, but +/// there may be other reasons in the future or for other chains. +/// +/// Reasons for decreases include: +/// +/// - Someone got slashed. +/// - Someone paid for a transaction to be included. +pub trait OnUnbalanced { + /// Handler for some imbalance. Infallible. + fn on_unbalanced(amount: Imbalance); +} + +impl OnUnbalanced for () { + fn on_unbalanced(amount: Imbalance) { drop(amount); } +} + +/// Simple boolean for whether an account needs to be kept in existence. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum ExistenceRequirement { + /// Operation must not result in the account going out of existence. + KeepAlive, + /// Operation may result in account going out of existence. + AllowDeath, +} + +/// A trait for a not-quite Linear Type that tracks an imbalance. +/// +/// Functions that alter account balances return an object of this trait to +/// express how much account balances have been altered in aggregate. If +/// dropped, the currency system will take some default steps to deal with +/// the imbalance (`balances` module simply reduces or increases its +/// total issuance). Your module should generally handle it in some way, +/// good practice is to do so in a configurable manner using an +/// `OnUnbalanced` type for each situation in which your module needs to +/// handle an imbalance. +/// +/// Imbalances can either be Positive (funds were added somewhere without +/// being subtracted elsewhere - e.g. a reward) or Negative (funds deducted +/// somewhere without an equal and opposite addition - e.g. a slash or +/// system fee payment). +/// +/// Since they are unsigned, the actual type is always Positive or Negative. +/// The trait makes no distinction except to define the `Opposite` type. +/// +/// New instances of zero value can be created (`zero`) and destroyed +/// (`drop_zero`). +/// +/// Existing instances can be `split` and merged either consuming `self` with +/// `merge` or mutating `self` with `subsume`. If the target is an `Option`, +/// then `maybe_merge` and `maybe_subsume` might work better. Instances can +/// also be `offset` with an `Opposite` that is less than or equal to in value. +/// +/// You can always retrieve the raw balance value using `peek`. +#[must_use] +pub trait Imbalance: Sized { + /// The oppositely imbalanced type. They come in pairs. + type Opposite: Imbalance; + + /// The zero imbalance. Can be destroyed with `drop_zero`. + fn zero() -> Self; + + /// Drop an instance cleanly. Only works if its `value()` is zero. + fn drop_zero(self) -> Result<(), Self>; + + /// Consume `self` and return two independent instances; the first + /// is guaranteed to be at most `amount` and the second will be the remainder. + fn split(self, amount: Balance) -> (Self, Self); + + /// Consume `self` and an `other` to return a new instance that combines + /// both. + fn merge(self, other: Self) -> Self; + + /// Consume `self` and maybe an `other` to return a new instance that combines + /// both. + fn maybe_merge(self, other: Option) -> Self { + if let Some(o) = other { + self.merge(o) + } else { + self + } + } + + /// Consume an `other` to mutate `self` into a new instance that combines + /// both. + fn subsume(&mut self, other: Self); + + /// Maybe consume an `other` to mutate `self` into a new instance that combines + /// both. + fn maybe_subsume(&mut self, other: Option) { + if let Some(o) = other { + self.subsume(o) + } + } + + /// Consume self and along with an opposite counterpart to return + /// a combined result. + /// + /// Returns `Ok` along with a new instance of `Self` if this instance has a + /// greater value than the `other`. Otherwise returns `Err` with an instance of + /// the `Opposite`. In both cases the value represents the combination of `self` + /// and `other`. + fn offset(self, other: Self::Opposite) -> Result; + + /// The raw value of self. + fn peek(&self) -> Balance; +} + +/// Either a positive or a negative imbalance. +pub enum SignedImbalance>{ + /// A positive imbalance (funds have been created but none destroyed). + Positive(P), + /// A negative imbalance (funds have been destroyed but none created). + Negative(P::Opposite), +} + +impl< + P: Imbalance, + N: Imbalance, + B: SimpleArithmetic + As + As + Codec + Copy + MaybeSerializeDebug + Default, +> SignedImbalance { + pub fn zero() -> Self { + SignedImbalance::Positive(P::zero()) + } + + pub fn drop_zero(self) -> Result<(), Self> { + match self { + SignedImbalance::Positive(x) => x.drop_zero().map_err(SignedImbalance::Positive), + SignedImbalance::Negative(x) => x.drop_zero().map_err(SignedImbalance::Negative), + } + } + + /// Consume `self` and an `other` to return a new instance that combines + /// both. + pub fn merge(self, other: Self) -> Self { + match (self, other) { + (SignedImbalance::Positive(one), SignedImbalance::Positive(other)) => + SignedImbalance::Positive(one.merge(other)), + (SignedImbalance::Negative(one), SignedImbalance::Negative(other)) => + SignedImbalance::Negative(one.merge(other)), + (SignedImbalance::Positive(one), SignedImbalance::Negative(other)) => + if one.peek() > other.peek() { + SignedImbalance::Positive(one.offset(other).ok().unwrap_or_else(P::zero)) + } else { + SignedImbalance::Negative(other.offset(one).ok().unwrap_or_else(N::zero)) + }, + (one, other) => other.merge(one), + } + } } /// Abstraction over a fungible assets system. pub trait Currency { /// The balance of an account. - type Balance; + type Balance: SimpleArithmetic + As + As + Codec + Copy + MaybeSerializeDebug + Default; + + /// The opaque token type for an imbalance. This is returned by unbalanced operations + /// and must be dealt with. It may be dropped but cannot be cloned. + type PositiveImbalance: Imbalance; + + /// The opaque token type for an imbalance. This is returned by unbalanced operations + /// and must be dealt with. It may be dropped but cannot be cloned. + type NegativeImbalance: Imbalance; // PUBLIC IMMUTABLES @@ -117,30 +287,83 @@ pub trait Currency { /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. fn reserved_balance(who: &AccountId) -> Self::Balance; - // PUBLIC MUTABLES (DANGEROUS) + /// Returns `Ok` iff the account is able to make a withdrawal of the given amount + /// for the given reason. Basically, it's just a dry-run of `withdraw`. + /// + /// `Err(...)` with the reason why not otherwise. + fn ensure_can_withdraw( + who: &AccountId, + _amount: Self::Balance, + reason: WithdrawReason, + new_balance: Self::Balance, + ) -> result::Result<(), &'static str>; - /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + // PUBLIC MUTABLES (DANGEROUS) + + /// Transfer some liquid free balance to another staker. + /// + /// This is a very high-level function. It will ensure all appropriate fees are paid + /// and no imbalance in the system remains. + fn transfer( + source: &AccountId, + dest: &AccountId, + value: Self::Balance, + ) -> result::Result<(), &'static str>; + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the /// free balance. This function cannot fail. /// + /// The resulting imbalance is the first item of the tuple returned. + /// /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - fn slash(who: &AccountId, value: Self::Balance) -> Option; + /// then a non-zero second item will be returned. + fn slash( + who: &AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance); - /// Adds up to `value` to the free balance of `who`. + /// Mints `value` to the free balance of `who`. /// /// If `who` doesn't exist, nothing is done and an Err returned. - fn reward(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>; + fn deposit_into_existing( + who: &AccountId, + value: Self::Balance + ) -> result::Result; - /// Adds up to `value` to the free balance of `who`. + /// Removes some free balance from `who` account for `reason` if possible. If `liveness` is `KeepAlive`, + /// then no less than `ExistentialDeposit` must be left remaining. /// - /// If `who` doesn't exist, it is created - /// - /// Returns if the account was successfully updated or update has led to killing of the account. - /// - /// NOTE: This assumes that the total stake remains unchanged after this operation. - fn increase_free_balance_creating(who: &AccountId, value: Self::Balance) -> UpdateBalanceOutcome; + /// This checks any locks, vesting and liquidity requirements. If the removal is not possible, then it + /// returns `Err`. + fn withdraw( + who: &AccountId, + value: Self::Balance, + reason: WithdrawReason, + liveness: ExistenceRequirement, + ) -> result::Result; - /// Moves `value` from balance to reserved balance. + /// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created + /// + /// Infallible. + fn deposit_creating( + who: &AccountId, + value: Self::Balance, + ) -> Self::PositiveImbalance; + + /// Ensure an account's free balance equals some value; this will create the account + /// if needed. + /// + /// Returns a signed imbalance and status to indicate if the account was successfully updated or update + /// has led to killing of the account. + fn ensure_free_balance_is( + who: &AccountId, + balance: Self::Balance, + ) -> ( + SignedImbalance, + UpdateBalanceOutcome, + ); + + /// Moves `value` from balance to reserved balance. /// /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will /// be returned to notify of this. This is different behaviour to `unreserve`. @@ -149,27 +372,31 @@ pub trait Currency { /// Moves up to `value` from reserved balance to balance. This function cannot fail. /// /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. + /// then non-zero will be returned. + /// /// NOTE: This is different to `reserve`. - fn unreserve(who: &AccountId, value: Self::Balance) -> Option; + fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance; /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. /// /// As much funds up to `value` will be deducted as possible. If this is less than `value`, - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - fn slash_reserved(who: &AccountId, value: Self::Balance) -> Option; + /// then non-zero second item will be returned. + fn slash_reserved( + who: &AccountId, + value: Self::Balance + ) -> (Self::NegativeImbalance, Self::Balance); /// Moves up to `value` from reserved balance of account `slashed` to free balance of account /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be /// returned. /// - /// As much funds up to `value` will be moved as possible. If this is less than `value`, then - /// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`. + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Ok(non_zero)` will be returned. fn repatriate_reserved( slashed: &AccountId, beneficiary: &AccountId, value: Self::Balance - ) -> result::Result, &'static str>; + ) -> result::Result; } /// An identifier for a lock. Used for disambiguating different locks so that @@ -207,25 +434,6 @@ pub trait LockableCurrency: Currency { ); } -/// Charge bytes fee trait -pub trait ChargeBytesFee { - /// Charge fees from `transactor` for an extrinsic (transaction) of encoded length - /// `encoded_len` bytes. Return Ok if the payment was successful. - fn charge_base_bytes_fee(transactor: &AccountId, encoded_len: usize) -> Result<(), &'static str>; -} - -/// 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 if the refund was successful. - fn refund_fee(transactor: &AccountId, amount: Self::Amount) -> Result<(), &'static str>; -} - bitmask! { /// Reasons for moving funds out of an account. #[derive(Encode, Decode)] @@ -240,39 +448,7 @@ bitmask! { Transfer = 0b00000010, /// In order to reserve some funds for a later return or repatriation Reserve = 0b00000100, + /// In order to pay some other (higher-level) fees. + Fee = 0b00001000, } } - -/// 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 deducting `amount` in the account balances. - fn withdraw(who: &AccountId, amount: Self::Amount, reason: WithdrawReason) -> Result<(), &'static str>; - - /// Add asset to `who` account by increasing `amount` in the account balances. - fn deposit(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 withdraw(_: &T, _: Self::Amount, _: WithdrawReason) -> Result<(), &'static str> { Ok(()) } - fn deposit(_: &T, _: Self::Amount) -> Result<(), &'static str> { Ok(()) } -} diff --git a/substrate/srml/treasury/src/lib.rs b/substrate/srml/treasury/src/lib.rs index f571420593..8585950f7c 100644 --- a/substrate/srml/treasury/src/lib.rs +++ b/substrate/srml/treasury/src/lib.rs @@ -22,12 +22,14 @@ use serde_derive::{Serialize, Deserialize}; use rstd::prelude::*; use srml_support::{StorageValue, StorageMap, decl_module, decl_storage, decl_event, ensure}; -use srml_support::traits::{Currency, OnDilution, ArithmeticType}; +use srml_support::traits::{Currency, OnDilution, OnUnbalanced, Imbalance}; use runtime_primitives::{Permill, traits::{Zero, EnsureOrigin, StaticLookup}}; use parity_codec::{Encode, Decode}; use system::ensure_signed; -type BalanceOf = <::Currency as ArithmeticType>::Type; +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; +type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; /// Our module's configuration trait. All our types and consts go in here. If the /// module is dependent on specific other modules, then their configuration traits @@ -36,7 +38,7 @@ type BalanceOf = <::Currency as ArithmeticType>::Type; /// `system::Trait` should always be included in our implied traits. pub trait Trait: system::Trait { /// The staking balance. - type Currency: ArithmeticType + Currency::Currency as ArithmeticType>::Type>; + type Currency: Currency; /// Origin from which approvals must come. type ApproveOrigin: EnsureOrigin; @@ -46,6 +48,12 @@ pub trait Trait: system::Trait { /// The overarching event type. type Event: From> + Into<::Event>; + + /// Handler for the unbalanced increase when minting cash from the "Pot". + type MintedForSpending: OnUnbalanced>; + + /// Handler for the unbalanced decrease when slashing for a rejected proposal. + type ProposalRejection: OnUnbalanced>; } type ProposalIndex = u32; @@ -103,7 +111,8 @@ decl_module! { let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; let value = proposal.bond; - let _ = T::Currency::slash_reserved(&proposal.proposer, value); + let imbalance = T::Currency::slash_reserved(&proposal.proposer, value).0; + T::ProposalRejection::on_unbalanced(imbalance); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -202,6 +211,7 @@ impl Module { Self::deposit_event(RawEvent::Spending(budget_remaining)); let mut missed_any = false; + let mut imbalance = >::zero(); >::mutate(|v| { v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. @@ -214,7 +224,7 @@ impl Module { let _ = T::Currency::unreserve(&p.proposer, p.bond); // provide the allocation. - T::Currency::increase_free_balance_creating(&p.beneficiary, p.value); + imbalance.subsume(T::Currency::deposit_creating(&p.beneficiary, p.value)); Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); false @@ -228,6 +238,8 @@ impl Module { }); }); + T::MintedForSpending::on_unbalanced(imbalance); + if !missed_any { // burn some proportion of the remaining budget if we run a surplus. let burn = (Self::burn() * budget_remaining).min(budget_remaining); @@ -288,12 +300,17 @@ mod tests { type OnNewAccount = (); type OnFreeBalanceZero = (); type Event = (); + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); } impl Trait for Test { type Currency = balances::Module; type ApproveOrigin = system::EnsureRoot; type RejectOrigin = system::EnsureRoot; type Event = (); + type MintedForSpending = (); + type ProposalRejection = (); } type Balances = balances::Module; type Treasury = Module; @@ -302,6 +319,8 @@ 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,