diff --git a/Cargo.lock b/Cargo.lock index c8ef9e2a8c192..3e7386758f495 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,6 +451,7 @@ dependencies = [ "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", @@ -491,6 +492,7 @@ dependencies = [ "substrate-codec-derive 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-council 0.1.0", "substrate-runtime-democracy 0.1.0", @@ -2655,6 +2657,25 @@ dependencies = [ "substrate-runtime-primitives 0.1.0", ] +[[package]] +name = "substrate-runtime-balances" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", +] + [[package]] name = "substrate-runtime-consensus" version = "0.1.0" @@ -2682,16 +2703,13 @@ dependencies = [ "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-codec 0.1.0", "substrate-primitives 0.1.0", - "substrate-runtime-consensus 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", "substrate-runtime-sandbox 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", "substrate-runtime-std 0.1.0", "substrate-runtime-support 0.1.0", "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", "wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2707,16 +2725,14 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-democracy 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", "substrate-runtime-std 0.1.0", "substrate-runtime-support 0.1.0", "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", ] [[package]] @@ -2730,15 +2746,13 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-codec-derive 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", "substrate-runtime-std 0.1.0", "substrate-runtime-support 0.1.0", "substrate-runtime-system 0.1.0", - "substrate-runtime-timestamp 0.1.0", ] [[package]] @@ -2751,6 +2765,7 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-codec-derive 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", @@ -2841,6 +2856,7 @@ dependencies = [ "substrate-codec-derive 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", diff --git a/Cargo.toml b/Cargo.toml index e01d45d2d03d3..d853608a8f99d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ members = [ "substrate/runtime-sandbox", "substrate/runtime-std", "substrate/runtime-support", + "substrate/runtime/balances", "substrate/runtime/consensus", "substrate/runtime/contract", "substrate/runtime/council", diff --git a/demo/cli/src/lib.rs b/demo/cli/src/lib.rs index 8346e9aaa6b3a..4260f7d1cd0fe 100644 --- a/demo/cli/src/lib.rs +++ b/demo/cli/src/lib.rs @@ -50,8 +50,8 @@ pub mod error; use std::sync::Arc; use demo_primitives::{AccountId, Hash}; use demo_runtime::{Block, BlockId, GenesisConfig, - ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig, StakingConfig, - TimestampConfig}; + BalancesConfig, ConsensusConfig, CouncilConfig, DemocracyConfig, SessionConfig, + StakingConfig, TimestampConfig}; use futures::{Future, Sink, Stream}; use tokio::runtime::Runtime; use demo_executor::NativeExecutor; @@ -164,13 +164,7 @@ pub fn run(args: I) -> error::Result<()> where authorities: vec![god_key.clone().into()], }), system: None, - session: Some(SessionConfig { - validators: vec![god_key.clone().into()], - session_length: 720, // that's 1 hour per session. - }), - staking: Some(StakingConfig { - current_era: 0, - intentions: vec![], + balances: Some(BalancesConfig { transaction_base_fee: 100, transaction_byte_fee: 1, transfer_fee: 0, @@ -178,10 +172,18 @@ pub fn run(args: I) -> error::Result<()> where reclaim_rebate: 0, existential_deposit: 500, balances: vec![(god_key.clone().into(), 1u64 << 63)].into_iter().collect(), + }), + session: Some(SessionConfig { + validators: vec![god_key.clone().into()], + session_length: 720, // that's 1 hour per session. + }), + staking: Some(StakingConfig { + current_era: 0, + intentions: vec![], validator_count: 12, minimum_validator_count: 4, sessions_per_era: 24, // 24 hours per era. - bonding_duration: 90, // 90 days per bond. + bonding_duration: 90 * 24 * 720, // 90 days per bond. early_era_slash: 10000, session_reward: 100, offline_slash_grace: 0, diff --git a/demo/executor/Cargo.toml b/demo/executor/Cargo.toml index 96b547af13f9b..c07fb3887841d 100644 --- a/demo/executor/Cargo.toml +++ b/demo/executor/Cargo.toml @@ -20,6 +20,7 @@ demo-runtime = { path = "../runtime" } [dev-dependencies] substrate-keyring = { path = "../../substrate/keyring" } substrate-runtime-primitives = { path = "../../substrate/runtime/primitives" } +substrate-runtime-balances = { path = "../../substrate/runtime/balances" } substrate-runtime-session = { path = "../../substrate/runtime/session" } substrate-runtime-staking = { path = "../../substrate/runtime/staking" } substrate-runtime-system = { path = "../../substrate/runtime/system" } diff --git a/demo/executor/src/lib.rs b/demo/executor/src/lib.rs index 89ab8de55974e..64289234b8070 100644 --- a/demo/executor/src/lib.rs +++ b/demo/executor/src/lib.rs @@ -30,6 +30,7 @@ extern crate triehash; #[cfg(test)] extern crate substrate_keyring as keyring; #[cfg(test)] extern crate substrate_runtime_primitives as runtime_primitives; #[cfg(test)] extern crate substrate_runtime_support as runtime_support; +#[cfg(test)] extern crate substrate_runtime_balances as balances; #[cfg(test)] extern crate substrate_runtime_session as session; #[cfg(test)] extern crate substrate_runtime_staking as staking; #[cfg(test)] extern crate substrate_runtime_system as system; @@ -52,10 +53,10 @@ mod tests { use demo_primitives::{Hash, BlockNumber, AccountId}; use runtime_primitives::traits::Header as HeaderT; use runtime_primitives::{ApplyOutcome, ApplyError, ApplyResult, MaybeUnsigned}; - use {staking, session, system, consensus}; + use {balances, staking, session, system, consensus}; use system::{EventRecord, Phase}; - use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Staking, - BuildStorage, GenesisConfig, SessionConfig, StakingConfig, BareExtrinsic, System, Event}; + use demo_runtime::{Header, Block, UncheckedExtrinsic, Extrinsic, Call, Concrete, Balances, + BuildStorage, GenesisConfig, BalancesConfig, SessionConfig, StakingConfig, BareExtrinsic, System, Event}; use ed25519::{Public, Pair}; const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm"); @@ -80,7 +81,7 @@ mod tests { let extrinsic = BareExtrinsic { signed: alice(), index: 0, - function: Call::Staking(staking::Call::transfer::(bob().into(), 69)), + function: Call::Balances(balances::Call::transfer::(bob().into(), 69)), }; let signature = MaybeUnsigned(Keyring::from_raw_public(extrinsic.signed.0.clone()).unwrap() .sign(&extrinsic.encode()).into()); @@ -103,13 +104,13 @@ mod tests { #[test] fn panic_execution_with_foreign_code_gives_error() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -123,13 +124,13 @@ mod tests { #[test] fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -143,13 +144,13 @@ mod tests { #[test] fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -159,21 +160,21 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 42); - assert_eq!(Staking::voting_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42); + assert_eq!(Balances::total_balance(&bob()), 69); }); } #[test] fn successful_execution_with_foreign_code_gives_ok() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -183,8 +184,8 @@ mod tests { assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 42); - assert_eq!(Staking::voting_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42); + assert_eq!(Balances::total_balance(&bob()), 69); }); } @@ -194,6 +195,15 @@ mod tests { GenesisConfig { consensus: Some(Default::default()), system: Some(Default::default()), + balances: Some(BalancesConfig { + balances: vec![(alice(), 111)], + transaction_base_fee: 1, + transaction_byte_fee: 0, + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }), session: Some(SessionConfig { session_length: 2, validators: vec![One.to_raw_public().into(), Two.to_raw_public().into(), three], @@ -201,17 +211,10 @@ mod tests { staking: Some(StakingConfig { sessions_per_era: 2, current_era: 0, - balances: vec![(alice(), 111)], intentions: vec![alice(), bob(), Charlie.to_raw_public().into()], validator_count: 3, minimum_validator_count: 0, bonding_duration: 0, - transaction_base_fee: 1, - transaction_byte_fee: 0, - existential_deposit: 0, - transfer_fee: 0, - creation_fee: 0, - reclaim_rebate: 0, early_era_slash: 0, session_reward: 0, offline_slash_grace: 0, @@ -257,11 +260,11 @@ mod tests { // Blake // hex!("3437bf4b182ab17bb322af5c67e55f6be487a77084ad2b4e27ddac7242e4ad21").into(), // Keccak - hex!("0b401681b95d04e91dbe53835867bdcb5d9e0590b54ae06bd7b347d49f9a737f").into(), + hex!("a3f5ce86e303f4001d14124ab690428d10cd9e60d21699b42096358c2422445f").into(), vec![BareExtrinsic { signed: alice(), index: 0, - function: Call::Staking(staking::Call::transfer(bob().into(), 69)), + function: Call::Balances(balances::Call::transfer(bob().into(), 69)), }] ) } @@ -273,17 +276,17 @@ mod tests { // Blake // hex!("741fcb660e6fa9f625fbcd993b49f6c1cc4040f5e0cc8727afdedf11fd3c464b").into(), // Keccak - hex!("03f051dc4f588fdc713145772486a129d33c7f178c133b5801fa79c3ecca2dc9").into(), + hex!("72dc147d2619a978adc38a38abc85bb77e25b0095ad38b15f97d56ccb66f36e8").into(), vec![ BareExtrinsic { signed: bob(), index: 0, - function: Call::Staking(staking::Call::transfer(alice().into(), 5)), + function: Call::Balances(balances::Call::transfer(alice().into(), 5)), }, BareExtrinsic { signed: alice(), index: 1, - function: Call::Staking(staking::Call::transfer(bob().into(), 15)), + function: Call::Balances(balances::Call::transfer(bob().into(), 15)), } ] ) @@ -296,7 +299,7 @@ mod tests { // Blake // hex!("2c7231a9c210a7aa4bea169d944bc4aaacd517862b244b8021236ffa7f697991").into(), // Keccak - hex!("6e3b6aaf0be927394b520e3ebc0c34a7c26519711bc836e116e371273c3aca44").into(), + hex!("7aa14ff631321ca5aa22e6fa53e3569faa732758993fa82e2dbde31a1b720391").into(), vec![BareExtrinsic { signed: alice(), index: 0, @@ -312,12 +315,12 @@ mod tests { executor().call(&mut t, COMPACT_CODE, "execute_block", &block1().0, true).0.unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 41); - assert_eq!(Staking::voting_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 41); + assert_eq!(Balances::total_balance(&bob()), 69); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::staking(staking::RawEvent::NewAccount(bob(), 1, staking::NewAccountOutcome::NoHint)) + event: Event::balances(balances::RawEvent::NewAccount(bob(), 1, balances::NewAccountOutcome::NoHint)) } ]); }); @@ -325,8 +328,8 @@ mod tests { executor().call(&mut t, COMPACT_CODE, "execute_block", &block2().0, true).0.unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 30); - assert_eq!(Staking::voting_balance(&bob()), 78); + assert_eq!(Balances::total_balance(&alice()), 30); + assert_eq!(Balances::total_balance(&bob()), 78); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::Finalization, @@ -347,15 +350,15 @@ mod tests { WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 41); - assert_eq!(Staking::voting_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 41); + assert_eq!(Balances::total_balance(&bob()), 69); }); WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap(); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 30); - assert_eq!(Staking::voting_balance(&bob()), 78); + assert_eq!(Balances::total_balance(&alice()), 30); + assert_eq!(Balances::total_balance(&bob()), 78); }); } @@ -386,13 +389,13 @@ mod tests { #[test] fn panic_execution_gives_error() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![70u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![69u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![70u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -407,13 +410,13 @@ mod tests { #[test] fn successful_execution_gives_ok() { let mut t: TestExternalities = map![ - twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], - twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(&>::key_for(alice())).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], + twox_128(>::key()).to_vec() => vec![0u8; 8], twox_128(&>::key_for(0)).to_vec() => vec![0u8; 32] ]; @@ -425,8 +428,8 @@ mod tests { assert_eq!(r, Ok(ApplyOutcome::Success)); runtime_io::with_externalities(&mut t, || { - assert_eq!(Staking::voting_balance(&alice()), 42); - assert_eq!(Staking::voting_balance(&bob()), 69); + assert_eq!(Balances::total_balance(&alice()), 42); + assert_eq!(Balances::total_balance(&bob()), 69); }); } } diff --git a/demo/runtime/Cargo.toml b/demo/runtime/Cargo.toml index a13a87469cb61..105c505e8db75 100644 --- a/demo/runtime/Cargo.toml +++ b/demo/runtime/Cargo.toml @@ -17,6 +17,7 @@ substrate-runtime-io = { path = "../../substrate/runtime-io" } substrate-runtime-support = { path = "../../substrate/runtime-support" } substrate-primitives = { path = "../../substrate/primitives" } substrate-keyring = { path = "../../substrate/keyring" } +substrate-runtime-balances = { path = "../../substrate/runtime/balances" } substrate-runtime-consensus = { path = "../../substrate/runtime/consensus" } substrate-runtime-council = { path = "../../substrate/runtime/council" } substrate-runtime-democracy = { path = "../../substrate/runtime/democracy" } @@ -37,6 +38,7 @@ std = [ "substrate-runtime-std/std", "substrate-runtime-io/std", "substrate-runtime-support/std", + "substrate-runtime-balances/std", "substrate-runtime-consensus/std", "substrate-runtime-council/std", "substrate-runtime-democracy/std", diff --git a/demo/runtime/src/lib.rs b/demo/runtime/src/lib.rs index 46101f8463595..f0c39af85d198 100644 --- a/demo/runtime/src/lib.rs +++ b/demo/runtime/src/lib.rs @@ -40,6 +40,7 @@ extern crate substrate_codec as codec; extern crate substrate_codec_derive; extern crate substrate_runtime_std as rstd; +extern crate substrate_runtime_balances as balances; extern crate substrate_runtime_consensus as consensus; extern crate substrate_runtime_council as council; extern crate substrate_runtime_democracy as democracy; @@ -88,6 +89,7 @@ impl HasPublicAux for Concrete { } impl system::Trait for Concrete { + type PublicAux = ::PublicAux; type Index = Index; type BlockNumber = BlockNumber; type Hash = Hash; @@ -101,9 +103,21 @@ impl system::Trait for Concrete { /// System module for this concrete runtime. pub type System = system::Module; +impl balances::Trait for Concrete { + type Balance = Balance; + type AccountIndex = AccountIndex; + type OnFreeBalanceZero = Staking; + type EnsureAccountLiquid = Staking; + type Event = Event; +} + +/// Staking module for this concrete runtime. +pub type Balances = balances::Module; + impl consensus::Trait for Concrete { - type PublicAux = ::PublicAux; + const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = SessionKey; + type OnOfflineValidator = Staking; } /// Consensus module for this concrete runtime. @@ -136,10 +150,6 @@ impl session::Trait for Concrete { pub type Session = session::Module; impl staking::Trait for Concrete { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; - type Balance = Balance; - type AccountIndex = AccountIndex; - type OnFreeBalanceZero = (); type Event = Event; } @@ -162,7 +172,7 @@ pub type CouncilVoting = council::voting::Module; impl_outer_event! { pub enum Event for Concrete { - session, staking + balances, session, staking } } @@ -171,9 +181,10 @@ impl_outer_dispatch! { #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub enum Call where aux: ::PublicAux { Consensus = 0, - Session = 1, - Staking = 2, - Timestamp = 3, + Balances = 1, + Session = 2, + Staking = 3, + Timestamp = 4, Democracy = 5, Council = 6, CouncilVoting = 7, @@ -183,16 +194,17 @@ impl_outer_dispatch! { #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub enum PrivCall { Consensus = 0, - Session = 1, - Staking = 2, - Democracy = 5, - Council = 6, - CouncilVoting = 7, + Balances = 1, + Session = 2, + Staking = 3, + Democracy = 4, + Council = 5, + CouncilVoting = 6, } } /// The address format for describing accounts. -pub type Address = staking::Address; +pub type Address = balances::Address; /// Block header type as expected by this runtime. pub type Header = generic::Header>; /// Block type as expected by this runtime. @@ -206,13 +218,14 @@ pub type Extrinsic = generic::Extrinsic; /// Extrinsic type that is signed. pub type BareExtrinsic = generic::Extrinsic; /// Executive: handles dispatch to the various modules. -pub type Executive = executive::Executive; impl_outer_config! { pub struct GenesisConfig for Concrete { ConsensusConfig => consensus, SystemConfig => system, + BalancesConfig => balances, SessionConfig => session, StakingConfig => staking, DemocracyConfig => democracy, diff --git a/demo/runtime/wasm/Cargo.lock b/demo/runtime/wasm/Cargo.lock index 8f831386d6db7..9bdb08220eccf 100644 --- a/demo/runtime/wasm/Cargo.lock +++ b/demo/runtime/wasm/Cargo.lock @@ -103,6 +103,7 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-codec-derive 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-council 0.1.0", "substrate-runtime-democracy 0.1.0", @@ -639,6 +640,29 @@ dependencies = [ "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-runtime-balances" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-codec 0.1.0", + "substrate-codec-derive 0.1.0", + "substrate-keyring 0.1.0", + "substrate-primitives 0.1.0", + "substrate-runtime-consensus 0.1.0", + "substrate-runtime-io 0.1.0", + "substrate-runtime-primitives 0.1.0", + "substrate-runtime-sandbox 0.1.0", + "substrate-runtime-session 0.1.0", + "substrate-runtime-std 0.1.0", + "substrate-runtime-support 0.1.0", + "substrate-runtime-system 0.1.0", + "substrate-runtime-timestamp 0.1.0", +] + [[package]] name = "substrate-runtime-consensus" version = "0.1.0" @@ -667,12 +691,11 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-democracy 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", "substrate-runtime-std 0.1.0", "substrate-runtime-support 0.1.0", "substrate-runtime-system 0.1.0", @@ -689,11 +712,10 @@ dependencies = [ "substrate-codec 0.1.0", "substrate-codec-derive 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", - "substrate-runtime-session 0.1.0", - "substrate-runtime-staking 0.1.0", "substrate-runtime-std 0.1.0", "substrate-runtime-support 0.1.0", "substrate-runtime-system 0.1.0", @@ -791,6 +813,7 @@ dependencies = [ "substrate-codec-derive 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-runtime-balances 0.1.0", "substrate-runtime-consensus 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-primitives 0.1.0", diff --git a/demo/runtime/wasm/Cargo.toml b/demo/runtime/wasm/Cargo.toml index 965c818788870..59cb7b4f720c8 100644 --- a/demo/runtime/wasm/Cargo.toml +++ b/demo/runtime/wasm/Cargo.toml @@ -15,6 +15,7 @@ substrate-primitives = { path = "../../../substrate/primitives", default-feature substrate-runtime-std = { path = "../../../substrate/runtime-std", default-features = false } substrate-runtime-io = { path = "../../../substrate/runtime-io", default-features = false } substrate-runtime-support = { path = "../../../substrate/runtime-support", default-features = false } +substrate-runtime-balances = { path = "../../../substrate/runtime/balances", default-features = false } substrate-runtime-consensus = { path = "../../../substrate/runtime/consensus", default-features = false } substrate-runtime-council = { path = "../../../substrate/runtime/council", default-features = false } substrate-runtime-democracy = { path = "../../../substrate/runtime/democracy", default-features = false } @@ -36,6 +37,7 @@ std = [ "substrate-runtime-std/std", "substrate-runtime-io/std", "substrate-runtime-support/std", + "substrate-runtime-balances/std", "substrate-runtime-consensus/std", "substrate-runtime-council/std", "substrate-runtime-democracy/std", diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm index 02a7fed93297c..675b08559e492 100644 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm differ diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm index b3376fd74f7cd..3cbfe39b46b48 100755 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index dc23e6a75f3dd..ed66e54638bdb 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index c1ef690b3fa2c..971048d5470b1 100755 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/runtime-support/src/dispatch.rs b/substrate/runtime-support/src/dispatch.rs index 9b26493839bff..62e34ecb09ca7 100644 --- a/substrate/runtime-support/src/dispatch.rs +++ b/substrate/runtime-support/src/dispatch.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Dispatch system. Just dispatches calls. diff --git a/substrate/runtime/balances/Cargo.toml b/substrate/runtime/balances/Cargo.toml new file mode 100644 index 0000000000000..5a86a99c11f30 --- /dev/null +++ b/substrate/runtime/balances/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "substrate-runtime-balances" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default_features = false } +serde_derive = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default_features = false} +substrate-keyring = { path = "../../keyring", optional = true } +substrate-codec = { path = "../../codec", default_features = false } +substrate-codec-derive = { path = "../../codec/derive", default_features = false } +substrate-primitives = { path = "../../primitives", default_features = false } +substrate-runtime-std = { path = "../../runtime-std", default_features = false } +substrate-runtime-io = { path = "../../runtime-io", default_features = false } +substrate-runtime-support = { path = "../../runtime-support", default_features = false } +substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-system = { path = "../system", default_features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "safe-mix/std", + "substrate-keyring", + "substrate-codec/std", + "substrate-codec-derive/std", + "substrate-primitives/std", + "substrate-runtime-std/std", + "substrate-runtime-io/std", + "substrate-runtime-support/std", + "substrate-runtime-primitives/std", + "substrate-runtime-system/std", +] diff --git a/substrate/runtime/staking/src/address.rs b/substrate/runtime/balances/src/address.rs similarity index 92% rename from substrate/runtime/staking/src/address.rs rename to substrate/runtime/balances/src/address.rs index 249e0b20db225..01fb25439507b 100644 --- a/substrate/runtime/staking/src/address.rs +++ b/substrate/runtime/balances/src/address.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Address type that is union of index and id for an account. diff --git a/substrate/runtime/balances/src/genesis_config.rs b/substrate/runtime/balances/src/genesis_config.rs new file mode 100644 index 0000000000000..677db1aaec20e --- /dev/null +++ b/substrate/runtime/balances/src/genesis_config.rs @@ -0,0 +1,84 @@ +// Copyright 2017 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 . + +//! Build a balances genesis block. + +#![cfg(feature = "std")] + +use std::collections::HashMap; +use rstd::prelude::*; +use codec::Encode; +use runtime_support::{StorageValue, StorageMap}; +use primitives::traits::{Zero, As}; +use substrate_primitives::KeccakHasher; +use {runtime_io, primitives}; +use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, CreationFee, TransferFee, + ReclaimRebate, ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalIssuance, + FreeBalance}; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +#[serde(deny_unknown_fields)] +pub struct GenesisConfig { + pub balances: Vec<(T::AccountId, T::Balance)>, + pub transaction_base_fee: T::Balance, + pub transaction_byte_fee: T::Balance, + pub transfer_fee: T::Balance, + pub creation_fee: T::Balance, + pub reclaim_rebate: T::Balance, + pub existential_deposit: T::Balance, +} + +impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + balances: vec![], + transaction_base_fee: T::Balance::sa(0), + transaction_byte_fee: T::Balance::sa(0), + transfer_fee: T::Balance::sa(0), + creation_fee: T::Balance::sa(0), + existential_deposit: T::Balance::sa(0), + reclaim_rebate: T::Balance::sa(0), + } + } +} + +impl primitives::BuildStorage for GenesisConfig { + fn build_storage(self) -> ::std::result::Result, Vec>, String> { + let total_stake: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n); + + let mut r: runtime_io::TestExternalities = map![ + Self::hash(>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(), + Self::hash(>::key()).to_vec() => self.transaction_base_fee.encode(), + Self::hash(>::key()).to_vec() => self.transaction_byte_fee.encode(), + Self::hash(>::key()).to_vec() => self.transfer_fee.encode(), + Self::hash(>::key()).to_vec() => self.creation_fee.encode(), + Self::hash(>::key()).to_vec() => self.existential_deposit.encode(), + Self::hash(>::key()).to_vec() => self.reclaim_rebate.encode(), + Self::hash(>::key()).to_vec() => total_stake.encode() + ]; + + let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect(); + for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE { + r.insert(Self::hash(&>::key_for(T::AccountIndex::sa(i))).to_vec(), + ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode()); + } + for (who, value) in self.balances.into_iter() { + r.insert(Self::hash(&>::key_for(who)).to_vec(), value.encode()); + } + Ok(r.into()) + } +} diff --git a/substrate/runtime/balances/src/lib.rs b/substrate/runtime/balances/src/lib.rs new file mode 100644 index 0000000000000..00c4fd04c320c --- /dev/null +++ b/substrate/runtime/balances/src/lib.rs @@ -0,0 +1,651 @@ +// Copyright 2017 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 . + +//! Balances: Handles balances. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +extern crate serde; + +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; + +#[macro_use] +extern crate substrate_runtime_support as runtime_support; + +#[cfg_attr(feature = "std", macro_use)] +extern crate substrate_runtime_std as rstd; + +#[macro_use] +extern crate substrate_codec_derive; + +extern crate substrate_codec as codec; +extern crate substrate_primitives; +extern crate substrate_runtime_io as runtime_io; +extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_system as system; + +use rstd::prelude::*; +use rstd::{cmp, result}; +use codec::{Encode, Decode, Codec, Input, Output}; +use runtime_support::{StorageValue, StorageMap, Parameter}; +use runtime_support::dispatch::Result; +use primitives::traits::{Zero, One, RefInto, SimpleArithmetic, Executable, MakePayment, + As, AuxLookup, Member, CheckedAdd, CheckedSub}; +use address::Address as RawAddress; + +mod mock; + +pub mod address; +mod tests; +mod genesis_config; + +#[cfg(feature = "std")] +pub use genesis_config::GenesisConfig; + +/// Number of account IDs stored per enum set. +const ENUM_SET_SIZE: usize = 64; + +/// The byte to identify intention to reclaim an existing account index. +const RECLAIM_INDEX_MAGIC: usize = 0x69; + +pub type Address = RawAddress<::AccountId, ::AccountIndex>; + +pub type Event = RawEvent< + ::AccountId, + ::AccountIndex +>; + +/// The account with the given id was killed. +pub trait OnFreeBalanceZero { + /// The account was the given id was killed. + fn on_free_balance_zero(who: &AccountId); +} + +impl OnFreeBalanceZero for () { + fn on_free_balance_zero(_who: &AccountId) {} +} +impl< + AccountId, + X: OnFreeBalanceZero, + Y: OnFreeBalanceZero, +> OnFreeBalanceZero for (X, Y) { + fn on_free_balance_zero(who: &AccountId) { + X::on_free_balance_zero(who); + Y::on_free_balance_zero(who); + } +} + +/// Determinator for whether a given account is able to transfer balance. +pub trait EnsureAccountLiquid { + /// Returns `Ok` iff the account is able to transfer funds normally. `Err(...)` + /// with the reason why not otherwise. + fn ensure_account_liquid(who: &AccountId) -> Result; +} + +impl EnsureAccountLiquid for () { + fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) } +} + +pub trait Trait: system::Trait { + /// The balance of an account. + type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As + As + As; + /// Type used for storing an account's index; implies the maximum number of accounts the system + /// can hold. + type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As + As + As + As + As + Copy; + /// 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; + + /// A function that returns true iff a given account can transfer its funds to another account. + type EnsureAccountLiquid: EnsureAccountLiquid; + + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +decl_module! { + pub struct Module; + + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum Call where aux: T::PublicAux { + fn transfer(aux, dest: RawAddress, value: T::Balance) -> Result = 0; + } + + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] + pub enum PrivCall { + fn set_balance(who: RawAddress, free: T::Balance, reserved: T::Balance) -> Result = 0; + } +} + +/// An event in this module. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, PartialEq, Eq, Clone)] +pub enum RawEvent { + /// A new account was created. + NewAccount(AccountId, AccountIndex, NewAccountOutcome), + /// An account was reaped. + ReapedAccount(AccountId), +} + +impl From> for () { + fn from(_: RawEvent) -> () { () } +} + +decl_storage! { + trait Store for Module as Balances { + // The total amount of stake on the system. + pub TotalIssuance get(total_stake): required T::Balance; + // The minimum amount allowed to keep an account open. + pub ExistentialDeposit get(existential_deposit): required T::Balance; + // The amount credited to a destination's account whose index was reclaimed. + pub ReclaimRebate get(reclaim_rebate): required T::Balance; + // The fee required to make a transfer. + pub TransferFee get(transfer_fee): required T::Balance; + // The fee required to create an account. At least as big as ReclaimRebate. + pub CreationFee get(creation_fee): required T::Balance; + + // The next free enumeration set. + pub NextEnumSet get(next_enum_set): required T::AccountIndex; + // The enumeration sets. + pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec ]; + + // The "free" balance of a given account. + // + // This is the only balance that matters in terms of most operations on tokens. It is + // alone used to determine the balance when in the contract execution environment. When this + // balance falls below the value of `ExistentialDeposit`, then the "current account" is + // deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback + // is invoked, giving a chance to external modules to cleanup data associated with + // the deleted account. + // + // `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets + // collapsed to zero if it ever becomes less than `ExistentialDeposit`. + pub FreeBalance get(free_balance): default map [ T::AccountId => T::Balance ]; + + // The amount of the balance of a given account that is exterally reserved; this can still get + // slashed, but gets slashed last of all. + // + // This balance is a "reserve" balance that other subsystems use in order to set aside tokens + // that are still "owned" by the account holder, but which are unspendable. (This is different + // and wholly unrelated to the `Bondage` system used in the staking module.) + // + // When this balance falls below the value of `ExistentialDeposit`, then this "reserve account" + // is deleted: specifically, `ReservedBalance`. + // + // `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets + // collapsed to zero if it ever becomes less than `ExistentialDeposit`. + pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ]; + + + // Payment stuff. + + // The fee to be paid for making a transaction; the base. + pub TransactionBaseFee get(transaction_base_fee): required T::Balance; + // The fee to be paid for making a transaction; the per-byte portion. + pub TransactionByteFee get(transaction_byte_fee): required T::Balance; + } +} + +/// Whatever happened about the hint given when creating the new account. +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)] +pub enum NewAccountOutcome { + NoHint, + GoodHint, + BadHint, +} + +/// Outcome of a balance update. +pub enum UpdateBalanceOutcome { + /// Account balance was simply updated. + Updated, + /// The update has led to killing of the account. + AccountKilled, +} + +impl Module { + + // PUBLIC IMMUTABLES + + /// The combined balance of `who`. + pub fn total_balance(who: &T::AccountId) -> T::Balance { + Self::free_balance(who) + Self::reserved_balance(who) + } + + /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no + /// balance changes in the meantime and only the reserved balance is not taken into account. + pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { + Self::free_balance(who) >= value + } + + /// Same result as `reserve(who, value)` (but without the side-effects) assuming there + /// are no balance changes in the meantime. + pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool { + if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() { + Self::free_balance(who) >= value + } else { + false + } + } + + /// Lookup an T::AccountIndex to get an Id, if there's one there. + pub fn lookup_index(index: T::AccountIndex) -> Option { + let enum_set_size = Self::enum_set_size(); + let set = Self::enum_set(index / enum_set_size); + let i: usize = (index % enum_set_size).as_(); + set.get(i).map(|x| x.clone()) + } + + /// `true` if the account `index` is ready for reclaim. + pub fn can_reclaim(try_index: T::AccountIndex) -> bool { + let enum_set_size = Self::enum_set_size(); + let try_set = Self::enum_set(try_index / enum_set_size); + let i = (try_index % enum_set_size).as_(); + i < try_set.len() && Self::total_balance(&try_set[i]).is_zero() + } + + /// Lookup an address to get an Id, if there's one there. + pub fn lookup_address(a: address::Address) -> Option { + match a { + address::Address::Id(i) => Some(i), + address::Address::Index(i) => Self::lookup_index(i), + } + } + + // PUBLIC DISPATCH + + /// Transfer some liquid free balance to another staker. + pub fn transfer(aux: &T::PublicAux, dest: Address, value: T::Balance) -> Result { + let dest = Self::lookup(dest)?; + + let transactor = aux.ref_into(); + let from_balance = Self::free_balance(transactor); + let would_create = from_balance.is_zero(); + let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; + let liability = value + fee; + + let new_from_balance = match from_balance.checked_sub(&liability) { + Some(b) => b, + None => return Err("balance too low to send value"), + }; + if would_create && value < Self::existential_deposit() { + return Err("value too low to create account"); + } + T::EnsureAccountLiquid::ensure_account_liquid(transactor)?; + + let to_balance = Self::free_balance(&dest); + // 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); + } + + Ok(()) + } + + // PRIV DISPATCH + + /// Deposit one of this module's events. + fn deposit_event(event: Event) { + >::deposit_event(::Event::from(event).into()); + } + + /// Set the balances of a given account. + fn set_balance(who: Address, free: T::Balance, reserved: T::Balance) -> Result { + let who = Self::lookup(who)?; + Self::set_free_balance(&who, free); + Self::set_reserved_balance(&who, reserved); + Ok(()) + } + + // PUBLIC MUTABLES (DANGEROUS) + + /// Set the free balance of an account to some new value. + /// + /// Will enforce ExistentialDeposit law, anulling the account as needed. + /// In that case it will return `AccountKilled`. + pub 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); + UpdateBalanceOutcome::AccountKilled + } else { + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit + /// law anulling 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 { + // Commented out for no - but consider it instructive. + // assert!(!Self::total_balance(who).is_zero()); + if balance < Self::existential_deposit() { + >::insert(who, balance); + Self::on_free_too_low(who); + UpdateBalanceOutcome::AccountKilled + } else { + >::insert(who, balance); + UpdateBalanceOutcome::Updated + } + } + + /// 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. + // TODO: enforce this for the other balance-altering functions. + if balance < ed { + Self::set_free_balance(who, balance); + UpdateBalanceOutcome::AccountKilled + } else { + if !>::exists(who) { + let outcome = Self::new_account(&who, balance); + let credit = match outcome { + NewAccountOutcome::GoodHint => balance + >::reclaim_rebate(), + _ => balance, + }; + Self::set_free_balance(who, credit); + Self::increase_total_stake_by(credit - balance); + } else { + Self::set_free_balance(who, balance); + } + + UpdateBalanceOutcome::Updated + } + } + + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the + /// free 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`. + pub fn slash(who: &T::AccountId, value: T::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) + } else { + None + } + } + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, nothing is done and an Err returned. + pub fn reward(who: &T::AccountId, value: T::Balance) -> 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(()) + } + + /// 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`. + pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result { + let b = Self::free_balance(who); + if b < value { + return Err("not enough free funds") + } + T::EnsureAccountLiquid::ensure_account_liquid(who)?; + Self::set_reserved_balance(who, Self::reserved_balance(who) + value); + Self::set_free_balance(who, b - value); + Ok(()) + } + + /// 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`. + /// NOTE: This is different to `reserve`. + pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option { + 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) + } + } + + /// 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`. + pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option { + let b = Self::reserved_balance(who); + let slash = cmp::min(b, value); + Self::set_reserved_balance(who, b - slash); + Self::decrease_total_stake_by(slash); + if value == slash { + None + } else { + Some(value - slash) + } + } + + /// 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)`. + pub fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: T::Balance + ) -> result::Result, &'static str> { + if Self::total_balance(beneficiary).is_zero() { + return Err("beneficiary account must pre-exist"); + } + let b = Self::reserved_balance(slashed); + 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)) + } + } + + fn enum_set_size() -> T::AccountIndex { + T::AccountIndex::sa(ENUM_SET_SIZE) + } + + /// Register a new account (with existential balance). + fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { + let enum_set_size = Self::enum_set_size(); + let next_set_index = Self::next_enum_set(); + let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC); + let reclaim_index_modulus = T::AccountIndex::sa(256usize); + let quantization = T::AccountIndex::sa(256usize); + + // A little easter-egg for reclaiming dead indexes.. + let ret = { + // we quantise the number of accounts so it stays constant over a reasonable + // period of time. + let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization; + // then modify the starting balance to be modulo this to allow it to potentially + // identify an account index for reuse. + let maybe_try_index = balance % >::sa(quantized_account_count * reclaim_index_modulus); + let maybe_try_index = As::::as_(maybe_try_index); + + // this identifier must end with magic byte 0x69 to trigger this check (a minor + // optimisation to ensure we don't check most unintended account creations). + if maybe_try_index % reclaim_index_modulus == reclaim_index_magic { + // reuse is probably intended. first, remove magic byte. + let try_index = maybe_try_index / reclaim_index_modulus; + + // then check to see if this balance identifies a dead account index. + let set_index = try_index / enum_set_size; + let mut try_set = Self::enum_set(set_index); + let item_index = (try_index % enum_set_size).as_(); + if item_index < try_set.len() { + if Self::total_balance(&try_set[item_index]).is_zero() { + // yup - this index refers to a dead account. can be reused. + try_set[item_index] = who.clone(); + >::insert(set_index, try_set); + + Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint)); + + return NewAccountOutcome::GoodHint + } + } + NewAccountOutcome::BadHint + } else { + NewAccountOutcome::NoHint + } + }; + + // insert normally as a back up + let mut set_index = next_set_index; + // defensive only: this loop should never iterate since we keep NextEnumSet up to date later. + let mut set = loop { + let set = Self::enum_set(set_index); + if set.len() < ENUM_SET_SIZE { + break set; + } + set_index += One::one(); + }; + + let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len()); + + // update set. + set.push(who.clone()); + + // keep NextEnumSet up to date + if set.len() == ENUM_SET_SIZE { + >::put(set_index + One::one()); + } + + // write set. + >::insert(set_index, set); + + Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret)); + + ret + } + + fn reap_account(who: &T::AccountId) { + >::remove(who); + Self::deposit_event(RawEvent::ReapedAccount(who.clone())); + } + + /// Kill an account's free portion. + fn on_free_too_low(who: &T::AccountId) { + Self::decrease_total_stake_by(Self::free_balance(who)); + >::remove(who); + + T::OnFreeBalanceZero::on_free_balance_zero(who); + + if Self::reserved_balance(who).is_zero() { + Self::reap_account(who); + } + } + + /// Kill an account's reserved portion. + fn on_reserved_too_low(who: &T::AccountId) { + Self::decrease_total_stake_by(Self::reserved_balance(who)); + >::remove(who); + + 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_stake().checked_add(&value) { + >::put(v); + } + } + /// Decrease TotalIssuance by Value. + pub fn decrease_total_stake_by(value: T::Balance) { + if let Some(v) = >::total_stake().checked_sub(&value) { + >::put(v); + } + } +} + +impl Executable for Module { + fn execute() { + } +} + +impl AuxLookup for Module { + type Source = address::Address; + type Target = T::AccountId; + fn lookup(a: Self::Source) -> result::Result { + match a { + address::Address::Id(i) => Ok(i), + address::Address::Index(i) => >::lookup_index(i).ok_or("invalid account index"), + } + } +} + +impl MakePayment for Module { + fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { + let b = Self::free_balance(transactor); + let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * >::sa(encoded_len as u64); + if b < transaction_fee + Self::existential_deposit() { + return Err("not enough funds for transaction fee"); + } + Self::set_free_balance(transactor, b - transaction_fee); + Self::decrease_total_stake_by(transaction_fee); + Ok(()) + } +} diff --git a/substrate/runtime/balances/src/mock.rs b/substrate/runtime/balances/src/mock.rs new file mode 100644 index 0000000000000..b7066a704fa16 --- /dev/null +++ b/substrate/runtime/balances/src/mock.rs @@ -0,0 +1,77 @@ +// Copyright 2018 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 primitives::BuildStorage; +use primitives::traits::HasPublicAux; +use primitives::testing::{Digest, Header}; +use substrate_primitives::{H256, KeccakHasher}; +use runtime_io; +use {GenesisConfig, Module, Trait, system}; + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +pub struct Test; +impl HasPublicAux for Test { + type PublicAux = u64; +} +impl system::Trait for Test { + type PublicAux = ::PublicAux; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = ::primitives::traits::BlakeTwo256; + type Digest = Digest; + type AccountId = u64; + type Header = Header; + type Event = (); +} +impl Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); + type Event = (); +} + +pub fn new_test_ext(ext_deposit: u64, monied: bool) -> runtime_io::TestExternalities { + let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + let balance_factor = if ext_deposit > 0 { + 256 + } else { + 1 + }; + t.extend(GenesisConfig::{ + balances: if monied { + vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor)] + } else { + vec![(10, balance_factor), (20, balance_factor)] + }, + transaction_base_fee: 0, + transaction_byte_fee: 0, + existential_deposit: ext_deposit, + transfer_fee: 0, + creation_fee: 0, + reclaim_rebate: 0, + }.build_storage().unwrap()); + t.into() +} + +pub type System = system::Module; +pub type Balances = Module; diff --git a/substrate/runtime/balances/src/tests.rs b/substrate/runtime/balances/src/tests.rs new file mode 100644 index 0000000000000..7f3a740a931a9 --- /dev/null +++ b/substrate/runtime/balances/src/tests.rs @@ -0,0 +1,324 @@ +// Copyright 2017 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 mock::{Balances, System, Test, new_test_ext}; + +#[test] +fn reward_should_work() { + with_externalities(&mut new_test_ext(0, true), || { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::reward(&1, 10)); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 110); + }); +} + +#[test] +fn indexing_lookup_should_work() { + with_externalities(&mut new_test_ext(10, true), || { + assert_eq!(Balances::lookup_index(0), Some(1)); + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(2), Some(3)); + assert_eq!(Balances::lookup_index(3), Some(4)); + assert_eq!(Balances::lookup_index(4), None); + }); +} + +#[test] +fn default_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(10, true), || { + assert_eq!(Balances::lookup_index(4), None); + assert_ok!(Balances::transfer(&1, 5.into(), 10)); + assert_eq!(Balances::lookup_index(4), Some(5)); + }); +} + +#[test] +fn dust_account_removal_should_work() { + with_externalities(&mut new_test_ext(256 * 10, true), || { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 256 * 10 + 1); + assert_eq!(System::account_nonce(&2), 0); + }); +} + +#[test] +fn reclaim_indexing_on_new_accounts_should_work() { + with_externalities(&mut new_test_ext(256 * 1, true), || { + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(4), None); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim) + assert_eq!(Balances::total_balance(&2), 0); + + assert_ok!(Balances::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1. + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(6)); + }); +} + +#[test] +fn reserved_balance_should_prevent_reclaim_count() { + with_externalities(&mut new_test_ext(256 * 1, true), || { + System::inc_account_nonce(&2); + assert_eq!(Balances::lookup_index(1), Some(2)); + assert_eq!(Balances::lookup_index(4), None); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists. + assert_eq!(System::account_nonce(&2), 1); + + assert_ok!(Balances::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(2)); // but fails. + assert_eq!(System::account_nonce(&2), 1); + + assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed + assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted." + assert_eq!(System::account_nonce(&2), 0); + + assert_ok!(Balances::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds. + }); +} + +#[test] +fn balance_works() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); +} + +#[test] +fn balance_transfer_works() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::transfer(&1, 2.into(), 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); +} + +#[test] +fn reserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(&1), 42); + assert_eq!(Balances::reserved_balance(&1), 69); + }); +} + +#[test] +fn balance_transfer_when_reserved_should_not_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!(Balances::transfer(&1, 2.into(), 69), "balance too low to send value"); + }); +} + +#[test] +fn deducting_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn refunding_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + Balances::set_reserved_balance(&1, 69); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(&1), 111); + assert_eq!(Balances::reserved_balance(&1), 0); + }); +} + +#[test] +fn slashing_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).is_none()); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 42); + assert_eq!(>::get(), 44); + }); +} + +#[test] +fn slashing_incomplete_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 42); + Balances::increase_total_stake_by(42); + assert_ok!(Balances::reserve(&1, 21)); + assert!(Balances::slash(&1, 69).is_some()); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 2); + }); +} + +#[test] +fn unreserving_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 42); + }); +} + +#[test] +fn slashing_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 111)); + assert!(Balances::slash_reserved(&1, 42).is_none()); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(>::get(), 71); + }); +} + +#[test] +fn slashing_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::reserve(&1, 42)); + assert!(Balances::slash_reserved(&1, 69).is_some()); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(>::get(), 71); + }); +} + +#[test] +fn transferring_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 110); + Balances::set_free_balance(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41), None); + assert_eq!(Balances::reserved_balance(&1), 69); + assert_eq!(Balances::free_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_reserved_balance_to_nonexistent_should_fail() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42), "beneficiary account must pre-exist"); + }); +} + +#[test] +fn transferring_incomplete_reserved_balance_should_work() { + with_externalities(&mut new_test_ext(0, false), || { + Balances::set_free_balance(&1, 110); + Balances::set_free_balance(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert!(Balances::repatriate_reserved(&1, &2, 69).unwrap().is_some()); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::free_balance(&1), 69); + assert_eq!(Balances::reserved_balance(&2), 0); + assert_eq!(Balances::free_balance(&2), 42); + }); +} + +#[test] +fn transferring_too_high_value_should_not_panic() { + with_externalities(&mut new_test_ext(0, false), || { + >::insert(1, u64::max_value()); + >::insert(2, 1); + + assert_err!( + Balances::transfer(&1, 2.into(), u64::max_value()), + "destination balance too high to receive value" + ); + + assert_eq!(Balances::free_balance(&1), u64::max_value()); + assert_eq!(Balances::free_balance(&2), 1); + }); +} + +#[test] +fn account_removal_on_free_too_low() { + with_externalities(&mut new_test_ext(100, false), || { + // Setup two accounts with free balance above the exsistential threshold. + { + Balances::set_free_balance(&1, 110); + Balances::increase_total_stake_by(110); + + Balances::set_free_balance(&2, 110); + Balances::increase_total_stake_by(110); + + assert_eq!(>::get(), 732); + } + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the exsistential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(&1, 2.into(), 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(&1), 0); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 642); + }); +} diff --git a/substrate/runtime/consensus/src/lib.rs b/substrate/runtime/consensus/src/lib.rs index 3b9e2554b4631..df6d034c8f555 100644 --- a/substrate/runtime/consensus/src/lib.rs +++ b/substrate/runtime/consensus/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Conensus module for runtime; manages the authority set ready for the native code. @@ -42,7 +42,7 @@ use rstd::prelude::*; use runtime_support::{storage, Parameter}; use runtime_support::dispatch::Result; use runtime_support::storage::unhashed::StorageVec; -use primitives::traits::{RefInto, MaybeSerializeDebug, MaybeEmpty}; +use primitives::traits::{MaybeSerializeDebug, MaybeEmpty}; use primitives::bft::MisbehaviorReport; #[cfg(any(feature = "std", test))] @@ -63,9 +63,20 @@ pub const CODE: &'static [u8] = b":code"; pub type KeyValue = (Vec, Vec); +pub trait OnOfflineValidator { + fn on_offline_validator(validator_index: usize); +} + +impl OnOfflineValidator for () { + fn on_offline_validator(_validator_index: usize) {} +} + pub trait Trait: system::Trait { - type PublicAux: RefInto + MaybeEmpty; // MaybeEmpty is for Timestamp's usage. + /// The allowed extrinsic position for `note_offline` inherent. + const NOTE_OFFLINE_POSITION: u32; + type SessionKey: Parameter + Default + MaybeSerializeDebug; + type OnOfflineValidator: OnOfflineValidator; } decl_module! { @@ -74,7 +85,8 @@ decl_module! { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Call where aux: T::PublicAux { fn report_misbehavior(aux, report: MisbehaviorReport) -> Result = 0; - fn remark(aux, remark: Vec) -> Result = 1; + fn note_offline(aux, offline_val_indices: Vec) -> Result = 1; + fn remark(aux, remark: Vec) -> Result = 2; } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -110,6 +122,24 @@ impl Module { Ok(()) } + /// Note the previous block's validator missed their opportunity to propose a block. This only comes in + /// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant + /// for the previous block. + fn note_offline(aux: &T::PublicAux, offline_val_indices: Vec) -> Result { + assert!(aux.is_empty()); + assert!( + >::extrinsic_index() == Some(T::NOTE_OFFLINE_POSITION), + "note_offline extrinsic must be at position {} in the block", + T::NOTE_OFFLINE_POSITION + ); + + for validator_index in offline_val_indices.into_iter() { + T::OnOfflineValidator::on_offline_validator(validator_index as usize); + } + + Ok(()) + } + /// Make some on-chain remark. fn remark(_aux: &T::PublicAux, _remark: Vec) -> Result { Ok(()) diff --git a/substrate/runtime/contract/Cargo.toml b/substrate/runtime/contract/Cargo.toml index 0658425c06cb4..f72c2b490ed75 100644 --- a/substrate/runtime/contract/Cargo.toml +++ b/substrate/runtime/contract/Cargo.toml @@ -8,16 +8,13 @@ serde = { version = "1.0", default_features = false } serde_derive = { version = "1.0", optional = true } substrate-codec = { path = "../../codec", default_features = false } substrate-primitives = { path = "../../primitives" } -substrate-runtime-consensus = { path = "../../runtime/consensus", default_features = false } substrate-runtime-primitives = { path = "../../runtime/primitives" } substrate-runtime-io = { path = "../../runtime-io", default_features = false } substrate-runtime-std = { path = "../../runtime-std", default_features = false } substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false } -substrate-runtime-staking = { path = "../../runtime/staking", default_features = false } substrate-runtime-support = { path = "../../runtime-support", default_features = false } substrate-runtime-system = { path = "../../runtime/system", default_features = false } -substrate-runtime-session = { path = "../session", default_features = false } -substrate-runtime-timestamp = { path = "../timestamp", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } parity-wasm = { version = "0.31", default_features = false } pwasm-utils = { version = "0.3", default_features = false } @@ -32,15 +29,12 @@ std = [ "serde/std", "substrate-codec/std", "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", "substrate-runtime-io/std", "substrate-runtime-std/std", + "substrate-runtime-balances/std", "substrate-runtime-sandbox/std", - "substrate-runtime-staking/std", "substrate-runtime-support/std", "substrate-runtime-system/std", - "substrate-runtime-timestamp/std", - "substrate-runtime-session/std", "parity-wasm/std", "pwasm-utils/std", ] diff --git a/substrate/runtime/contract/src/account_db.rs b/substrate/runtime/contract/src/account_db.rs index e5ad0e2b88391..d7ff094003c97 100644 --- a/substrate/runtime/contract/src/account_db.rs +++ b/substrate/runtime/contract/src/account_db.rs @@ -22,8 +22,7 @@ use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; use runtime_support::StorageMap; -use staking; -use system; +use {balances, system}; pub struct ChangeEntry { balance: Option, @@ -61,13 +60,13 @@ impl AccountDb for DirectAccountDb { >::get(account) } fn get_balance(&self, account: &T::AccountId) -> T::Balance { - staking::Module::::free_balance(account) + balances::Module::::free_balance(account) } fn commit(&mut self, s: ChangeSet) { for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance { - if let staking::UpdateBalanceOutcome::AccountKilled = - staking::Module::::set_free_balance_creating(&address, balance) + if let balances::UpdateBalanceOutcome::AccountKilled = + balances::Module::::set_free_balance_creating(&address, balance) { // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback // which will make removal of CodeOf and StorageOf for this account. diff --git a/substrate/runtime/contract/src/double_map.rs b/substrate/runtime/contract/src/double_map.rs index 505fe9e6a29a0..6867d2a5c697c 100644 --- a/substrate/runtime/contract/src/double_map.rs +++ b/substrate/runtime/contract/src/double_map.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! An implementation of double map backed by storage. //! diff --git a/substrate/runtime/contract/src/exec.rs b/substrate/runtime/contract/src/exec.rs index a62e2ce97f972..4779e959c17f4 100644 --- a/substrate/runtime/contract/src/exec.rs +++ b/substrate/runtime/contract/src/exec.rs @@ -22,8 +22,7 @@ use vm; use rstd::prelude::*; use runtime_primitives::traits::{Zero, CheckedAdd, CheckedSub}; use runtime_support::{StorageMap, StorageValue}; -use staking; -use system; +use balances::{self, EnsureAccountLiquid}; pub struct CreateReceipt { pub address: T::AccountId, @@ -185,9 +184,9 @@ fn transfer( >::contract_fee() } else { if would_create { - >::creation_fee() + >::creation_fee() } else { - >::transfer_fee() + >::transfer_fee() } }; @@ -200,12 +199,10 @@ fn transfer( Some(b) => b, None => return Err("balance too low to send value"), }; - if would_create && value < >::existential_deposit() { + if would_create && value < >::existential_deposit() { return Err("value too low to create account"); } - if >::bondage(transactor) > >::block_number() { - return Err("bondage too high to send value"); - } + ::EnsureAccountLiquid::ensure_account_liquid(transactor)?; let to_balance = overlay.get_balance(dest); let new_to_balance = match to_balance.checked_add(&value) { diff --git a/substrate/runtime/contract/src/gas.rs b/substrate/runtime/contract/src/gas.rs index b405e31d7fd59..9d1978f7a527c 100644 --- a/substrate/runtime/contract/src/gas.rs +++ b/substrate/runtime/contract/src/gas.rs @@ -17,7 +17,7 @@ use {Trait, Module, GasSpent}; use runtime_primitives::traits::{As, CheckedMul, CheckedSub, Zero}; use runtime_support::StorageValue; -use staking; +use balances; #[must_use] #[derive(Debug, PartialEq, Eq)] @@ -146,15 +146,15 @@ pub fn buy_gas( // Buy the specified amount of gas. let gas_price = >::gas_price(); - let b = >::free_balance(transactor); + let b = >::free_balance(transactor); let cost = >::as_(gas_limit.clone()) .checked_mul(&gas_price) .ok_or("overflow multiplying gas limit by price")?; - if b < cost + >::existential_deposit() { + if b < cost + >::existential_deposit() { return Err("not enough funds for transaction fee"); } - >::set_free_balance(transactor, b - cost); - >::decrease_total_stake_by(cost); + >::set_free_balance(transactor, b - cost); + >::decrease_total_stake_by(cost); Ok(GasMeter { limit: gas_limit, gas_left: gas_limit, @@ -171,8 +171,8 @@ pub fn refund_unused_gas(transactor: &T::AccountId, gas_meter: GasMete >::put(gas_spent); // Refund gas left by the price it was bought. - let b = >::free_balance(transactor); + 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); + >::set_free_balance(transactor, b + refund); + >::increase_total_stake_by(refund); } diff --git a/substrate/runtime/contract/src/lib.rs b/substrate/runtime/contract/src/lib.rs index 7479686ddc453..9f77b07fb8816 100644 --- a/substrate/runtime/contract/src/lib.rs +++ b/substrate/runtime/contract/src/lib.rs @@ -70,15 +70,9 @@ extern crate substrate_runtime_sandbox as sandbox; #[cfg_attr(feature = "std", macro_use)] extern crate substrate_runtime_std as rstd; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_staking as staking; +extern crate substrate_runtime_balances as balances; extern crate substrate_runtime_system as system; -#[cfg(test)] -extern crate substrate_runtime_timestamp as timestamp; -#[cfg(test)] -extern crate substrate_runtime_session as session; - #[macro_use] extern crate substrate_runtime_support as runtime_support; @@ -112,7 +106,7 @@ use runtime_primitives::traits::{As, RefInto, SimpleArithmetic, Executable}; use runtime_support::dispatch::Result; use runtime_support::{Parameter, StorageMap, StorageValue}; -pub trait Trait: system::Trait + staking::Trait + consensus::Trait { +pub trait Trait: balances::Trait { /// Function type to get the contract address given the creator. type DetermineContractAddress: ContractAddressFor; @@ -188,7 +182,7 @@ impl double_map::StorageDoubleMap for StorageOf { impl Module { /// Make a call to a specified account, optionally transferring some balance. fn call( - aux: &::PublicAux, + aux: &::PublicAux, dest: T::AccountId, value: T::Balance, gas_limit: T::Gas, @@ -233,7 +227,7 @@ impl Module { /// after the execution is saved as the `code` of the account. That code will be invoked /// upon any message received by this account. fn create( - aux: &::PublicAux, + aux: &::PublicAux, endowment: T::Balance, gas_limit: T::Gas, ctor_code: Vec, @@ -269,7 +263,7 @@ impl Module { } } -impl staking::OnFreeBalanceZero for Module { +impl balances::OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { >::remove(who); >::remove_prefix(who.clone()); diff --git a/substrate/runtime/contract/src/tests.rs b/substrate/runtime/contract/src/tests.rs index 5f1014243a046..d12b137d41e82 100644 --- a/substrate/runtime/contract/src/tests.rs +++ b/substrate/runtime/contract/src/tests.rs @@ -17,13 +17,13 @@ use double_map::StorageDoubleMap; use runtime_io::with_externalities; use runtime_primitives::testing::{Digest, H256, Header}; -use runtime_primitives::traits::{BlakeTwo256, HasPublicAux, Identity}; +use runtime_primitives::traits::{BlakeTwo256, HasPublicAux}; use runtime_primitives::BuildStorage; use runtime_support::StorageMap; use substrate_primitives::KeccakHasher; use wabt; use { - consensus, runtime_io, session, staking, system, timestamp, CodeOf, ContractAddressFor, + runtime_io, balances, system, CodeOf, ContractAddressFor, GenesisConfig, Module, StorageOf, Trait, }; @@ -32,11 +32,8 @@ pub struct Test; impl HasPublicAux for Test { type PublicAux = u64; } -impl consensus::Trait for Test { - type PublicAux = ::PublicAux; - type SessionKey = u64; -} impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -46,20 +43,11 @@ impl system::Trait for Test { type Header = Header; type Event = (); } -impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; -} -impl staking::Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; +impl balances::Trait for Test { type Balance = u64; type AccountIndex = u64; type OnFreeBalanceZero = Contract; - type Event = (); -} -impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = Staking; + type EnsureAccountLiquid = (); type Event = (); } impl Trait for Test { @@ -67,7 +55,7 @@ impl Trait for Test { type DetermineContractAddress = DummyContractAddressFor; } -type Staking = staking::Module; +type Balances = balances::Module; type Contract = Module; pub struct DummyContractAddressFor; @@ -109,45 +97,17 @@ impl ExtBuilder { .build_storage() .unwrap(); t.extend( - consensus::GenesisConfig:: { - code: vec![], - authorities: vec![], - }.build_storage() - .unwrap(), - ); - t.extend( - session::GenesisConfig:: { - session_length: 1, - validators: vec![10, 20], - }.build_storage() - .unwrap(), - ); - t.extend( - staking::GenesisConfig:: { - sessions_per_era: 1, - current_era: 0, + balances::GenesisConfig:: { balances: vec![], - intentions: vec![], - validator_count: 2, - minimum_validator_count: 0, - bonding_duration: 0, transaction_base_fee: 0, transaction_byte_fee: 0, existential_deposit: self.existential_deposit, transfer_fee: 0, creation_fee: 0, reclaim_rebate: 0, - early_era_slash: 0, - session_reward: 0, - offline_slash_grace: 0, }.build_storage() .unwrap(), ); - t.extend( - timestamp::GenesisConfig::::default() - .build_storage() - .unwrap(), - ); t.extend( GenesisConfig:: { contract_fee: 21, @@ -195,15 +155,15 @@ fn contract_transfer() { with_externalities(&mut ExtBuilder::default().build(), || { >::insert(1, code_transfer.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); - Staking::set_free_balance(&1, 11); - Staking::increase_total_stake_by(11); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); assert_ok!(Contract::call(&0, 1, 3, 100_000, Vec::new())); assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), // 3 - value sent with the transaction // 2 * 6 - gas used by the contract (6) multiplied by gas price (2) // 2 * 135 - base gas fee for call (by transaction) @@ -211,11 +171,11 @@ fn contract_transfer() { 100_000_000 - 3 - (2 * 6) - (2 * 135) - (2 * 135), ); assert_eq!( - Staking::free_balance(&1), + Balances::free_balance(&1), 11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE, ); assert_eq!( - Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), + Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), CONTRACT_SHOULD_TRANSFER_VALUE, ); }); @@ -230,10 +190,10 @@ fn contract_transfer_oog() { with_externalities(&mut ExtBuilder::default().build(), || { >::insert(1, code_transfer.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); - Staking::set_free_balance(&1, 11); - Staking::increase_total_stake_by(11); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); assert_err!( Contract::call(&0, 1, 3, 276, Vec::new()), @@ -241,15 +201,15 @@ fn contract_transfer_oog() { ); assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), // 3 - value sent with the transaction // 2 * 6 - gas used by the contract (6) multiplied by gas price (2) // 2 * 135 - base gas fee for call (by transaction) // 2 * 135 - base gas fee for call (by contract) 100_000_000 - (2 * 6) - (2 * 135) - (2 * 135), ); - assert_eq!(Staking::free_balance(&1), 11); - assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0); + assert_eq!(Balances::free_balance(&1), 11); + assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 0); }); } @@ -262,10 +222,10 @@ fn contract_transfer_max_depth() { with_externalities(&mut ExtBuilder::default().build(), || { >::insert(CONTRACT_SHOULD_TRANSFER_TO, code_transfer.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); - Staking::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11); - Staking::increase_total_stake_by(11); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 11); + Balances::increase_total_stake_by(11); assert_err!( Contract::call(&0, CONTRACT_SHOULD_TRANSFER_TO, 3, 100_000, Vec::new()), @@ -273,14 +233,14 @@ fn contract_transfer_max_depth() { ); assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), // 3 - value sent with the transaction // 2 * 6 * 100 - gas used by the contract (6) multiplied by gas price (2) // multiplied by max depth (100). // 2 * 135 * 100 - base gas fee for call (by transaction) multiplied by max depth (100). 100_000_000 - (2 * 135 * 100) - (2 * 6 * 100), ); - assert_eq!(Staking::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 11); + assert_eq!(Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), 11); }); } @@ -360,11 +320,11 @@ fn contract_create() { let code_create = wabt::wat2wasm(&code_create(&code_ctor_transfer)).unwrap(); with_externalities(&mut ExtBuilder::default().build(), || { - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); - Staking::set_free_balance(&1, 0); - Staking::set_free_balance(&9, 30); - Staking::increase_total_stake_by(30); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 0); + Balances::set_free_balance(&9, 30); + Balances::increase_total_stake_by(30); >::insert(1, code_create.to_vec()); @@ -383,23 +343,23 @@ fn contract_create() { // ((21 / 2) * 2) - price per account creation let expected_gas_after_create = 100_000_000 - 11 - (2 * 128) - (2 * 135) - (2 * 175) - ((21 / 2) * 2); - assert_eq!(Staking::free_balance(&0), expected_gas_after_create); - assert_eq!(Staking::free_balance(&1), 8); - assert_eq!(Staking::free_balance(&derived_address), 3); + assert_eq!(Balances::free_balance(&0), expected_gas_after_create); + assert_eq!(Balances::free_balance(&1), 8); + assert_eq!(Balances::free_balance(&derived_address), 3); // Initiate transfer to the newly created contract. assert_ok!(Contract::call(&0, derived_address, 22, 100_000, Vec::new())); assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), // 22 - value sent with the transaction // (2 * 6) - gas used by the contract // (2 * 135) - base gas fee for call (top level) // (2 * 135) - base gas fee for call (by transfer contract) expected_gas_after_create - 22 - (2 * 6) - (2 * 135) - (2 * 135), ); - assert_eq!(Staking::free_balance(&derived_address), 22 - 3); - assert_eq!(Staking::free_balance(&9), 36); + assert_eq!(Balances::free_balance(&derived_address), 22 - 3); + assert_eq!(Balances::free_balance(&9), 36); }); } @@ -414,10 +374,10 @@ fn top_level_create() { &0, ); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); - Staking::set_free_balance(&derived_address, 30); - Staking::increase_total_stake_by(30); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&derived_address, 30); + Balances::increase_total_stake_by(30); assert_ok!(Contract::create( &0, @@ -432,10 +392,10 @@ fn top_level_create() { // (3 * 175) - base gas fee for create (175) (top level) multipled by gas price (3) // ((21 / 3) * 3) - price for contract creation assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), 100_000_000 - 11 - (3 * 122) - (3 * 175) - ((21 / 3) * 3) ); - assert_eq!(Staking::free_balance(&derived_address), 30 + 11); + assert_eq!(Balances::free_balance(&derived_address), 30 + 11); assert_eq!(>::get(&derived_address), code_transfer); }); @@ -456,12 +416,12 @@ fn refunds_unused_gas() { with_externalities(&mut ExtBuilder::default().build(), || { >::insert(1, code_nop.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new())); - assert_eq!(Staking::free_balance(&0), 100_000_000 - 4 - (2 * 135)); + assert_eq!(Balances::free_balance(&0), 100_000_000 - 4 - (2 * 135)); }); } @@ -470,12 +430,12 @@ fn call_with_zero_value() { with_externalities(&mut ExtBuilder::default().build(), || { >::insert(1, vec![]); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); assert_ok!(Contract::call(&0, 1, 0, 100_000, Vec::new())); - assert_eq!(Staking::free_balance(&0), 100_000_000 - (2 * 135)); + assert_eq!(Balances::free_balance(&0), 100_000_000 - (2 * 135)); }); } @@ -484,13 +444,13 @@ fn create_with_zero_endowment() { let code_nop = wabt::wat2wasm(CODE_NOP).unwrap(); with_externalities(&mut ExtBuilder::default().build(), || { - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); assert_ok!(Contract::create(&0, 0, 100_000, code_nop, Vec::new())); assert_eq!( - Staking::free_balance(&0), + Balances::free_balance(&0), // 4 - for the gas spent by the constructor // 2 * 175 - base gas fee for create (175) multiplied by gas price (2) (top level) 100_000_000 - 4 - (2 * 175), @@ -505,13 +465,13 @@ fn account_removal_removes_storage() { || { // Setup two accounts with free balance above than exsistential threshold. { - Staking::set_free_balance(&1, 110); - Staking::increase_total_stake_by(110); + Balances::set_free_balance(&1, 110); + Balances::increase_total_stake_by(110); >::insert(1, b"foo".to_vec(), b"1".to_vec()); >::insert(1, b"bar".to_vec(), b"2".to_vec()); - Staking::set_free_balance(&2, 110); - Staking::increase_total_stake_by(110); + Balances::set_free_balance(&2, 110); + Balances::increase_total_stake_by(110); >::insert(2, b"hello".to_vec(), b"3".to_vec()); >::insert(2, b"world".to_vec(), b"4".to_vec()); } @@ -520,7 +480,7 @@ fn account_removal_removes_storage() { // the balance of account 1 is will be below than exsistential threshold. // // This should lead to the removal of all storage associated with this account. - assert_ok!(Staking::transfer(&1, 2.into(), 20)); + assert_ok!(Balances::transfer(&1, 2.into(), 20)); // Verify that all entries from account 1 is removed, while // entries from account 2 is in place. @@ -556,15 +516,15 @@ fn top_level_call_refunds_even_if_fails() { with_externalities(&mut ExtBuilder::default().gas_price(4).build(), || { >::insert(1, code_unreachable.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); assert_err!( Contract::call(&0, 1, 0, 100_000, Vec::new()), "vm execute returned error while call" ); - assert_eq!(Staking::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135)); + assert_eq!(Balances::free_balance(&0), 100_000_000 - (4 * 3) - (4 * 135)); }); } @@ -586,8 +546,8 @@ fn block_gas_limit() { || { >::insert(1, code_loop.to_vec()); - Staking::set_free_balance(&0, 100_000_000); - Staking::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); // Spend 50_000 units of gas (OOG). assert_err!( diff --git a/substrate/runtime/contract/src/vm/mod.rs b/substrate/runtime/contract/src/vm/mod.rs index ec26fb2daabfb..5b3318e6fcc8a 100644 --- a/substrate/runtime/contract/src/vm/mod.rs +++ b/substrate/runtime/contract/src/vm/mod.rs @@ -21,12 +21,10 @@ use exec::{CallReceipt, CreateReceipt}; use gas::{GasMeter, GasMeterResult}; use rstd::prelude::*; use runtime_primitives::traits::{As, CheckedMul}; -use sandbox; -use staking; -use system; +use {sandbox, balances, system}; use Trait; -type BalanceOf = ::Balance; +type BalanceOf = ::Balance; type AccountIdOf = ::AccountId; mod prepare; diff --git a/substrate/runtime/council/Cargo.toml b/substrate/runtime/council/Cargo.toml index aaffa4ba1af96..1f85d467d4863 100644 --- a/substrate/runtime/council/Cargo.toml +++ b/substrate/runtime/council/Cargo.toml @@ -17,14 +17,10 @@ substrate-runtime-io = { path = "../../runtime-io", default_features = false } substrate-runtime-support = { path = "../../runtime-support", default_features = false } substrate-runtime-primitives = { path = "../primitives", default_features = false } substrate-runtime-consensus = { path = "../consensus", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } substrate-runtime-democracy = { path = "../democracy", default_features = false } -substrate-runtime-session = { path = "../session", default_features = false } -substrate-runtime-staking = { path = "../staking", default_features = false } substrate-runtime-system = { path = "../system", default_features = false } -[dev-dependencies] -substrate-runtime-timestamp = { path = "../timestamp" } - [features] default = ["std"] std = [ @@ -38,9 +34,7 @@ std = [ "substrate-runtime-io/std", "substrate-runtime-support/std", "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", + "substrate-runtime-balances/std", "substrate-runtime-democracy/std", - "substrate-runtime-session/std", - "substrate-runtime-staking/std", "substrate-runtime-system/std", ] diff --git a/substrate/runtime/council/src/lib.rs b/substrate/runtime/council/src/lib.rs index 103679e8a7f0f..f13f637b3a259 100644 --- a/substrate/runtime/council/src/lib.rs +++ b/substrate/runtime/council/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Council system: Handles the voting in and maintenance of council members. @@ -33,19 +33,15 @@ extern crate substrate_primitives; extern crate substrate_runtime_io as runtime_io; #[macro_use] extern crate substrate_runtime_support; extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_consensus as consensus; +extern crate substrate_runtime_balances as balances; extern crate substrate_runtime_democracy as democracy; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_staking as staking; extern crate substrate_runtime_system as system; -#[cfg(test)] -extern crate substrate_runtime_timestamp as timestamp; use rstd::prelude::*; use primitives::traits::{Zero, One, RefInto, As, AuxLookup}; use substrate_runtime_support::{StorageValue, StorageMap}; use substrate_runtime_support::dispatch::Result; -use staking::address::Address; +use balances::address::Address; #[cfg(any(feature = "std", test))] use std::collections::HashMap; @@ -245,7 +241,7 @@ impl Module { if !>::exists(aux.ref_into()) { // not yet a voter - deduct bond. // NOTE: this must be the last potential bailer, since it changes state. - >::reserve(aux.ref_into(), Self::voting_bond())?; + >::reserve(aux.ref_into(), Self::voting_bond())?; >::put({ let mut v = Self::voters(); @@ -270,7 +266,7 @@ impl Module { who_index: u32, assumed_vote_index: VoteIndex ) -> Result { - let who = >::lookup(who)?; + let who = >::lookup(who)?; ensure!(!Self::presentation_active(), "cannot reap during presentation period"); ensure!(Self::voter_last_active(aux.ref_into()).is_some(), "reaper must be a voter"); let last_active = Self::voter_last_active(&who).ok_or("target for inactivity cleanup must be active")?; @@ -300,9 +296,9 @@ impl Module { if valid { // This only fails if `who` doesn't exist, which it clearly must do since its the aux. // Still, it's no more harmful to propagate any error at this point. - >::transfer_reserved(&who, aux.ref_into(), Self::voting_bond())?; + >::repatriate_reserved(&who, aux.ref_into(), Self::voting_bond())?; } else { - >::slash_reserved(aux.ref_into(), Self::voting_bond()); + >::slash_reserved(aux.ref_into(), Self::voting_bond()); } Ok(()) } @@ -317,7 +313,7 @@ impl Module { ensure!(&voters[index] == aux.ref_into(), "retraction index mismatch"); Self::remove_voter(aux.ref_into(), index, voters); - >::unreserve(aux.ref_into(), Self::voting_bond()); + >::unreserve(aux.ref_into(), Self::voting_bond()); Ok(()) } @@ -335,7 +331,7 @@ impl Module { "invalid candidate slot" ); // NOTE: This must be last as it has side-effects. - >::reserve(aux.ref_into(), Self::candidacy_bond()) + >::reserve(aux.ref_into(), Self::candidacy_bond()) .map_err(|_| "candidate has not enough funds")?; let mut candidates = candidates; @@ -359,13 +355,13 @@ impl Module { total: T::Balance, index: VoteIndex ) -> Result { - let candidate = >::lookup(candidate)?; + let candidate = >::lookup(candidate)?; ensure!(index == Self::vote_index(), "index not current"); let (_, _, expiring) = Self::next_finalise().ok_or("cannot present outside of presentation period")?; let stakes = Self::snapshoted_stakes(); let voters = Self::voters(); let bad_presentation_punishment = Self::present_slash_per_voter() * T::Balance::sa(voters.len() as u64); - ensure!(>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds"); + ensure!(>::can_slash(aux.ref_into(), bad_presentation_punishment), "presenter must have sufficient slashable funds"); let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); @@ -396,7 +392,7 @@ impl Module { } else { // we can rest assured it will be Ok since we checked `can_slash` earlier; still // better safe than sorry. - let _ = >::slash(aux.ref_into(), bad_presentation_punishment); + let _ = >::slash(aux.ref_into(), bad_presentation_punishment); Err(if dupe { "duplicate presentation" } else { "incorrect total" }) } } @@ -413,7 +409,7 @@ impl Module { /// period) to fill the seat if removal means that the desired members are not met. /// This is effective immediately. fn remove_member(who: Address) -> Result { - let who = >::lookup(who)?; + let who = >::lookup(who)?; let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() .into_iter() .filter(|i| i.0 != who) @@ -473,7 +469,7 @@ impl Module { >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); let voters = Self::voters(); - let votes = voters.iter().map(>::voting_balance).collect::>(); + let votes = voters.iter().map(>::total_balance).collect::>(); >::put(votes); // initialise leaderboard. @@ -500,7 +496,7 @@ impl Module { .take_while(|&&(b, _)| !b.is_zero()) .take(coming as usize) { - >::unreserve(w, candidacy_bond); + >::unreserve(w, candidacy_bond); } // set the new council. @@ -622,14 +618,14 @@ mod tests { pub use runtime_io::with_externalities; pub use substrate_primitives::H256; use primitives::BuildStorage; - use primitives::traits::{HasPublicAux, Identity, BlakeTwo256}; + use primitives::traits::{HasPublicAux, BlakeTwo256}; use primitives::testing::{Digest, Header}; use substrate_primitives::KeccakHasher; impl_outer_dispatch! { #[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)] pub enum Proposal { - Staking = 0, + Balances = 0, Democracy = 1, } } @@ -640,11 +636,8 @@ mod tests { impl HasPublicAux for Test { type PublicAux = u64; } - impl consensus::Trait for Test { - type PublicAux = ::PublicAux; - type SessionKey = u64; - } impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -654,54 +647,28 @@ mod tests { type Header = Header; type Event = (); } - impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = staking::Module; - type Event = (); - } - impl staking::Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; + impl balances::Trait for Test { type Balance = u64; type AccountIndex = u64; type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); type Event = (); } impl democracy::Trait for Test { type Proposal = Proposal; } - impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; - } impl Trait for Test {} pub fn new_test_ext(with_council: bool) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![], - }.build_storage().unwrap()); - t.extend(session::GenesisConfig::{ - session_length: 1, //??? or 2? - validators: vec![10, 20], - }.build_storage().unwrap()); - t.extend(staking::GenesisConfig::{ - sessions_per_era: 1, - current_era: 0, + t.extend(balances::GenesisConfig::{ balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], - intentions: vec![], - validator_count: 2, - minimum_validator_count: 0, - bonding_duration: 0, transaction_base_fee: 0, transaction_byte_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, reclaim_rebate: 0, - early_era_slash: 0, - session_reward: 0, - offline_slash_grace: 0, }.build_storage().unwrap()); t.extend(democracy::GenesisConfig::{ launch_period: 1, @@ -726,12 +693,11 @@ mod tests { cooloff_period: 2, voting_period: 1, }.build_storage().unwrap()); - t.extend(timestamp::GenesisConfig::::default().build_storage().unwrap()); t.into() } pub type System = system::Module; - pub type Staking = staking::Module; + pub type Balances = balances::Module; pub type Democracy = democracy::Module; pub type Council = Module; @@ -1045,7 +1011,7 @@ mod tests { #[test] fn double_presentations_should_be_punished() { with_externalities(&mut new_test_ext(false), || { - assert!(Staking::can_slash(&4, 10)); + assert!(Balances::can_slash(&4, 10)); System::set_block_number(4); assert_ok!(Council::submit_candidacy(&2, 0)); @@ -1061,7 +1027,7 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - assert_eq!(Staking::voting_balance(&4), 38); + assert_eq!(Balances::total_balance(&4), 38); }); } @@ -1094,8 +1060,8 @@ mod tests { assert_eq!(Council::voters(), vec![5]); assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Staking::voting_balance(&2), 17); - assert_eq!(Staking::voting_balance(&5), 53); + assert_eq!(Balances::total_balance(&2), 17); + assert_eq!(Balances::total_balance(&5), 53); }); } @@ -1153,8 +1119,8 @@ mod tests { assert_eq!(Council::voters(), vec![5]); assert_eq!(Council::approvals_of(2).len(), 0); - assert_eq!(Staking::voting_balance(&2), 17); - assert_eq!(Staking::voting_balance(&5), 53); + assert_eq!(Balances::total_balance(&2), 17); + assert_eq!(Balances::total_balance(&5), 53); }); } @@ -1254,7 +1220,7 @@ mod tests { assert_eq!(Council::voters(), vec![2, 3, 5]); assert_eq!(Council::approvals_of(4).len(), 0); - assert_eq!(Staking::voting_balance(&4), 37); + assert_eq!(Balances::total_balance(&4), 37); }); } @@ -1381,8 +1347,8 @@ mod tests { assert_ok!(Council::end_block(System::block_number())); System::set_block_number(6); - assert_eq!(Staking::free_balance(&1), 1); - assert_eq!(Staking::reserved_balance(&1), 9); + assert_eq!(Balances::free_balance(&1), 1); + assert_eq!(Balances::reserved_balance(&1), 9); assert_noop!(Council::present_winner(&1, 1.into(), 20, 0), "presenter must have sufficient slashable funds"); }); } @@ -1392,7 +1358,7 @@ mod tests { with_externalities(&mut new_test_ext(false), || { System::set_block_number(4); assert!(!Council::presentation_active()); - assert_eq!(Staking::voting_balance(&4), 40); + assert_eq!(Balances::total_balance(&4), 40); assert_ok!(Council::submit_candidacy(&2, 0)); assert_ok!(Council::submit_candidacy(&5, 1)); @@ -1403,7 +1369,7 @@ mod tests { System::set_block_number(6); assert_err!(Council::present_winner(&4, 2.into(), 80, 0), "incorrect total"); - assert_eq!(Staking::voting_balance(&4), 38); + assert_eq!(Balances::total_balance(&4), 38); }); } diff --git a/substrate/runtime/council/src/voting.rs b/substrate/runtime/council/src/voting.rs index b26df57e604f8..5569adc9e4faf 100644 --- a/substrate/runtime/council/src/voting.rs +++ b/substrate/runtime/council/src/voting.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Council voting system. @@ -227,7 +227,7 @@ mod tests { fn basic_environment_works() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - assert_eq!(Staking::bonding_duration(), 0); + assert_eq!(Balances::free_balance(&42), 0); assert_eq!(CouncilVoting::cooloff_period(), 2); assert_eq!(CouncilVoting::voting_period(), 1); assert_eq!(CouncilVoting::will_still_be_councillor_at(&1, 1), true); @@ -243,8 +243,8 @@ mod tests { }); } - fn bonding_duration_proposal(value: u64) -> Proposal { - Proposal::Staking(staking::PrivCall::set_bonding_duration(value)) + fn set_balance_proposal(value: u64) -> Proposal { + Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0)) } fn cancel_referendum_proposal(id: u32) -> Proposal { @@ -255,7 +255,7 @@ mod tests { fn referendum_cancellation_should_work_when_unanimous() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); assert_eq!(Democracy::active_referendums(), vec![(0, 4, proposal, VoteThreshold::SuperMajorityApprove)]); @@ -270,7 +270,7 @@ mod tests { System::set_block_number(2); assert_ok!(CouncilVoting::end_block(System::block_number())); assert_eq!(Democracy::active_referendums(), vec![]); - assert_eq!(Staking::bonding_duration(), 0); + assert_eq!(Balances::free_balance(&42), 0); }); } @@ -278,7 +278,7 @@ mod tests { fn referendum_cancellation_should_fail_when_not_unanimous() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); let cancellation = cancel_referendum_proposal(0); @@ -298,7 +298,7 @@ mod tests { fn referendum_cancellation_should_fail_when_abstentions() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(Democracy::internal_start_referendum(proposal.clone(), VoteThreshold::SuperMajorityApprove), 0); let cancellation = cancel_referendum_proposal(0); @@ -317,7 +317,7 @@ mod tests { fn veto_should_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::veto(&2, hash)); @@ -330,7 +330,7 @@ mod tests { fn double_veto_should_not_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::veto(&2, hash)); @@ -345,7 +345,7 @@ mod tests { fn retry_in_cooloff_should_not_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::veto(&2, hash)); @@ -359,7 +359,7 @@ mod tests { fn retry_after_cooloff_should_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::veto(&2, hash)); @@ -373,7 +373,7 @@ mod tests { System::set_block_number(4); assert_ok!(CouncilVoting::end_block(System::block_number())); assert_eq!(CouncilVoting::proposals().len(), 0); - assert_eq!(Democracy::active_referendums(), vec![(0, 7, bonding_duration_proposal(42), VoteThreshold::SimpleMajority)]); + assert_eq!(Democracy::active_referendums(), vec![(0, 7, set_balance_proposal(42), VoteThreshold::SimpleMajority)]); }); } @@ -381,7 +381,7 @@ mod tests { fn alternative_double_veto_should_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::veto(&2, hash)); @@ -398,7 +398,7 @@ mod tests { fn simple_propose_should_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); let hash = proposal.blake2_256().into(); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_eq!(CouncilVoting::proposals().len(), 1); @@ -412,7 +412,7 @@ mod tests { fn unvoted_proposal_should_expire_without_action() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_eq!(CouncilVoting::tally(&proposal.blake2_256().into()), (1, 0, 2)); assert_ok!(CouncilVoting::end_block(System::block_number())); @@ -428,7 +428,7 @@ mod tests { fn unanimous_proposal_should_expire_with_biased_referendum() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true)); assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), true)); @@ -446,7 +446,7 @@ mod tests { fn majority_proposal_should_expire_with_unbiased_referendum() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_ok!(CouncilVoting::propose(&1, Box::new(proposal.clone()))); assert_ok!(CouncilVoting::vote(&2, proposal.blake2_256().into(), true)); assert_ok!(CouncilVoting::vote(&3, proposal.blake2_256().into(), false)); @@ -464,7 +464,7 @@ mod tests { fn propose_by_public_should_not_work() { with_externalities(&mut new_test_ext(true), || { System::set_block_number(1); - let proposal = bonding_duration_proposal(42); + let proposal = set_balance_proposal(42); assert_noop!(CouncilVoting::propose(&4, Box::new(proposal)), "proposer would not be on council"); }); } diff --git a/substrate/runtime/democracy/Cargo.toml b/substrate/runtime/democracy/Cargo.toml index bf144eece6346..602bf1adb4190 100644 --- a/substrate/runtime/democracy/Cargo.toml +++ b/substrate/runtime/democracy/Cargo.toml @@ -15,14 +15,10 @@ substrate-runtime-std = { path = "../../runtime-std", default_features = false } substrate-runtime-io = { path = "../../runtime-io", default_features = false } substrate-runtime-support = { path = "../../runtime-support", default_features = false } substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } substrate-runtime-consensus = { path = "../consensus", default_features = false } -substrate-runtime-session = { path = "../session", default_features = false } -substrate-runtime-staking = { path = "../staking", default_features = false } substrate-runtime-system = { path = "../system", default_features = false } -[dev-dependencies] -substrate-runtime-timestamp = { path = "../timestamp" } - [features] default = ["std"] std = [ @@ -35,8 +31,6 @@ std = [ "substrate-runtime-io/std", "substrate-runtime-support/std", "substrate-runtime-primitives/std", - "substrate-runtime-consensus/std", - "substrate-runtime-session/std", - "substrate-runtime-staking/std", + "substrate-runtime-balances/std", "substrate-runtime-system/std", ] diff --git a/substrate/runtime/democracy/src/lib.rs b/substrate/runtime/democracy/src/lib.rs index bc04fe877acd0..7f3846ad9755f 100644 --- a/substrate/runtime/democracy/src/lib.rs +++ b/substrate/runtime/democracy/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Democratic system: Handles administration of general stakeholder voting. @@ -38,12 +38,8 @@ extern crate substrate_runtime_support; extern crate substrate_codec as codec; extern crate substrate_runtime_io as runtime_io; extern crate substrate_runtime_primitives as primitives; -extern crate substrate_runtime_consensus as consensus; -extern crate substrate_runtime_session as session; -extern crate substrate_runtime_staking as staking; +extern crate substrate_runtime_balances as balances; extern crate substrate_runtime_system as system; -#[cfg(test)] -extern crate substrate_runtime_timestamp as timestamp; use rstd::prelude::*; use rstd::result; @@ -62,7 +58,7 @@ pub type PropIndex = u32; /// A referendum index. pub type ReferendumIndex = u32; -pub trait Trait: staking::Trait + Sized { +pub trait Trait: balances::Trait + Sized { type Proposal: Parameter + Dispatchable + IsSubType> + MaybeSerializeDebug; } @@ -152,7 +148,7 @@ impl Module { /// Get the voters for the current proposal. pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance) { Self::voters_for(ref_index).iter() - .map(|a| (>::voting_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/)) + .map(|a| (>::total_balance(a), Self::vote_of((ref_index, a.clone())).unwrap_or(false)/*defensive only: all items come from `voters`; for an item to be in `voters` there must be a vote registered; qed*/)) .map(|(bal, vote)| if vote { (bal, Zero::zero()) } else { (Zero::zero(), bal) }) .fold((Zero::zero(), Zero::zero()), |(a, b), (c, d)| (a + c, b + d)) } @@ -162,7 +158,7 @@ impl Module { /// Propose a sensitive action to be taken. fn propose(aux: &T::PublicAux, proposal: Box, value: T::Balance) -> Result { ensure!(value >= Self::minimum_deposit(), "value too low"); - >::reserve(aux.ref_into(), value) + >::reserve(aux.ref_into(), value) .map_err(|_| "proposer's balance too low")?; let index = Self::public_prop_count(); @@ -179,7 +175,7 @@ impl Module { fn second(aux: &T::PublicAux, proposal: PropIndex) -> Result { let mut deposit = Self::deposit_of(proposal) .ok_or("can only second an existing proposal")?; - >::reserve(aux.ref_into(), deposit.0) + >::reserve(aux.ref_into(), deposit.0) .map_err(|_| "seconder's balance too low")?; deposit.1.push(aux.ref_into().clone()); >::insert(proposal, deposit); @@ -190,7 +186,7 @@ impl Module { /// false would be a vote to keep the status quo.. fn vote(aux: &T::PublicAux, ref_index: ReferendumIndex, approve_proposal: bool) -> Result { ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); - ensure!(!>::voting_balance(aux.ref_into()).is_zero(), + ensure!(!>::total_balance(aux.ref_into()).is_zero(), "transactor must have balance to signal approval."); if !>::exists(&(ref_index, aux.ref_into().clone())) { let mut voters = Self::voters_for(ref_index); @@ -268,7 +264,7 @@ impl Module { if let Some((deposit, depositors)) = >::take(prop_index) {//: (T::Balance, Vec) = // refund depositors for d in &depositors { - >::unreserve(d, deposit); + >::unreserve(d, deposit); } >::put(public_props); Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove)?; @@ -281,7 +277,7 @@ impl Module { // tally up votes for any expiring referenda. for (index, _, proposal, vote_threshold) in Self::maturing_referendums_at(now) { let (approve, against) = Self::tally(index); - let total_stake = >::total_stake(); + let total_stake = >::total_stake(); Self::clear_referendum(index); if vote_threshold.approved(approve, against, total_stake) { proposal.dispatch()?; @@ -319,14 +315,6 @@ impl GenesisConfig { minimum_deposit: T::Balance::sa(1), } } - - pub fn extended() -> Self { - GenesisConfig { - launch_period: T::BlockNumber::sa(1), - voting_period: T::BlockNumber::sa(3), - minimum_deposit: T::Balance::sa(1), - } - } } #[cfg(any(feature = "std", test))] @@ -363,16 +351,14 @@ mod tests { use runtime_io::with_externalities; use substrate_primitives::{H256, KeccakHasher}; use primitives::BuildStorage; - use primitives::traits::{HasPublicAux, Identity, BlakeTwo256}; + use primitives::traits::{HasPublicAux, BlakeTwo256}; use primitives::testing::{Digest, Header}; - use session::OnSessionChange; impl_outer_dispatch! { #[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialEq)] pub enum Proposal { - Session = 0, - Staking = 1, - Democracy = 2, + Balances = 0, + Democracy = 1, } } @@ -382,11 +368,8 @@ mod tests { impl HasPublicAux for Test { type PublicAux = u64; } - impl consensus::Trait for Test { - type PublicAux = ::PublicAux; - type SessionKey = u64; - } impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -396,66 +379,38 @@ mod tests { type Header = Header; type Event = (); } - impl session::Trait for Test { - type ConvertAccountIdToSessionKey = Identity; - type OnSessionChange = staking::Module; - type Event = (); - } - impl staking::Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; + impl balances::Trait for Test { type Balance = u64; type AccountIndex = u64; type OnFreeBalanceZero = (); + type EnsureAccountLiquid = (); type Event = (); } - impl timestamp::Trait for Test { - const TIMESTAMP_SET_POSITION: u32 = 0; - type Moment = u64; - } impl Trait for Test { type Proposal = Proposal; } fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(consensus::GenesisConfig::{ - code: vec![], - authorities: vec![], - }.build_storage().unwrap()); - t.extend(session::GenesisConfig::{ - session_length: 1, //??? or 2? - validators: vec![10, 20], - }.build_storage().unwrap()); - t.extend(staking::GenesisConfig::{ - sessions_per_era: 1, - current_era: 0, + t.extend(balances::GenesisConfig::{ balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], - intentions: vec![], - validator_count: 2, - minimum_validator_count: 0, - bonding_duration: 3, transaction_base_fee: 0, transaction_byte_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, reclaim_rebate: 0, - early_era_slash: 0, - session_reward: 0, - offline_slash_grace: 0, }.build_storage().unwrap()); t.extend(GenesisConfig::{ launch_period: 1, voting_period: 1, minimum_deposit: 1, }.build_storage().unwrap()); - t.extend(timestamp::GenesisConfig::::default().build_storage().unwrap()); t.into() } type System = system::Module; - type Session = session::Module; - type Staking = staking::Module; + type Balances = balances::Module; type Democracy = Module; #[test] @@ -465,22 +420,26 @@ mod tests { assert_eq!(Democracy::voting_period(), 1); assert_eq!(Democracy::minimum_deposit(), 1); assert_eq!(Democracy::referendum_count(), 0); - assert_eq!(Staking::sessions_per_era(), 1); - assert_eq!(Staking::total_stake(), 210); + assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::total_stake(), 210); }); } - fn propose_sessions_per_era(who: u64, value: u64, locked: u64) -> super::Result { - Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_sessions_per_era(value))), locked) + fn set_balance_proposal(value: u64) -> Proposal { + Proposal::Balances(balances::PrivCall::set_balance(balances::address::Address::Id(42), value, 0)) + } + + fn propose_set_balance(who: u64, value: u64, locked: u64) -> super::Result { + Democracy::propose(&who, Box::new(set_balance_proposal(value)), locked) } #[test] fn locked_for_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_ok!(propose_sessions_per_era(1, 2, 2)); - assert_ok!(propose_sessions_per_era(1, 4, 4)); - assert_ok!(propose_sessions_per_era(1, 3, 3)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); assert_eq!(Democracy::locked_for(0), Some(2)); assert_eq!(Democracy::locked_for(1), Some(4)); assert_eq!(Democracy::locked_for(2), Some(3)); @@ -491,7 +450,7 @@ mod tests { fn single_proposal_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_ok!(propose_sessions_per_era(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); System::set_block_number(2); @@ -504,9 +463,8 @@ mod tests { assert_eq!(Democracy::tally(r), (10, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 2); + assert_eq!(Balances::free_balance(&42), 2); }); } @@ -514,14 +472,14 @@ mod tests { fn deposit_for_proposals_should_be_taken() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_ok!(propose_sessions_per_era(1, 2, 5)); + assert_ok!(propose_set_balance(1, 2, 5)); assert_ok!(Democracy::second(&2, 0)); assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); - assert_eq!(Staking::free_balance(&1), 5); - assert_eq!(Staking::free_balance(&2), 15); - assert_eq!(Staking::free_balance(&5), 35); + assert_eq!(Balances::free_balance(&1), 5); + assert_eq!(Balances::free_balance(&2), 15); + assert_eq!(Balances::free_balance(&5), 35); }); } @@ -529,15 +487,15 @@ mod tests { fn deposit_for_proposals_should_be_returned() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_ok!(propose_sessions_per_era(1, 2, 5)); + assert_ok!(propose_set_balance(1, 2, 5)); assert_ok!(Democracy::second(&2, 0)); assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); assert_ok!(Democracy::second(&5, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - assert_eq!(Staking::free_balance(&1), 10); - assert_eq!(Staking::free_balance(&2), 20); - assert_eq!(Staking::free_balance(&5), 50); + assert_eq!(Balances::free_balance(&1), 10); + assert_eq!(Balances::free_balance(&2), 20); + assert_eq!(Balances::free_balance(&5), 50); }); } @@ -545,7 +503,7 @@ mod tests { fn proposal_with_deposit_below_minimum_should_not_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_noop!(propose_sessions_per_era(1, 2, 0), "value too low"); + assert_noop!(propose_set_balance(1, 2, 0), "value too low"); }); } @@ -553,7 +511,7 @@ mod tests { fn poor_proposer_should_not_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_noop!(propose_sessions_per_era(1, 2, 11), "proposer\'s balance too low"); + assert_noop!(propose_set_balance(1, 2, 11), "proposer\'s balance too low"); }); } @@ -561,53 +519,41 @@ mod tests { fn poor_seconder_should_not_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - assert_ok!(propose_sessions_per_era(2, 2, 11)); + assert_ok!(propose_set_balance(2, 2, 11)); assert_noop!(Democracy::second(&1, 0), "seconder\'s balance too low"); }); } - fn propose_bonding_duration(who: u64, value: u64, locked: u64) -> super::Result { - Democracy::propose(&who, Box::new(Proposal::Staking(staking::PrivCall::set_bonding_duration(value))), locked) - } - #[test] fn runners_up_should_come_after() { with_externalities(&mut new_test_ext(), || { System::set_block_number(0); - assert_ok!(propose_bonding_duration(1, 2, 2)); - assert_ok!(propose_bonding_duration(1, 4, 4)); - assert_ok!(propose_bonding_duration(1, 3, 3)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); System::set_block_number(1); assert_ok!(Democracy::vote(&1, 0, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::bonding_duration(), 4); + assert_eq!(Balances::free_balance(&42), 4); System::set_block_number(2); assert_ok!(Democracy::vote(&1, 1, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::bonding_duration(), 3); + assert_eq!(Balances::free_balance(&42), 3); System::set_block_number(3); assert_ok!(Democracy::vote(&1, 2, true)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::bonding_duration(), 2); }); } - fn sessions_per_era_proposal(value: u64) -> Proposal { - Proposal::Staking(staking::PrivCall::set_sessions_per_era(value)) - } - #[test] fn simple_passing_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&1, r, true)); assert_eq!(Democracy::voters_for(r), vec![1]); @@ -615,9 +561,8 @@ mod tests { assert_eq!(Democracy::tally(r), (10, 0)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 2); + assert_eq!(Balances::free_balance(&42), 2); }); } @@ -625,14 +570,13 @@ mod tests { fn cancel_referendum_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&1, r, true)); assert_ok!(Democracy::cancel_referendum(r)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 1); + assert_eq!(Balances::free_balance(&42), 0); }); } @@ -640,7 +584,7 @@ mod tests { fn simple_failing_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&1, r, false)); assert_eq!(Democracy::voters_for(r), vec![1]); @@ -648,9 +592,8 @@ mod tests { assert_eq!(Democracy::tally(r), (0, 10)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 1); + assert_eq!(Balances::free_balance(&42), 0); }); } @@ -658,7 +601,7 @@ mod tests { fn controversial_voting_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&1, r, true)); assert_ok!(Democracy::vote(&2, r, false)); assert_ok!(Democracy::vote(&3, r, false)); @@ -669,9 +612,8 @@ mod tests { assert_eq!(Democracy::tally(r), (110, 100)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 2); + assert_eq!(Balances::free_balance(&42), 2); }); } @@ -679,27 +621,26 @@ mod tests { fn controversial_low_turnout_voting_should_work() { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&5, r, false)); assert_ok!(Democracy::vote(&6, r, true)); assert_eq!(Democracy::tally(r), (60, 50)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 1); + assert_eq!(Balances::free_balance(&42), 0); }); } #[test] fn passing_low_turnout_voting_should_work() { with_externalities(&mut new_test_ext(), || { - assert_eq!(Staking::era_length(), 1); - assert_eq!(Staking::total_stake(), 210); + assert_eq!(Balances::free_balance(&42), 0); + assert_eq!(Balances::total_stake(), 210); System::set_block_number(1); - let r = Democracy::inject_referendum(1, sessions_per_era_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); + let r = Democracy::inject_referendum(1, set_balance_proposal(2), VoteThreshold::SuperMajorityApprove).unwrap(); assert_ok!(Democracy::vote(&4, r, true)); assert_ok!(Democracy::vote(&5, r, false)); assert_ok!(Democracy::vote(&6, r, true)); @@ -707,9 +648,8 @@ mod tests { assert_eq!(Democracy::tally(r), (100, 50)); assert_eq!(Democracy::end_block(System::block_number()), Ok(())); - Staking::on_session_change(0, true); - assert_eq!(Staking::era_length(), 2); + assert_eq!(Balances::free_balance(&42), 2); }); } } diff --git a/substrate/runtime/democracy/src/vote_threshold.rs b/substrate/runtime/democracy/src/vote_threshold.rs index 0027f7c0060e0..ce46149222055 100644 --- a/substrate/runtime/democracy/src/vote_threshold.rs +++ b/substrate/runtime/democracy/src/vote_threshold.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Voting thresholds. diff --git a/substrate/runtime/executive/Cargo.toml b/substrate/runtime/executive/Cargo.toml index 20d913e92553a..74a10d18f8642 100644 --- a/substrate/runtime/executive/Cargo.toml +++ b/substrate/runtime/executive/Cargo.toml @@ -17,6 +17,7 @@ substrate-runtime-system = { path = "../system", default_features = false } [dev-dependencies] substrate-primitives = { path = "../../primitives" } +substrate-runtime-balances = { path = "../balances" } substrate-runtime-session = { path = "../session" } substrate-runtime-staking = { path = "../staking" } substrate-runtime-consensus = { path = "../consensus" } diff --git a/substrate/runtime/executive/src/lib.rs b/substrate/runtime/executive/src/lib.rs index 4f7d6bd27822e..1d832b73e3fbd 100644 --- a/substrate/runtime/executive/src/lib.rs +++ b/substrate/runtime/executive/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Executive: Handles all of the top-level stuff; essentially just executing blocks/extrinsics. @@ -53,6 +53,9 @@ extern crate substrate_runtime_consensus as consensus; #[cfg(test)] extern crate substrate_runtime_session as session; +#[cfg(test)] +extern crate substrate_runtime_balances as balances; + #[cfg(test)] extern crate substrate_runtime_staking as staking; @@ -224,7 +227,7 @@ impl< #[cfg(test)] mod tests { use super::*; - use staking::Call; + use balances::Call; use runtime_io::with_externalities; use substrate_primitives::{H256, KeccakHasher}; use primitives::BuildStorage; @@ -242,7 +245,7 @@ mod tests { impl_outer_event!{ pub enum MetaEvent for Test { - session, staking + balances, session, staking } } @@ -253,10 +256,19 @@ mod tests { type PublicAux = u64; } impl consensus::Trait for Test { - type PublicAux = ::PublicAux; + const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = u64; + type OnOfflineValidator = staking::Module; + } + impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = staking::Module; + type EnsureAccountLiquid = staking::Module; + type Event = MetaEvent; } impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = substrate_primitives::H256; @@ -272,10 +284,6 @@ mod tests { type Event = MetaEvent; } impl staking::Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); type Event = MetaEvent; } impl timestamp::Trait for Test { @@ -284,25 +292,27 @@ mod tests { } type TestXt = primitives::testing::TestXt>; - type Executive = super::Executive, NullLookup, staking::Module, (session::Module, staking::Module)>; + type Executive = super::Executive, NullLookup, balances::Module, (session::Module, staking::Module)>; #[test] fn staking_balance_transfer_dispatch_works() { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); - t.extend(staking::GenesisConfig:: { - sessions_per_era: 0, - current_era: 0, + t.extend(balances::GenesisConfig:: { balances: vec![(1, 111)], - intentions: vec![], - validator_count: 0, - minimum_validator_count: 0, - bonding_duration: 0, transaction_base_fee: 10, transaction_byte_fee: 0, existential_deposit: 0, transfer_fee: 0, creation_fee: 0, reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(staking::GenesisConfig:: { + sessions_per_era: 0, + current_era: 0, + intentions: vec![], + validator_count: 0, + minimum_validator_count: 0, + bonding_duration: 0, early_era_slash: 0, session_reward: 0, offline_slash_grace: 0, @@ -312,13 +322,14 @@ mod tests { with_externalities(&mut t, || { Executive::initialise_block(&Header::new(1, H256::default(), H256::default(), [69u8; 32].into(), Digest::default())); Executive::apply_extrinsic(xt).unwrap(); - assert_eq!(>::voting_balance(&1), 32); - assert_eq!(>::voting_balance(&2), 69); + assert_eq!(>::total_balance(&1), 32); + assert_eq!(>::total_balance(&2), 69); }); } fn new_test_ext() -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); + t.extend(balances::GenesisConfig::::default().build_storage().unwrap()); t.extend(consensus::GenesisConfig::::default().build_storage().unwrap()); t.extend(session::GenesisConfig::::default().build_storage().unwrap()); t.extend(staking::GenesisConfig::::default().build_storage().unwrap()); @@ -336,7 +347,7 @@ mod tests { // Blake // state_root: hex!("02532989c613369596025dfcfc821339fc9861987003924913a5a1382f87034a").into(), // Keccak - state_root: hex!("e576ed2adacdc09b61844b5106bfaa18d2a4bfd7feb56d7af97c3421cdefca48").into(), + state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(), extrinsics_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), digest: Digest { logs: vec![], }, }, @@ -370,7 +381,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("e576ed2adacdc09b61844b5106bfaa18d2a4bfd7feb56d7af97c3421cdefca48").into(), + state_root: hex!("ffe27b4c3a8b421fa10592be61fb28eca7ebbe04cbfa99cdda9f703f35522569").into(), extrinsics_root: [0u8; 32].into(), digest: Digest { logs: vec![], }, }, diff --git a/substrate/runtime/primitives/src/generic.rs b/substrate/runtime/primitives/src/generic.rs index f1a9448c8fafc..efcc52fbecb49 100644 --- a/substrate/runtime/primitives/src/generic.rs +++ b/substrate/runtime/primitives/src/generic.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Generic implementations of Extrinsic/Header/Block. diff --git a/substrate/runtime/primitives/src/lib.rs b/substrate/runtime/primitives/src/lib.rs index a1ff760f7608f..87553942b921e 100644 --- a/substrate/runtime/primitives/src/lib.rs +++ b/substrate/runtime/primitives/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! System manager: Handles all of the top-level stuff; executing block/transaction, setting code //! and depositing logs. diff --git a/substrate/runtime/primitives/src/testing.rs b/substrate/runtime/primitives/src/testing.rs index eab978d3b8951..b1dcef2eadbef 100644 --- a/substrate/runtime/primitives/src/testing.rs +++ b/substrate/runtime/primitives/src/testing.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Testing utilities. diff --git a/substrate/runtime/primitives/src/traits.rs b/substrate/runtime/primitives/src/traits.rs index 4026fa7cf7670..b64aa678f2867 100644 --- a/substrate/runtime/primitives/src/traits.rs +++ b/substrate/runtime/primitives/src/traits.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Primitives for the runtime modules. diff --git a/substrate/runtime/session/src/lib.rs b/substrate/runtime/session/src/lib.rs index 1530723907086..75dad0adae76d 100644 --- a/substrate/runtime/session/src/lib.rs +++ b/substrate/runtime/session/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Session manager: is told the validators and allows them to manage their session keys for the //! consensus module. @@ -302,10 +302,12 @@ mod tests { type PublicAux = u64; } impl consensus::Trait for Test { - type PublicAux = ::PublicAux; + const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = u64; + type OnOfflineValidator = (); } impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -327,7 +329,6 @@ mod tests { type System = system::Module; type Consensus = consensus::Module; - type Timestamp = timestamp::Module; type Session = Module; fn new_test_ext() -> runtime_io::TestExternalities { diff --git a/substrate/runtime/staking/Cargo.toml b/substrate/runtime/staking/Cargo.toml index 7cffe09d01217..d4214862a73d3 100644 --- a/substrate/runtime/staking/Cargo.toml +++ b/substrate/runtime/staking/Cargo.toml @@ -17,6 +17,7 @@ substrate-runtime-io = { path = "../../runtime-io", default_features = false } substrate-runtime-sandbox = { path = "../../runtime-sandbox", default_features = false } substrate-runtime-support = { path = "../../runtime-support", default_features = false } substrate-runtime-primitives = { path = "../primitives", default_features = false } +substrate-runtime-balances = { path = "../balances", default_features = false } substrate-runtime-consensus = { path = "../consensus", default_features = false } substrate-runtime-system = { path = "../system", default_features = false } substrate-runtime-session = { path = "../session", default_features = false } @@ -40,6 +41,7 @@ std = [ "substrate-runtime-sandbox/std", "substrate-runtime-support/std", "substrate-runtime-primitives/std", + "substrate-runtime-balances/std", "substrate-runtime-session/std", "substrate-runtime-system/std", "substrate-runtime-timestamp/std" diff --git a/substrate/runtime/staking/src/genesis_config.rs b/substrate/runtime/staking/src/genesis_config.rs index 8da72bf1d92ab..7577e125c259d 100644 --- a/substrate/runtime/staking/src/genesis_config.rs +++ b/substrate/runtime/staking/src/genesis_config.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Build a staking genesis block. @@ -21,15 +21,12 @@ use std::collections::HashMap; use rstd::prelude::*; use codec::Encode; -use runtime_support::{StorageValue, StorageMap}; -use primitives::traits::{Zero, As}; +use runtime_support::StorageValue; +use primitives::traits::As; use substrate_primitives::KeccakHasher; use {runtime_io, primitives}; -use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra, - BondingDuration, CreationFee, TransferFee, ReclaimRebate, - ExistentialDeposit, TransactionByteFee, TransactionBaseFee, TotalStake, - SessionsPerEra, ValidatorCount, FreeBalance, SessionReward, EarlyEraSlash, - OfflineSlashGrace, MinimumValidatorCount}; +use super::{Trait, Intentions, CurrentEra, OfflineSlashGrace, MinimumValidatorCount, + BondingDuration, SessionsPerEra, ValidatorCount, SessionReward, EarlyEraSlash}; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -37,90 +34,24 @@ use super::{Trait, ENUM_SET_SIZE, EnumSet, NextEnumSet, Intentions, CurrentEra, pub struct GenesisConfig { pub sessions_per_era: T::BlockNumber, pub current_era: T::BlockNumber, - pub balances: Vec<(T::AccountId, T::Balance)>, pub intentions: Vec, pub validator_count: u32, pub minimum_validator_count: u32, pub bonding_duration: T::BlockNumber, - pub transaction_base_fee: T::Balance, - pub transaction_byte_fee: T::Balance, - pub transfer_fee: T::Balance, - pub creation_fee: T::Balance, - pub reclaim_rebate: T::Balance, - pub existential_deposit: T::Balance, pub session_reward: T::Balance, pub early_era_slash: T::Balance, pub offline_slash_grace: u32, } -impl GenesisConfig where T::AccountId: From { - pub fn simple() -> Self { - GenesisConfig { - sessions_per_era: T::BlockNumber::sa(2), - current_era: T::BlockNumber::sa(0), - balances: vec![(T::AccountId::from(1), T::Balance::sa(111))], - intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)], - validator_count: 3, - minimum_validator_count: 1, - bonding_duration: T::BlockNumber::sa(0), - transaction_base_fee: T::Balance::sa(0), - transaction_byte_fee: T::Balance::sa(0), - transfer_fee: T::Balance::sa(0), - creation_fee: T::Balance::sa(0), - existential_deposit: T::Balance::sa(0), - reclaim_rebate: T::Balance::sa(0), - session_reward: T::Balance::sa(0), - early_era_slash: T::Balance::sa(0), - offline_slash_grace: 1, - } - } - - pub fn extended() -> Self { - GenesisConfig { - sessions_per_era: T::BlockNumber::sa(3), - current_era: T::BlockNumber::sa(1), - balances: vec![ - (T::AccountId::from(1), T::Balance::sa(10)), - (T::AccountId::from(2), T::Balance::sa(20)), - (T::AccountId::from(3), T::Balance::sa(30)), - (T::AccountId::from(4), T::Balance::sa(40)), - (T::AccountId::from(5), T::Balance::sa(50)), - (T::AccountId::from(6), T::Balance::sa(60)), - (T::AccountId::from(7), T::Balance::sa(1)) - ], - intentions: vec![T::AccountId::from(1), T::AccountId::from(2), T::AccountId::from(3)], - validator_count: 3, - minimum_validator_count: 1, - bonding_duration: T::BlockNumber::sa(0), - transaction_base_fee: T::Balance::sa(1), - transaction_byte_fee: T::Balance::sa(0), - transfer_fee: T::Balance::sa(0), - creation_fee: T::Balance::sa(0), - existential_deposit: T::Balance::sa(0), - reclaim_rebate: T::Balance::sa(0), - session_reward: T::Balance::sa(0), - early_era_slash: T::Balance::sa(0), - offline_slash_grace: 1, - } - } -} - impl Default for GenesisConfig { fn default() -> Self { GenesisConfig { sessions_per_era: T::BlockNumber::sa(1000), current_era: T::BlockNumber::sa(0), - balances: vec![], intentions: vec![], validator_count: 0, minimum_validator_count: 0, bonding_duration: T::BlockNumber::sa(1000), - transaction_base_fee: T::Balance::sa(0), - transaction_byte_fee: T::Balance::sa(0), - transfer_fee: T::Balance::sa(0), - creation_fee: T::Balance::sa(0), - existential_deposit: T::Balance::sa(0), - reclaim_rebate: T::Balance::sa(0), session_reward: T::Balance::sa(0), early_era_slash: T::Balance::sa(0), offline_slash_grace: 0, @@ -130,36 +61,17 @@ impl Default for GenesisConfig { impl primitives::BuildStorage for GenesisConfig { fn build_storage(self) -> ::std::result::Result, Vec>, String> { - let total_stake: T::Balance = self.balances.iter().fold(Zero::zero(), |acc, &(_, n)| acc + n); - - let mut r: runtime_io::TestExternalities = map![ - Self::hash(>::key()).to_vec() => T::AccountIndex::sa(self.balances.len() / ENUM_SET_SIZE).encode(), + let r: runtime_io::TestExternalities = map![ Self::hash(>::key()).to_vec() => self.intentions.encode(), Self::hash(>::key()).to_vec() => self.sessions_per_era.encode(), Self::hash(>::key()).to_vec() => self.validator_count.encode(), Self::hash(>::key()).to_vec() => self.minimum_validator_count.encode(), Self::hash(>::key()).to_vec() => self.bonding_duration.encode(), - Self::hash(>::key()).to_vec() => self.transaction_base_fee.encode(), - Self::hash(>::key()).to_vec() => self.transaction_byte_fee.encode(), - Self::hash(>::key()).to_vec() => self.transfer_fee.encode(), - Self::hash(>::key()).to_vec() => self.creation_fee.encode(), - Self::hash(>::key()).to_vec() => self.existential_deposit.encode(), - Self::hash(>::key()).to_vec() => self.reclaim_rebate.encode(), Self::hash(>::key()).to_vec() => self.current_era.encode(), Self::hash(>::key()).to_vec() => self.session_reward.encode(), Self::hash(>::key()).to_vec() => self.early_era_slash.encode(), - Self::hash(>::key()).to_vec() => self.offline_slash_grace.encode(), - Self::hash(>::key()).to_vec() => total_stake.encode() + Self::hash(>::key()).to_vec() => self.offline_slash_grace.encode() ]; - - let ids: Vec<_> = self.balances.iter().map(|x| x.0.clone()).collect(); - for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE { - r.insert(Self::hash(&>::key_for(T::AccountIndex::sa(i))).to_vec(), - ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode()); - } - for (who, value) in self.balances.into_iter() { - r.insert(Self::hash(&>::key_for(who)).to_vec(), value.encode()); - } Ok(r.into()) } } diff --git a/substrate/runtime/staking/src/lib.rs b/substrate/runtime/staking/src/lib.rs index 92c8f47faa244..3c3c2cd9df138 100644 --- a/substrate/runtime/staking/src/lib.rs +++ b/substrate/runtime/staking/src/lib.rs @@ -1,20 +1,20 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . -//! Staking manager: Handles balances and periodically determines the best set of validators. +//! Staking manager: Periodically determines the best set of validators. #![cfg_attr(not(feature = "std"), no_std)] @@ -41,26 +41,23 @@ extern crate substrate_codec as codec; extern crate substrate_primitives; extern crate substrate_runtime_io as runtime_io; extern crate substrate_runtime_primitives as primitives; +extern crate substrate_runtime_balances as balances; extern crate substrate_runtime_consensus as consensus; extern crate substrate_runtime_sandbox as sandbox; extern crate substrate_runtime_session as session; extern crate substrate_runtime_system as system; extern crate substrate_runtime_timestamp as timestamp; -#[cfg(test)] use std::fmt::Debug; use rstd::prelude::*; -use rstd::{cmp, result}; -use codec::{Encode, Decode, Codec, Input, Output}; -use runtime_support::{StorageValue, StorageMap, Parameter}; +use runtime_support::{Parameter, StorageValue, StorageMap}; use runtime_support::dispatch::Result; use session::OnSessionChange; -use primitives::traits::{Zero, One, Bounded, RefInto, SimpleArithmetic, Executable, MakePayment, - As, AuxLookup, Member, CheckedAdd, CheckedSub, MaybeEmpty}; -use address::Address as RawAddress; +use primitives::traits::{Zero, One, Bounded, RefInto, Executable, + As, AuxLookup}; +use balances::address::Address; mod mock; -pub mod address; mod tests; mod genesis_config; @@ -69,44 +66,17 @@ pub use genesis_config::GenesisConfig; const DEFAULT_MINIMUM_VALIDATOR_COUNT: usize = 4; -/// Number of account IDs stored per enum set. -const ENUM_SET_SIZE: usize = 64; - -/// The byte to identify intention to reclaim an existing account index. -const RECLAIM_INDEX_MAGIC: usize = 0x69; - -pub type Address = RawAddress<::AccountId, ::AccountIndex>; - pub type Event = RawEvent< - ::Balance, - ::AccountId, - ::AccountIndex + ::Balance, + ::AccountId >; -#[cfg(test)] -#[derive(Debug, PartialEq, Clone)] -pub enum LockStatus { - Liquid, - LockedUntil(BlockNumber), - Staked, -} - -#[cfg(not(test))] #[derive(PartialEq, Clone)] -pub enum LockStatus { +#[cfg_attr(test, derive(Debug))] +pub enum LockStatus { Liquid, LockedUntil(BlockNumber), - Staked, -} - -/// The account was the given id was killed. -pub trait OnFreeBalanceZero { - /// The account was the given id was killed. - fn on_free_balance_zero(who: &AccountId); -} - -impl OnFreeBalanceZero for () { - fn on_free_balance_zero(_who: &AccountId) {} + Bonded, } /// Preference of what happens on a slash event. @@ -125,21 +95,7 @@ impl Default for SlashPreference { } } -pub trait Trait: system::Trait + session::Trait { - /// The allowed extrinsic position for `missed_proposal` inherent. - const NOTE_MISSED_PROPOSAL_POSITION: u32; - - /// The balance of an account. - type Balance: Parameter + SimpleArithmetic + Codec + Default + Copy + As + As + As; - /// Type used for storing an account's index; implies the maximum number of accounts the system - /// can hold. - type AccountIndex: Parameter + Member + Codec + SimpleArithmetic + As + As + As + As + As + Copy; - /// 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; - +pub trait Trait: balances::Trait + session::Trait { /// The overarching event type. type Event: From> + Into<::Event>; } @@ -149,13 +105,11 @@ decl_module! { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Call where aux: T::PublicAux { - fn transfer(aux, dest: RawAddress, value: T::Balance) -> Result = 0; - fn stake(aux) -> Result = 1; - fn unstake(aux, intentions_index: u32) -> Result = 2; - fn nominate(aux, target: RawAddress) -> Result = 3; - fn unnominate(aux, target_index: u32) -> Result = 4; - fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 5; - fn note_missed_proposal(aux, offline_val_indices: Vec) -> Result = 6; + fn stake(aux) -> Result = 0; + fn unstake(aux, intentions_index: u32) -> Result = 1; + fn nominate(aux, target: Address) -> Result = 2; + fn unnominate(aux, target_index: u32) -> Result = 3; + fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 4; } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -165,14 +119,13 @@ decl_module! { fn set_validator_count(new: u32) -> Result = 2; fn force_new_era(apply_rewards: bool) -> Result = 3; fn set_offline_slash_grace(new: u32) -> Result = 4; - fn set_balance(who: RawAddress, free: T::Balance, reserved: T::Balance) -> Result = 5; } } /// An event in this module. #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] #[derive(Encode, Decode, PartialEq, Eq, Clone)] -pub enum RawEvent { +pub enum RawEvent { /// All validators have been rewarded by the given balance. Reward(Balance), /// One validator (and their nominators) has been given a offline-warning (they're still within @@ -180,47 +133,28 @@ pub enum RawEvent { OfflineWarning(AccountId, u32), /// One validator (and their nominators) has been slashed by the given amount. OfflineSlash(AccountId, Balance), - /// A new account was created. - NewAccount(AccountId, AccountIndex, NewAccountOutcome), - /// An account was reaped. - ReapedAccount(AccountId), } -impl From> for () { - fn from(_: RawEvent) -> () { () } +impl From> for () { + fn from(_: RawEvent) -> () { () } } decl_storage! { trait Store for Module as Staking { - // The length of the bonding duration in eras. - pub BondingDuration get(bonding_duration): required T::BlockNumber; // The ideal number of staking participants. pub ValidatorCount get(validator_count): required u32; // Minimum number of staking participants before emergency conditions are imposed. pub MinimumValidatorCount: u32; // The length of a staking era in sessions. pub SessionsPerEra get(sessions_per_era): required T::BlockNumber; - // The total amount of stake on the system. - // TODO: this doesn't actually track total stake yet - it should do. - pub TotalStake get(total_stake): required T::Balance; - // The fee to be paid for making a transaction; the base. - pub TransactionBaseFee get(transaction_base_fee): required T::Balance; - // The fee to be paid for making a transaction; the per-byte portion. - pub TransactionByteFee get(transaction_byte_fee): required T::Balance; - // The minimum amount allowed to keep an account open. - pub ExistentialDeposit get(existential_deposit): required T::Balance; - // The amount credited to a destination's account whose index was reclaimed. - pub ReclaimRebate get(reclaim_rebate): required T::Balance; - // The fee required to make a transfer. - pub TransferFee get(transfer_fee): required T::Balance; - // The fee required to create an account. At least as big as ReclaimRebate. - pub CreationFee get(creation_fee): required T::Balance; // Maximum reward, per validator, that is provided per acceptable session. pub SessionReward get(session_reward): required T::Balance; // Slash, per validator that is taken per abnormal era end. pub EarlyEraSlash get(early_era_slash): required T::Balance; // Number of instances of offline reports before slashing begins for validators. pub OfflineSlashGrace get(offline_slash_grace): default u32; + // The length of the bonding duration in blocks. + pub BondingDuration get(bonding_duration): required T::BlockNumber; // The current era index. pub CurrentEra get(current_era): required T::BlockNumber; @@ -238,69 +172,20 @@ decl_storage! { pub NextSessionsPerEra get(next_sessions_per_era): T::BlockNumber; // The session index at which the era length last changed. pub LastEraLengthChange get(last_era_length_change): default T::BlockNumber; - // The current era stake threshold + + // The current era stake threshold - unused at present. Consider for removal. pub StakeThreshold get(stake_threshold): required T::Balance; + // The block at which the `who`'s funds become entirely liquid. + pub Bondage get(bondage): default map [ T::AccountId => T::BlockNumber ]; // The number of times a given validator has been reported offline. This gets decremented by one each era that passes. pub SlashCount get(slash_count): default map [ T::AccountId => u32 ]; - // The next free enumeration set. - pub NextEnumSet get(next_enum_set): required T::AccountIndex; - // The enumeration sets. - pub EnumSet get(enum_set): default map [ T::AccountIndex => Vec ]; - // We are forcing a new era. pub ForcingNewEra get(forcing_new_era): (); - - // The "free" balance of a given account. - // - // This is the only balance that matters in terms of most operations on tokens. It is - // alone used to determine the balance when in the contract execution environment. When this - // balance falls below the value of `ExistentialDeposit`, then the "current account" is - // deleted: specifically, `Bondage` and `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback - // is invoked, giving a chance to external modules to cleanup data associated with - // the deleted account. - // - // `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets - // collapsed to zero if it ever becomes less than `ExistentialDeposit`. - pub FreeBalance get(free_balance): default map [ T::AccountId => T::Balance ]; - - // The amount of the balance of a given account that is exterally reserved; this can still get - // slashed, but gets slashed last of all. - // - // This balance is a "reserve" balance that other subsystems use in order to set aside tokens - // that are still "owned" by the account holder, but which are unspendable. This is different - // and wholly unrelated to the `Bondage` system used for staking. - // - // When this balance falls below the value of `ExistentialDeposit`, then this "reserve account" - // is deleted: specifically, `ReservedBalance`. - // - // `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets - // collapsed to zero if it ever becomes less than `ExistentialDeposit`. - pub ReservedBalance get(reserved_balance): default map [ T::AccountId => T::Balance ]; - - // The block at which the `who`'s funds become entirely liquid. - pub Bondage get(bondage): default map [ T::AccountId => T::BlockNumber ]; } } -/// Whatever happened about the hint given when creating the new account. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)] -pub enum NewAccountOutcome { - NoHint, - GoodHint, - BadHint, -} - -/// Outcome of a balance update. -pub enum UpdateBalanceOutcome { - /// Account balance was simply updated. - Updated, - /// The update has led to killing of the account. - AccountKilled, -} - impl Module { // PUBLIC IMMUTABLES @@ -314,112 +199,31 @@ impl Module { Self::sessions_per_era() * >::length() } - /// The combined balance of `who`. - pub fn voting_balance(who: &T::AccountId) -> T::Balance { - Self::free_balance(who) + Self::reserved_balance(who) - } - /// Balance of a (potential) validator that includes all nominators. pub fn nomination_balance(who: &T::AccountId) -> T::Balance { Self::nominators_for(who).iter() - .map(Self::voting_balance) + .map(>::total_balance) .fold(Zero::zero(), |acc, x| acc + x) } /// The total balance that can be slashed from an account. pub fn slashable_balance(who: &T::AccountId) -> T::Balance { Self::nominators_for(who).iter() - .map(Self::voting_balance) - .fold(Self::voting_balance(who), |acc, x| acc + x) - } - - /// Some result as `slash(who, value)` (but without the side-effects) assuming there are no - /// balance changes in the meantime and only the reserved balance is not taken into account. - pub fn can_slash(who: &T::AccountId, value: T::Balance) -> bool { - Self::free_balance(who) >= value - } - - /// Same result as `reserve(who, value)` (but without the side-effects) assuming there - /// are no balance changes in the meantime. - pub fn can_reserve(who: &T::AccountId, value: T::Balance) -> bool { - if let LockStatus::Liquid = Self::unlock_block(who) { - Self::free_balance(who) >= value - } else { - false - } - } - - /// Lookup an T::AccountIndex to get an Id, if there's one there. - pub fn lookup_index(index: T::AccountIndex) -> Option { - let enum_set_size = Self::enum_set_size(); - let set = Self::enum_set(index / enum_set_size); - let i: usize = (index % enum_set_size).as_(); - set.get(i).map(|x| x.clone()) - } - - /// `true` if the account `index` is ready for reclaim. - pub fn can_reclaim(try_index: T::AccountIndex) -> bool { - let enum_set_size = Self::enum_set_size(); - let try_set = Self::enum_set(try_index / enum_set_size); - let i = (try_index % enum_set_size).as_(); - i < try_set.len() && Self::voting_balance(&try_set[i]).is_zero() + .map(>::total_balance) + .fold(>::total_balance(who), |acc, x| acc + x) } /// The block at which the `who`'s funds become entirely liquid. pub fn unlock_block(who: &T::AccountId) -> LockStatus { match Self::bondage(who) { - i if i == T::BlockNumber::max_value() => LockStatus::Staked, + i if i == T::BlockNumber::max_value() => LockStatus::Bonded, i if i <= >::block_number() => LockStatus::Liquid, i => LockStatus::LockedUntil(i), } } - /// Lookup an address to get an Id, if there's one there. - pub fn lookup_address(a: address::Address) -> Option { - match a { - address::Address::Id(i) => Some(i), - address::Address::Index(i) => Self::lookup_index(i), - } - } - // PUBLIC DISPATCH - /// Transfer some unlocked staking balance to another staker. - pub fn transfer(aux: &T::PublicAux, dest: Address, value: T::Balance) -> Result { - let dest = Self::lookup(dest)?; - - let transactor = aux.ref_into(); - let from_balance = Self::free_balance(transactor); - let would_create = from_balance.is_zero(); - let fee = if would_create { Self::creation_fee() } else { Self::transfer_fee() }; - let liability = value + fee; - - let new_from_balance = match from_balance.checked_sub(&liability) { - Some(b) => b, - None => return Err("balance too low to send value"), - }; - if would_create && value < Self::existential_deposit() { - return Err("value too low to create account"); - } - if >::get(transactor) > >::block_number() { - return Err("bondage too high to send value"); - } - - let to_balance = Self::free_balance(&dest); - 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); - } - - Ok(()) - } - /// Declare the desire to stake for the transactor. /// /// Effects will be felt at the beginning of the next era. @@ -446,8 +250,8 @@ impl Module { Self::apply_unstake(aux.ref_into(), intentions_index as usize) } - fn nominate(aux: &T::PublicAux, target: RawAddress) -> Result { - let target = Self::lookup(target)?; + fn nominate(aux: &T::PublicAux, target: Address) -> Result { + let target = >::lookup(target)?; let aux = aux.ref_into(); ensure!(Self::nominating(aux).is_none(), "Cannot nominate if already nominating."); @@ -490,7 +294,7 @@ impl Module { >::remove(source); // update bondage - >::insert(aux.ref_into(), Self::current_era() + Self::bonding_duration()); + >::insert(aux.ref_into(), >::block_number() + Self::bonding_duration()); Ok(()) } @@ -513,49 +317,6 @@ impl Module { Ok(()) } - /// Note the previous block's validator missed their opportunity to propose a block. This only comes in - /// if 2/3+1 of the validators agree that no proposal was submitted. It's only relevant - /// for the previous block. - fn note_missed_proposal(aux: &T::PublicAux, offline_val_indices: Vec) -> Result { - assert!(aux.is_empty()); - assert!( - >::extrinsic_index() == Some(T::NOTE_MISSED_PROPOSAL_POSITION), - "note_missed_proposal extrinsic must be at position {} in the block", - T::NOTE_MISSED_PROPOSAL_POSITION - ); - - for validator_index in offline_val_indices.into_iter() { - let v = >::validators()[validator_index as usize].clone(); - let slash_count = Self::slash_count(&v); - >::insert(v.clone(), slash_count + 1); - let grace = Self::offline_slash_grace(); - - let event = if slash_count >= grace { - let instances = slash_count - grace; - let slash = Self::early_era_slash() << instances; - let next_slash = slash << 1u32; - let _ = Self::slash_validator(&v, slash); - if instances >= Self::slash_preference_of(&v).unstake_threshold - || Self::slashable_balance(&v) < next_slash - { - if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { - Self::apply_unstake(&v, pos) - .expect("pos derived correctly from Self::intentions(); \ - apply_unstake can only fail if pos wrong; \ - Self::intentions() doesn't change; qed"); - } - let _ = Self::force_new_era(false); - } - RawEvent::OfflineSlash(v, slash) - } else { - RawEvent::OfflineWarning(v, slash_count) - }; - Self::deposit_event(event); - } - - Ok(()) - } - // PRIV DISPATCH /// Deposit one of this module's events. @@ -594,195 +355,8 @@ impl Module { Ok(()) } - /// Set the balances of a given account. - fn set_balance(who: Address, free: T::Balance, reserved: T::Balance) -> Result { - let who = Self::lookup(who)?; - Self::set_free_balance(&who, free); - Self::set_reserved_balance(&who, reserved); - Ok(()) - } - // PUBLIC MUTABLES (DANGEROUS) - /// Set the free balance of an account to some new value. - /// - /// Will enforce ExistentialDeposit law, anulling the account as needed. - /// In that case it will return `AccountKilled`. - pub 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); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated - } - } - - /// Set the free balance of an account to some new value. Will enforce ExistentialDeposit - /// law anulling 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 { - // Commented out for no - but consider it instructive. - // assert!(!Self::voting_balance(who).is_zero()); - if balance < Self::existential_deposit() { - >::insert(who, balance); - Self::on_free_too_low(who); - UpdateBalanceOutcome::AccountKilled - } else { - >::insert(who, balance); - UpdateBalanceOutcome::Updated - } - } - - /// 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. - // TODO: enforce this for the other balance-altering functions. - if balance < ed { - Self::set_free_balance(who, balance); - UpdateBalanceOutcome::AccountKilled - } else { - if !>::exists(who) { - let outcome = Self::new_account(&who, balance); - let credit = match outcome { - NewAccountOutcome::GoodHint => balance + >::reclaim_rebate(), - _ => balance, - }; - Self::set_free_balance(who, credit); - Self::increase_total_stake_by(credit - balance); - } else { - Self::set_free_balance(who, balance); - } - - UpdateBalanceOutcome::Updated - } - } - - /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the - /// free 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`. - pub fn slash(who: &T::AccountId, value: T::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) - } else { - None - } - } - - /// Adds up to `value` to the free balance of `who`. - /// - /// If `who` doesn't exist, nothing is done and an Err returned. - pub fn reward(who: &T::AccountId, value: T::Balance) -> Result { - if Self::voting_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(()) - } - - /// 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`. - pub fn reserve(who: &T::AccountId, value: T::Balance) -> Result { - let b = Self::free_balance(who); - if b < value { - return Err("not enough free funds") - } - if Self::unlock_block(who) != LockStatus::Liquid { - return Err("free funds are still bonded") - } - Self::set_reserved_balance(who, Self::reserved_balance(who) + value); - Self::set_free_balance(who, b - value); - Ok(()) - } - - /// 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`. - /// NOTE: This is different to `reserve`. - pub fn unreserve(who: &T::AccountId, value: T::Balance) -> Option { - 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) - } - } - - /// 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`. - pub fn slash_reserved(who: &T::AccountId, value: T::Balance) -> Option { - let b = Self::reserved_balance(who); - let slash = cmp::min(b, value); - Self::set_reserved_balance(who, b - slash); - Self::decrease_total_stake_by(slash); - if value == slash { - None - } else { - Some(value - slash) - } - } - - /// 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)`. - pub fn transfer_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: T::Balance - ) -> result::Result, &'static str> { - if Self::voting_balance(beneficiary).is_zero() { - return Err("beneficiary account must pre-exist"); - } - let b = Self::reserved_balance(slashed); - 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)) - } - } - /// Slash a given validator by a specific amount. Removes the slash from their balance by preference, /// and reduces the nominators' balance if needed. fn slash_validator(v: &T::AccountId, slash: T::Balance) { @@ -792,13 +366,13 @@ impl Module { return } - if let Some(rem) = Self::slash(v, slash) { + if let Some(rem) = >::slash(v, slash) { let noms = Self::current_nominators_for(v); - let total = noms.iter().map(Self::voting_balance).fold(T::Balance::zero(), |acc, x| acc + x); + let total = noms.iter().map(>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x); if !total.is_zero() { let safe_mul_rational = |b| b * rem / total;// TODO: avoid overflow for n in noms.iter() { - let _ = Self::slash(n, safe_mul_rational(Self::voting_balance(n))); // best effort - not much that can be done on fail. + let _ = >::slash(n, safe_mul_rational(>::total_balance(n))); // best effort - not much that can be done on fail. } } } @@ -808,13 +382,13 @@ impl Module { /// balance, pro-rata. fn reward_validator(who: &T::AccountId, reward: T::Balance) { let noms = Self::current_nominators_for(who); - let total = noms.iter().map(Self::voting_balance).fold(Self::voting_balance(who), |acc, x| acc + x); + let total = noms.iter().map(>::total_balance).fold(>::total_balance(who), |acc, x| acc + x); if !total.is_zero() { let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow for n in noms.iter() { - let _ = Self::reward(n, safe_mul_rational(Self::voting_balance(n))); + let _ = >::reward(n, safe_mul_rational(>::total_balance(n))); } - let _ = Self::reward(who, safe_mul_rational(Self::voting_balance(who))); + let _ = >::reward(who, safe_mul_rational(>::total_balance(who))); } } @@ -829,7 +403,7 @@ impl Module { >::put(intentions); >::remove(who); >::remove(who); - >::insert(who, Self::current_era() + Self::bonding_duration()); + >::insert(who, >::block_number() + Self::bonding_duration()); Ok(()) } @@ -916,125 +490,6 @@ impl Module { } >::set_validators(vals); } - - fn enum_set_size() -> T::AccountIndex { - T::AccountIndex::sa(ENUM_SET_SIZE) - } - - /// Register a new account (with existential balance). - fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome { - let enum_set_size = Self::enum_set_size(); - let next_set_index = Self::next_enum_set(); - let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC); - let reclaim_index_modulus = T::AccountIndex::sa(256usize); - let quantization = T::AccountIndex::sa(256usize); - - // A little easter-egg for reclaiming dead indexes.. - let ret = { - // we quantise the number of accounts so it stays constant over a reasonable - // period of time. - let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization; - // then modify the starting balance to be modulo this to allow it to potentially - // identify an account index for reuse. - let maybe_try_index = balance % >::sa(quantized_account_count * reclaim_index_modulus); - let maybe_try_index = As::::as_(maybe_try_index); - - // this identifier must end with magic byte 0x69 to trigger this check (a minor - // optimisation to ensure we don't check most unintended account creations). - if maybe_try_index % reclaim_index_modulus == reclaim_index_magic { - // reuse is probably intended. first, remove magic byte. - let try_index = maybe_try_index / reclaim_index_modulus; - - // then check to see if this balance identifies a dead account index. - let set_index = try_index / enum_set_size; - let mut try_set = Self::enum_set(set_index); - let item_index = (try_index % enum_set_size).as_(); - if item_index < try_set.len() { - if Self::voting_balance(&try_set[item_index]).is_zero() { - // yup - this index refers to a dead account. can be reused. - try_set[item_index] = who.clone(); - >::insert(set_index, try_set); - - Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint)); - - return NewAccountOutcome::GoodHint - } - } - NewAccountOutcome::BadHint - } else { - NewAccountOutcome::NoHint - } - }; - - // insert normally as a back up - let mut set_index = next_set_index; - // defensive only: this loop should never iterate since we keep NextEnumSet up to date later. - let mut set = loop { - let set = Self::enum_set(set_index); - if set.len() < ENUM_SET_SIZE { - break set; - } - set_index += One::one(); - }; - - let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len()); - - // update set. - set.push(who.clone()); - - // keep NextEnumSet up to date - if set.len() == ENUM_SET_SIZE { - >::put(set_index + One::one()); - } - - // write set. - >::insert(set_index, set); - - Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret)); - - ret - } - - fn reap_account(who: &T::AccountId) { - >::remove(who); - Self::deposit_event(RawEvent::ReapedAccount(who.clone())); - } - - /// Kill an account's free portion. - fn on_free_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::free_balance(who)); - >::remove(who); - >::remove(who); - - T::OnFreeBalanceZero::on_free_balance_zero(who); - - if Self::reserved_balance(who).is_zero() { - Self::reap_account(who); - } - } - - /// Kill an account's reserved portion. - fn on_reserved_too_low(who: &T::AccountId) { - Self::decrease_total_stake_by(Self::reserved_balance(who)); - >::remove(who); - - if Self::free_balance(who).is_zero() { - Self::reap_account(who); - } - } - - /// Increase TotalStake by Value. - pub fn increase_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_stake().checked_add(&value) { - >::put(v); - } - } - /// Decrease TotalStake by Value. - pub fn decrease_total_stake_by(value: T::Balance) { - if let Some(v) = >::total_stake().checked_sub(&value) { - >::put(v); - } - } } impl Executable for Module { @@ -1048,26 +503,49 @@ impl OnSessionChange for Module { } } -impl AuxLookup for Module { - type Source = address::Address; - type Target = T::AccountId; - fn lookup(a: Self::Source) -> result::Result { - match a { - address::Address::Id(i) => Ok(i), - address::Address::Index(i) => >::lookup_index(i).ok_or("invalid account index"), +impl balances::EnsureAccountLiquid for Module { + fn ensure_account_liquid(who: &T::AccountId) -> Result { + if Self::bondage(who) <= >::block_number() { + Ok(()) + } else { + Err("cannot transfer illiquid funds") } } } -impl MakePayment for Module { - fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { - let b = Self::free_balance(transactor); - let transaction_fee = Self::transaction_base_fee() + Self::transaction_byte_fee() * >::sa(encoded_len as u64); - if b < transaction_fee + Self::existential_deposit() { - return Err("not enough funds for transaction fee"); - } - Self::set_free_balance(transactor, b - transaction_fee); - Self::decrease_total_stake_by(transaction_fee); - Ok(()) +impl balances::OnFreeBalanceZero for Module { + fn on_free_balance_zero(who: &T::AccountId) { + >::remove(who); + } +} + +impl consensus::OnOfflineValidator for Module { + fn on_offline_validator(validator_index: usize) { + let v = >::validators()[validator_index].clone(); + let slash_count = Self::slash_count(&v); + >::insert(v.clone(), slash_count + 1); + let grace = Self::offline_slash_grace(); + + let event = if slash_count >= grace { + let instances = slash_count - grace; + let slash = Self::early_era_slash() << instances; + let next_slash = slash << 1u32; + let _ = Self::slash_validator(&v, slash); + if instances >= Self::slash_preference_of(&v).unstake_threshold + || Self::slashable_balance(&v) < next_slash + { + if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) { + Self::apply_unstake(&v, pos) + .expect("pos derived correctly from Self::intentions(); \ + apply_unstake can only fail if pos wrong; \ + Self::intentions() doesn't change; qed"); + } + let _ = Self::force_new_era(false); + } + RawEvent::OfflineSlash(v, slash) + } else { + RawEvent::OfflineWarning(v, slash_count) + }; + Self::deposit_event(event); } } diff --git a/substrate/runtime/staking/src/mock.rs b/substrate/runtime/staking/src/mock.rs index 7b3097010d7eb..25447c39cbe23 100644 --- a/substrate/runtime/staking/src/mock.rs +++ b/substrate/runtime/staking/src/mock.rs @@ -23,7 +23,7 @@ use primitives::traits::{HasPublicAux, Identity}; use primitives::testing::{Digest, Header}; use substrate_primitives::{H256, KeccakHasher}; use runtime_io; -use {GenesisConfig, Module, Trait, consensus, session, system, timestamp}; +use {GenesisConfig, Module, Trait, consensus, session, system, timestamp, balances}; // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] @@ -32,10 +32,12 @@ impl HasPublicAux for Test { type PublicAux = u64; } impl consensus::Trait for Test { - type PublicAux = ::PublicAux; + const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = u64; + type OnOfflineValidator = (); } impl system::Trait for Test { + type PublicAux = ::PublicAux; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -45,6 +47,13 @@ impl system::Trait for Test { type Header = Header; type Event = (); } +impl balances::Trait for Test { + type Balance = u64; + type AccountIndex = u64; + type OnFreeBalanceZero = Staking; + type EnsureAccountLiquid = Staking; + type Event = (); +} impl session::Trait for Test { type ConvertAccountIdToSessionKey = Identity; type OnSessionChange = Staking; @@ -55,10 +64,6 @@ impl timestamp::Trait for Test { type Moment = u64; } impl Trait for Test { - const NOTE_MISSED_PROPOSAL_POSITION: u32 = 1; - type Balance = u64; - type AccountIndex = u64; - type OnFreeBalanceZero = (); type Event = (); } @@ -77,9 +82,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 session_length, validators: vec![10, 20], }.build_storage().unwrap()); - t.extend(GenesisConfig::{ - sessions_per_era, - current_era, + t.extend(balances::GenesisConfig::{ balances: if monied { if reward > 0 { vec![(1, 10 * balance_factor), (2, 20 * balance_factor), (3, 30 * balance_factor), (4, 40 * balance_factor), (10, balance_factor), (20, balance_factor)] @@ -89,16 +92,20 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 } else { vec![(10, balance_factor), (20, balance_factor)] }, - intentions: vec![10, 20], - validator_count: 2, - minimum_validator_count: 0, - bonding_duration: 3, transaction_base_fee: 0, transaction_byte_fee: 0, existential_deposit: ext_deposit, transfer_fee: 0, creation_fee: 0, reclaim_rebate: 0, + }.build_storage().unwrap()); + t.extend(GenesisConfig::{ + sessions_per_era, + current_era, + intentions: vec![10, 20], + validator_count: 2, + minimum_validator_count: 0, + bonding_duration: sessions_per_era * session_length * 3, session_reward: reward, early_era_slash: if monied { 20 } else { 0 }, offline_slash_grace: 0, @@ -110,6 +117,7 @@ pub fn new_test_ext(ext_deposit: u64, session_length: u64, sessions_per_era: u64 } pub type System = system::Module; +pub type Balances = balances::Module; pub type Session = session::Module; pub type Timestamp = timestamp::Module; pub type Staking = Module; diff --git a/substrate/runtime/staking/src/tests.rs b/substrate/runtime/staking/src/tests.rs index 809838379ae12..e60a7b129b833 100644 --- a/substrate/runtime/staking/src/tests.rs +++ b/substrate/runtime/staking/src/tests.rs @@ -1,175 +1,171 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Tests for the module. #![cfg(test)] use super::*; +use consensus::OnOfflineValidator; use runtime_io::with_externalities; -use mock::{Session, Staking, System, Timestamp, Test, new_test_ext}; +use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext}; #[test] -fn note_null_missed_proposal_should_work() { +fn note_null_offline_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { assert_eq!(Staking::offline_slash_grace(), 0); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 1); + assert_eq!(Balances::free_balance(&10), 1); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![])); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 1); + assert_eq!(Balances::free_balance(&10), 1); assert!(Staking::forcing_new_era().is_none()); }); } #[test] -fn note_missed_proposal_should_work() { +fn note_offline_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Staking::set_free_balance(&10, 70); + Balances::set_free_balance(&10, 70); assert_eq!(Staking::offline_slash_grace(), 0); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Balances::free_balance(&10), 70); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + Staking::on_offline_validator(0); assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Staking::free_balance(&10), 50); + assert_eq!(Balances::free_balance(&10), 50); assert!(Staking::forcing_new_era().is_none()); }); } #[test] -fn note_missed_proposal_exponent_should_work() { +fn note_offline_exponent_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Staking::set_free_balance(&10, 150); + Balances::set_free_balance(&10, 150); assert_eq!(Staking::offline_slash_grace(), 0); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 150); + assert_eq!(Balances::free_balance(&10), 150); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + Staking::on_offline_validator(0); assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Staking::free_balance(&10), 130); + assert_eq!(Balances::free_balance(&10), 130); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + Staking::on_offline_validator(0); assert_eq!(Staking::slash_count(&10), 2); - assert_eq!(Staking::free_balance(&10), 90); + assert_eq!(Balances::free_balance(&10), 90); assert!(Staking::forcing_new_era().is_none()); }); } #[test] -fn note_missed_proposal_grace_should_work() { +fn note_offline_grace_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Staking::set_free_balance(&10, 70); - Staking::set_free_balance(&20, 70); + Balances::set_free_balance(&10, 70); + Balances::set_free_balance(&20, 70); assert_ok!(Staking::set_offline_slash_grace(1)); assert_eq!(Staking::offline_slash_grace(), 1); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Balances::free_balance(&10), 70); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + Staking::on_offline_validator(0); assert_eq!(Staking::slash_count(&10), 1); - assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Balances::free_balance(&10), 70); assert_eq!(Staking::slash_count(&20), 0); - assert_eq!(Staking::free_balance(&20), 70); + assert_eq!(Balances::free_balance(&20), 70); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); assert_eq!(Staking::slash_count(&10), 2); - assert_eq!(Staking::free_balance(&10), 50); + assert_eq!(Balances::free_balance(&10), 50); assert_eq!(Staking::slash_count(&20), 1); - assert_eq!(Staking::free_balance(&20), 70); + assert_eq!(Balances::free_balance(&20), 70); assert!(Staking::forcing_new_era().is_none()); }); } #[test] -fn note_missed_proposal_force_unstake_session_change_should_work() { +fn note_offline_force_unstake_session_change_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Staking::set_free_balance(&10, 70); - Staking::set_free_balance(&20, 70); + Balances::set_free_balance(&10, 70); + Balances::set_free_balance(&20, 70); assert_ok!(Staking::stake(&1)); assert_eq!(Staking::slash_count(&10), 0); - assert_eq!(Staking::free_balance(&10), 70); + assert_eq!(Balances::free_balance(&10), 70); assert_eq!(Staking::intentions(), vec![10, 20, 1]); assert_eq!(Session::validators(), vec![10, 20]); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); - assert_eq!(Staking::free_balance(&10), 50); + Staking::on_offline_validator(0); + assert_eq!(Balances::free_balance(&10), 50); assert_eq!(Staking::slash_count(&10), 1); assert_eq!(Staking::intentions(), vec![10, 20, 1]); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0])); + Staking::on_offline_validator(0); assert_eq!(Staking::intentions(), vec![1, 20]); - assert_eq!(Staking::free_balance(&10), 10); + assert_eq!(Balances::free_balance(&10), 10); assert!(Staking::forcing_new_era().is_some()); }); } #[test] -fn note_missed_proposal_auto_unstake_session_change_should_work() { +fn note_offline_auto_unstake_session_change_should_work() { with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - Staking::set_free_balance(&10, 7000); - Staking::set_free_balance(&20, 7000); + Balances::set_free_balance(&10, 7000); + Balances::set_free_balance(&20, 7000); assert_ok!(Staking::register_slash_preference(&10, 0, SlashPreference { unstake_threshold: 1 })); assert_eq!(Staking::intentions(), vec![10, 20]); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); - assert_eq!(Staking::free_balance(&10), 6980); - assert_eq!(Staking::free_balance(&20), 6980); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6980); + assert_eq!(Balances::free_balance(&20), 6980); assert_eq!(Staking::intentions(), vec![10, 20]); assert!(Staking::forcing_new_era().is_none()); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); - assert_eq!(Staking::free_balance(&10), 6940); - assert_eq!(Staking::free_balance(&20), 6940); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6940); assert_eq!(Staking::intentions(), vec![20]); assert!(Staking::forcing_new_era().is_some()); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1])); - assert_eq!(Staking::free_balance(&10), 6940); - assert_eq!(Staking::free_balance(&20), 6860); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6860); assert_eq!(Staking::intentions(), vec![20]); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![1])); - assert_eq!(Staking::free_balance(&10), 6940); - assert_eq!(Staking::free_balance(&20), 6700); + Staking::on_offline_validator(1); + assert_eq!(Balances::free_balance(&10), 6940); + assert_eq!(Balances::free_balance(&20), 6700); assert_eq!(Staking::intentions(), vec![0u64; 0]); }); } -#[test] -fn reward_should_work() { - with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || { - assert_eq!(Staking::voting_balance(&10), 1); - assert_ok!(Staking::reward(&10, 10)); - assert_eq!(Staking::voting_balance(&10), 11); - assert_eq!(>::get(), 112); - }); -} + + #[test] fn rewards_should_work() { @@ -179,26 +175,26 @@ fn rewards_should_work() { assert_eq!(Staking::last_era_length_change(), 0); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 0); - assert_eq!(Staking::voting_balance(&10), 1); + assert_eq!(Balances::total_balance(&10), 1); System::set_block_number(3); Timestamp::set_timestamp(15); // on time. Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 1); - assert_eq!(Staking::voting_balance(&10), 11); + assert_eq!(Balances::total_balance(&10), 11); System::set_block_number(6); Timestamp::set_timestamp(31); // a little late Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 2); - assert_eq!(Staking::voting_balance(&10), 20); // less reward + assert_eq!(Balances::total_balance(&10), 20); // less reward System::set_block_number(9); Timestamp::set_timestamp(50); // very late Session::check_rotate_session(); assert_eq!(Staking::current_era(), 1); assert_eq!(Session::current_index(), 3); - assert_eq!(Staking::voting_balance(&10), 27); // much less reward + assert_eq!(Balances::total_balance(&10), 27); // much less reward }); } @@ -210,112 +206,40 @@ fn slashing_should_work() { assert_eq!(Staking::last_era_length_change(), 0); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 0); - assert_eq!(Staking::voting_balance(&10), 1); + assert_eq!(Balances::total_balance(&10), 1); System::set_block_number(3); Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 1); - assert_eq!(Staking::voting_balance(&10), 11); + assert_eq!(Balances::total_balance(&10), 11); System::set_block_number(6); Session::check_rotate_session(); assert_eq!(Staking::current_era(), 0); assert_eq!(Session::current_index(), 2); - assert_eq!(Staking::voting_balance(&10), 21); + assert_eq!(Balances::total_balance(&10), 21); System::set_block_number(7); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); - assert_eq!(Staking::voting_balance(&10), 1); - }); -} - -#[test] -fn indexing_lookup_should_work() { - with_externalities(&mut new_test_ext(10, 1, 2, 0, true, 0), || { - assert_eq!(Staking::lookup_index(0), Some(1)); - assert_eq!(Staking::lookup_index(1), Some(2)); - assert_eq!(Staking::lookup_index(2), Some(3)); - assert_eq!(Staking::lookup_index(3), Some(4)); - assert_eq!(Staking::lookup_index(4), None); - }); -} - -#[test] -fn default_indexing_on_new_accounts_should_work() { - with_externalities(&mut new_test_ext(10, 1, 2, 0, true, 0), || { - assert_eq!(Staking::lookup_index(4), None); - assert_ok!(Staking::transfer(&1, 5.into(), 10)); - assert_eq!(Staking::lookup_index(4), Some(5)); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::total_balance(&10), 1); }); } -#[test] -fn dust_account_removal_should_work() { - with_externalities(&mut new_test_ext(256 * 10, 1, 2, 0, true, 0), || { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Staking::voting_balance(&2), 256 * 20); - - assert_ok!(Staking::transfer(&2, 5.into(), 256 * 10 + 1)); // index 1 (account 2) becomes zombie - assert_eq!(Staking::voting_balance(&2), 0); - assert_eq!(Staking::voting_balance(&5), 256 * 10 + 1); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn reclaim_indexing_on_new_accounts_should_work() { - with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true, 0), || { - assert_eq!(Staking::lookup_index(1), Some(2)); - assert_eq!(Staking::lookup_index(4), None); - assert_eq!(Staking::voting_balance(&2), 256 * 20); - - assert_ok!(Staking::transfer(&2, 5.into(), 256 * 20)); // account 2 becomes zombie freeing index 1 for reclaim) - assert_eq!(Staking::voting_balance(&2), 0); - - assert_ok!(Staking::transfer(&5, 6.into(), 256 * 1 + 0x69)); // account 6 takes index 1. - assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69); - assert_eq!(Staking::lookup_index(1), Some(6)); - }); -} -#[test] -fn reserved_balance_should_prevent_reclaim_count() { - with_externalities(&mut new_test_ext(256 * 1, 1, 2, 0, true, 0), || { - System::inc_account_nonce(&2); - assert_eq!(Staking::lookup_index(1), Some(2)); - assert_eq!(Staking::lookup_index(4), None); - assert_eq!(Staking::voting_balance(&2), 256 * 20); - - assert_ok!(Staking::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(Staking::free_balance(&2), 0); // "free" account deleted." - assert_eq!(Staking::voting_balance(&2), 256 * 19 + 1); // reserve still exists. - assert_eq!(System::account_nonce(&2), 1); - - assert_ok!(Staking::transfer(&4, 5.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 for account 5. - assert_eq!(Staking::voting_balance(&5), 256 * 1 + 0x69); - assert_eq!(Staking::lookup_index(1), Some(2)); // but fails. - assert_eq!(System::account_nonce(&2), 1); - - assert_eq!(Staking::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed - assert_eq!(Staking::voting_balance(&2), 0); // "free" account deleted." - assert_eq!(System::account_nonce(&2), 0); - - assert_ok!(Staking::transfer(&4, 6.into(), 256 * 1 + 0x69)); // account 4 tries to take index 1 again for account 6. - assert_eq!(Staking::voting_balance(&6), 256 * 1 + 0x69); - assert_eq!(Staking::lookup_index(1), Some(6)); // and succeeds. - }); -} #[test] fn staking_should_work() { with_externalities(&mut new_test_ext(0, 1, 2, 0, true, 0), || { + assert_eq!(Staking::era_length(), 2); assert_eq!(Staking::validator_count(), 2); - assert_eq!(Staking::bonding_duration(), 3); assert_eq!(Session::validators(), vec![10, 20]); + + assert_ok!(Staking::set_bonding_duration(2)); + assert_eq!(Staking::bonding_duration(), 2); // Block 1: Add three validators. No obvious change. System::set_block_number(1); @@ -347,7 +271,7 @@ fn staking_should_work() { // Block 5: Transfer stake from highest to lowest. No change yet. System::set_block_number(5); - assert_ok!(Staking::transfer(&4, 1.into(), 40)); + assert_ok!(Balances::transfer(&4, 1.into(), 40)); Session::check_rotate_session(); // Block 6: Lowest now validator. @@ -384,20 +308,20 @@ fn nominating_and_rewards_should_work() { Session::check_rotate_session(); assert_eq!(Staking::current_era(), 1); assert_eq!(Session::validators(), vec![1, 3]); // 4 + 1, 3 - assert_eq!(Staking::voting_balance(&1), 10); - assert_eq!(Staking::voting_balance(&2), 20); - assert_eq!(Staking::voting_balance(&3), 30); - assert_eq!(Staking::voting_balance(&4), 40); + assert_eq!(Balances::total_balance(&1), 10); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 30); + assert_eq!(Balances::total_balance(&4), 40); System::set_block_number(2); assert_ok!(Staking::unnominate(&4, 0)); Session::check_rotate_session(); assert_eq!(Staking::current_era(), 2); assert_eq!(Session::validators(), vec![3, 2]); - assert_eq!(Staking::voting_balance(&1), 12); - assert_eq!(Staking::voting_balance(&2), 20); - assert_eq!(Staking::voting_balance(&3), 40); - assert_eq!(Staking::voting_balance(&4), 48); + assert_eq!(Balances::total_balance(&1), 12); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 40); + assert_eq!(Balances::total_balance(&4), 48); System::set_block_number(3); assert_ok!(Staking::stake(&4)); @@ -405,17 +329,17 @@ fn nominating_and_rewards_should_work() { assert_ok!(Staking::nominate(&3, 1.into())); Session::check_rotate_session(); assert_eq!(Session::validators(), vec![1, 4]); - assert_eq!(Staking::voting_balance(&1), 12); - assert_eq!(Staking::voting_balance(&2), 30); - assert_eq!(Staking::voting_balance(&3), 50); - assert_eq!(Staking::voting_balance(&4), 48); + assert_eq!(Balances::total_balance(&1), 12); + assert_eq!(Balances::total_balance(&2), 30); + assert_eq!(Balances::total_balance(&3), 50); + assert_eq!(Balances::total_balance(&4), 48); System::set_block_number(4); Session::check_rotate_session(); - assert_eq!(Staking::voting_balance(&1), 13); - assert_eq!(Staking::voting_balance(&2), 30); - assert_eq!(Staking::voting_balance(&3), 58); - assert_eq!(Staking::voting_balance(&4), 58); + assert_eq!(Balances::total_balance(&1), 13); + assert_eq!(Balances::total_balance(&2), 30); + assert_eq!(Balances::total_balance(&3), 58); + assert_eq!(Balances::total_balance(&4), 58); }); } @@ -424,7 +348,7 @@ fn nominating_slashes_should_work() { with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || { assert_eq!(Staking::era_length(), 4); assert_eq!(Staking::validator_count(), 2); - assert_eq!(Staking::bonding_duration(), 3); + assert_eq!(Staking::bonding_duration(), 12); assert_eq!(Session::validators(), vec![10, 20]); System::set_block_number(2); @@ -440,18 +364,19 @@ fn nominating_slashes_should_work() { assert_eq!(Staking::current_era(), 1); assert_eq!(Session::validators(), vec![1, 3]); // 1 + 4, 3 + 2 - assert_eq!(Staking::voting_balance(&1), 10); - assert_eq!(Staking::voting_balance(&2), 20); - assert_eq!(Staking::voting_balance(&3), 30); - assert_eq!(Staking::voting_balance(&4), 40); + assert_eq!(Balances::total_balance(&1), 10); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 30); + assert_eq!(Balances::total_balance(&4), 40); System::set_block_number(5); ::system::ExtrinsicIndex::::put(1); - assert_ok!(Staking::note_missed_proposal(&Default::default(), vec![0, 1])); - assert_eq!(Staking::voting_balance(&1), 0); - assert_eq!(Staking::voting_balance(&2), 20); - assert_eq!(Staking::voting_balance(&3), 10); - assert_eq!(Staking::voting_balance(&4), 30); + Staking::on_offline_validator(0); + Staking::on_offline_validator(1); + assert_eq!(Balances::total_balance(&1), 0); + assert_eq!(Balances::total_balance(&2), 20); + assert_eq!(Balances::total_balance(&3), 10); + assert_eq!(Balances::total_balance(&4), 30); }); } @@ -536,235 +461,22 @@ fn staking_eras_work() { }); } -#[test] -fn staking_balance_works() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 42); - assert_eq!(Staking::free_balance(&1), 42); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(Staking::voting_balance(&1), 42); - assert_eq!(Staking::free_balance(&2), 0); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::voting_balance(&2), 0); - }); -} - -#[test] -fn staking_balance_transfer_works() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - Staking::increase_total_stake_by(111); - assert_ok!(Staking::transfer(&1, 2.into(), 69)); - assert_eq!(Staking::voting_balance(&1), 42); - assert_eq!(Staking::voting_balance(&2), 69); - }); -} - #[test] fn staking_balance_transfer_when_bonded_should_not_work() { with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); + Balances::set_free_balance(&1, 111); assert_ok!(Staking::stake(&1)); - assert_noop!(Staking::transfer(&1, 2.into(), 69), "bondage too high to send value"); - }); -} - -#[test] -fn reserving_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - - assert_eq!(Staking::voting_balance(&1), 111); - assert_eq!(Staking::free_balance(&1), 111); - assert_eq!(Staking::reserved_balance(&1), 0); - - assert_ok!(Staking::reserve(&1, 69)); - - assert_eq!(Staking::voting_balance(&1), 111); - assert_eq!(Staking::free_balance(&1), 42); - assert_eq!(Staking::reserved_balance(&1), 69); - }); -} - -#[test] -fn staking_balance_transfer_when_reserved_should_not_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - assert_ok!(Staking::reserve(&1, 69)); - assert_noop!(Staking::transfer(&1, 2.into(), 69), "balance too low to send value"); - }); -} - -#[test] -fn deducting_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - assert_ok!(Staking::reserve(&1, 69)); - assert_eq!(Staking::free_balance(&1), 42); + assert_noop!(Balances::transfer(&1, 2.into(), 69), "cannot transfer illiquid funds"); }); } #[test] fn deducting_balance_when_bonded_should_not_work() { with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); + Balances::set_free_balance(&1, 111); >::insert(1, 2); System::set_block_number(1); assert_eq!(Staking::unlock_block(&1), LockStatus::LockedUntil(2)); - assert_noop!(Staking::reserve(&1, 69), "free funds are still bonded"); - }); -} - -#[test] -fn refunding_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 42); - Staking::set_reserved_balance(&1, 69); - Staking::unreserve(&1, 69); - assert_eq!(Staking::free_balance(&1), 111); - assert_eq!(Staking::reserved_balance(&1), 0); - }); -} - -#[test] -fn slashing_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - Staking::increase_total_stake_by(111); - assert_ok!(Staking::reserve(&1, 69)); - assert!(Staking::slash(&1, 69).is_none()); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&1), 42); - assert_eq!(>::get(), 44); - }); -} - -#[test] -fn slashing_incomplete_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 42); - Staking::increase_total_stake_by(42); - assert_ok!(Staking::reserve(&1, 21)); - assert!(Staking::slash(&1, 69).is_some()); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(>::get(), 2); - }); -} - -#[test] -fn unreserving_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - assert_ok!(Staking::reserve(&1, 111)); - Staking::unreserve(&1, 42); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 42); - }); -} - -#[test] -fn slashing_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - Staking::increase_total_stake_by(111); - assert_ok!(Staking::reserve(&1, 111)); - assert!(Staking::slash_reserved(&1, 42).is_none()); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(>::get(), 71); - }); -} - -#[test] -fn slashing_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - Staking::increase_total_stake_by(111); - assert_ok!(Staking::reserve(&1, 42)); - assert!(Staking::slash_reserved(&1, 69).is_some()); - assert_eq!(Staking::free_balance(&1), 69); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(>::get(), 71); - }); -} - -#[test] -fn transferring_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 110); - Staking::set_free_balance(&2, 1); - assert_ok!(Staking::reserve(&1, 110)); - assert_ok!(Staking::transfer_reserved(&1, &2, 41), None); - assert_eq!(Staking::reserved_balance(&1), 69); - assert_eq!(Staking::free_balance(&1), 0); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_reserved_balance_to_nonexistent_should_fail() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 111); - assert_ok!(Staking::reserve(&1, 111)); - assert_noop!(Staking::transfer_reserved(&1, &2, 42), "beneficiary account must pre-exist"); - }); -} - -#[test] -fn transferring_incomplete_reserved_balance_should_work() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - Staking::set_free_balance(&1, 110); - Staking::set_free_balance(&2, 1); - assert_ok!(Staking::reserve(&1, 41)); - assert!(Staking::transfer_reserved(&1, &2, 69).unwrap().is_some()); - assert_eq!(Staking::reserved_balance(&1), 0); - assert_eq!(Staking::free_balance(&1), 69); - assert_eq!(Staking::reserved_balance(&2), 0); - assert_eq!(Staking::free_balance(&2), 42); - }); -} - -#[test] -fn transferring_too_high_value_should_not_panic() { - with_externalities(&mut new_test_ext(0, 1, 3, 1, false, 0), || { - >::insert(1, u64::max_value()); - >::insert(2, 1); - - assert_err!( - Staking::transfer(&1, 2.into(), u64::max_value()), - "destination balance too high to receive value" - ); - - assert_eq!(Staking::free_balance(&1), u64::max_value()); - assert_eq!(Staking::free_balance(&2), 1); - }); -} - -#[test] -fn account_removal_on_free_too_low() { - with_externalities(&mut new_test_ext(100, 1, 3, 1, false, 0), || { - // Setup two accounts with free balance above the exsistential threshold. - { - Staking::set_free_balance(&1, 110); - Staking::increase_total_stake_by(110); - - Staking::set_free_balance(&2, 110); - Staking::increase_total_stake_by(110); - - assert_eq!(>::get(), 732); - } - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the exsistential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Staking::transfer(&1, 2.into(), 20)); - - // Verify free balance removal of account 1. - assert_eq!(Staking::free_balance(&1), 0); - - // Verify that TotalStake tracks balance removal when free balance is too low. - assert_eq!(>::get(), 642); + assert_noop!(Balances::reserve(&1, 69), "cannot transfer illiquid funds"); }); } diff --git a/substrate/runtime/system/src/lib.rs b/substrate/runtime/system/src/lib.rs index c91799dc93952..420192468f2f0 100644 --- a/substrate/runtime/system/src/lib.rs +++ b/substrate/runtime/system/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! System manager: Handles lowest level stuff like depositing logs, basic set up and take down of //! temporary storage entries, access to old block hashes. @@ -45,7 +45,7 @@ extern crate safe_mix; use rstd::prelude::*; use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, Zero, One, Bounded, - Hash, Member, MaybeDisplay}; + Hash, Member, MaybeDisplay, RefInto, MaybeEmpty}; use runtime_support::{StorageValue, StorageMap, Parameter}; use safe_mix::TripletMix; @@ -69,6 +69,9 @@ pub fn extrinsics_data_root(xts: Vec>) -> H::Output { } pub trait Trait: Eq + Clone { + // We require that PublicAux impl MaybeEmpty, since we require that inherents - or unsigned + // user-level extrinsics - can exist. + type PublicAux: RefInto + MaybeEmpty; type Index: Parameter + Member + Default + MaybeDisplay + SimpleArithmetic + Copy; type BlockNumber: Parameter + Member + MaybeDisplay + SimpleArithmetic + Default + Bounded + Copy + rstd::hash::Hash; type Hash: Parameter + Member + MaybeDisplay + SimpleBitOps + Default + Copy + CheckEqual + rstd::hash::Hash + AsRef<[u8]>; @@ -287,6 +290,7 @@ mod tests { #[derive(Clone, Eq, PartialEq)] pub struct Test; impl Trait for Test { + type PublicAux = u64; type Index = u64; type BlockNumber = u64; type Hash = H256; diff --git a/substrate/runtime/timestamp/src/lib.rs b/substrate/runtime/timestamp/src/lib.rs index db1c7e1281128..80781ab075b64 100644 --- a/substrate/runtime/timestamp/src/lib.rs +++ b/substrate/runtime/timestamp/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Timestamp manager: just handles the current timestamp. @@ -43,7 +43,7 @@ use runtime_support::dispatch::Result; use runtime_primitives::traits::{Executable, MaybeEmpty, SimpleArithmetic, As, Zero}; pub trait Trait: consensus::Trait where - ::PublicAux: MaybeEmpty + ::PublicAux: MaybeEmpty { // the position of the required timestamp-set extrinsic. const TIMESTAMP_SET_POSITION: u32; @@ -152,6 +152,7 @@ mod tests { type PublicAux = u64; } impl system::Trait for Test { + type PublicAux = u64; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -162,8 +163,9 @@ mod tests { type Event = (); } impl consensus::Trait for Test { - type PublicAux = u64; + const NOTE_OFFLINE_POSITION: u32 = 1; type SessionKey = u64; + type OnOfflineValidator = (); } impl Trait for Test { const TIMESTAMP_SET_POSITION: u32 = 0; diff --git a/substrate/runtime/version/src/lib.rs b/substrate/runtime/version/src/lib.rs index 1dbf4aa2da2f5..ab66ecc88d25d 100644 --- a/substrate/runtime/version/src/lib.rs +++ b/substrate/runtime/version/src/lib.rs @@ -1,18 +1,18 @@ // Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Substrate Demo. +// This file is part of Substrate. -// Substrate Demo is free software: you can redistribute it and/or modify +// 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 Demo is distributed in the hope that it will be useful, +// 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 Demo. If not, see . +// along with Substrate. If not, see . //! Version module for runtime; Provide a function that returns runtime version. diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 7d2f104d0966a..625a093a6d41b 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index 5618789cf379b..874a7a2da0c3f 100755 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ