diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index e6e74460b3de0..96ac003d36874 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -124,6 +124,182 @@ pub trait BlockNumberToHash { } } +/// 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); + } +} + +/// Trait for a hook to get called when some balance has been minted, causing dilution. +pub trait OnDilution { + /// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth + /// amount (it doesn't take account of the recent growth). + fn on_dilution(minted: Balance, portion: Balance); +} + +impl OnDilution for () { + fn on_dilution(_minted: Balance, _portion: Balance) {} +} + +/// 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::Result<(), &'static str>; +} +impl< + AccountId, + X: EnsureAccountLiquid, + Y: EnsureAccountLiquid, +> EnsureAccountLiquid for (X, Y) { + fn ensure_account_liquid(who: &AccountId) -> result::Result<(), &'static str> { + X::ensure_account_liquid(who)?; + Y::ensure_account_liquid(who) + } +} +impl EnsureAccountLiquid for () { + fn ensure_account_liquid(_who: &AccountId) -> result::Result<(), &'static str> { Ok(()) } +} + +/// Outcome of a balance update. +pub enum UpdateBalanceOutcome { + /// Account balance was simply updated. + Updated, + /// The update has led to killing of the account. + AccountKilled, +} + +/// Abstraction over staking mecanism +pub trait Staking { + /// The balance of an account. + type Balance: SimpleArithmetic + As + As + Codec + Copy + MaybeSerializeDebug + Default; + + // PUBLIC IMMUTABLES + + /// The combined balance of `who`. + fn total_balance(who: &AccountId) -> Self::Balance; + + /// 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. + fn can_slash(who: &AccountId, value: Self::Balance) -> bool; + + /// Same result as `reserve(who, value)` (but without the side-effects) assuming there + /// are no balance changes in the meantime. + fn can_reserve(who: &AccountId, value: Self::Balance) -> bool; + + /// The total amount of stake on the system. + fn total_issuance() -> Self:: Balance; + + /// 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`. + fn free_balance(who: &AccountId) -> Self::Balance; + + /// The amount of the balance of a given account that is externally 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 suspendable. (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`. + fn reserved_balance(who: &AccountId) -> Self::Balance; + + // PUBLIC MUTABLES (DANGEROUS) + + /// 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`. + fn slash(who: &AccountId, value: Self::Balance) -> Option; + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, nothing is done and an Err returned. + fn reward(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>; + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, it is created + /// + /// Returns if the account was successfully updated or update has led to killing of the account. + fn reward_creating(who: &AccountId, value: Self::Balance) -> UpdateBalanceOutcome; + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, nothing is done and an Err returned. + /// + /// NOTE: This assumes that the total stake remains unchanged after this operation. + fn reward_with_no_stake_increase(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>; + + /// Adds up to `value` to the free balance of `who`. + /// + /// If `who` doesn't exist, it is created + /// + /// Returns if the account was successfully updated or update has led to killing of the account. + /// + /// NOTE: This assumes that the total stake remains unchanged after this operation. + fn reward_with_no_stake_increase_creating(who: &AccountId, value: Self::Balance) -> UpdateBalanceOutcome; + + /// Moves `value` from balance to reserved balance. + /// + /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will + /// be returned to notify of this. This is different behaviour to `unreserve`. + fn reserve(who: &AccountId, value: Self::Balance) -> result::Result<(), &'static str>; + + /// 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`. + fn unreserve(who: &AccountId, value: Self::Balance) -> Option; + + /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. + /// + /// As much funds up to `value` will be deducted as possible. If this is less than `value`, + /// then `Some(remaining)` will be returned. Full completion is given by `None`. + fn slash_reserved(who: &AccountId, value: Self::Balance) -> Option; + + /// 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)`. + fn repatriate_reserved( + slashed: &AccountId, + beneficiary: &AccountId, + value: Self::Balance + ) -> result::Result, &'static str>; +} + /// Simple payment making trait, operating on a single generic `AccountId` type. pub trait MakePayment { /// Make some sort of payment concerning `who` for an extrinsic (transaction) of encoded length diff --git a/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 00c4d4ce9f0f7..1860909e3dff1 100644 Binary files a/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index f769b284e2065..ba4b8513d6151 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -38,7 +38,7 @@ mod tests { twox_128, Blake2Hasher, ChangesTrieConfiguration, ed25519::{Public, Pair}, NeverNativeValue }; use node_primitives::{Hash, BlockNumber, AccountId}; - use runtime_primitives::traits::{Header as HeaderT, Digest as DigestT, Hash as HashT}; + use runtime_primitives::traits::{Header as HeaderT, Digest as DigestT, Hash as HashT, Staking}; use runtime_primitives::{generic, generic::Era, ApplyOutcome, ApplyError, ApplyResult, Perbill}; use {balances, indices, staking, session, system, consensus, timestamp, treasury, contract}; use contract::ContractAddressFor; diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 625e00921dedd..168e01d064efd 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -142,11 +142,13 @@ impl session::Trait for Runtime { } impl staking::Trait for Runtime { + type Staking = balances::Module; type OnRewardMinted = Treasury; type Event = Event; } impl democracy::Trait for Runtime { + type Staking = balances::Module; type Proposal = Call; type Event = Event; } @@ -166,6 +168,7 @@ impl council::motions::Trait for Runtime { } impl treasury::Trait for Runtime { + type Staking = balances::Module; type ApproveOrigin = council_motions::EnsureMembers<_4>; type RejectOrigin = council_motions::EnsureMembers<_2>; type Event = Event; diff --git a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 01f64762a5e60..a89568adbee15 100644 Binary files a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index 8f338e7b1344b..bf1c3ce367f12 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -47,63 +47,14 @@ use codec::Codec; use runtime_support::{StorageValue, StorageMap, Parameter}; use runtime_support::dispatch::Result; use primitives::traits::{Zero, SimpleArithmetic, MakePayment, - As, StaticLookup, Member, CheckedAdd, CheckedSub}; + As, StaticLookup, Member, CheckedAdd, CheckedSub, + UpdateBalanceOutcome, MaybeSerializeDebug, Staking, + EnsureAccountLiquid, OnFreeBalanceZero}; use system::{IsDeadAccount, OnNewAccount, ensure_signed}; mod mock; mod tests; -/// 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); - } -} - -/// Trait for a hook to get called when some balance has been minted, causing dilution. -pub trait OnDilution { - /// Some `portion` of the total balance just "grew" by `minted`. `portion` is the pre-growth - /// amount (it doesn't take account of the recent growth). - fn on_dilution(minted: Balance, portion: Balance); -} - -impl OnDilution for () { - fn on_dilution(_minted: Balance, _portion: Balance) {} -} - -/// 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< - AccountId, - X: EnsureAccountLiquid, - Y: EnsureAccountLiquid, -> EnsureAccountLiquid for (X, Y) { - fn ensure_account_liquid(who: &AccountId) -> Result { - X::ensure_account_liquid(who)?; - Y::ensure_account_liquid(who) - } -} -impl EnsureAccountLiquid for () { - fn ensure_account_liquid(_who: &AccountId) -> Result { Ok(()) } -} - pub trait Trait: system::Trait { /// The balance of an account. type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As + As; @@ -219,40 +170,8 @@ decl_storage! { } } -/// Outcome of a balance update. -pub enum UpdateBalanceOutcome { - /// Account balance was simply updated. - Updated, - /// The update has led to killing of the account. - AccountKilled, -} - +// For staking methods, see Staking trait 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 - } - } - - //PUBLIC MUTABLES (DANGEROUS) - /// Set the free balance of an account to some new value. /// /// Will enforce ExistentialDeposit law, anulling the account as needed. @@ -348,67 +267,6 @@ impl Module { Ok(Self::set_free_balance(who, b - value)) } - /// 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) - } - } - /// Transfer some liquid free balance to another staker. pub fn make_transfer(transactor: &T::AccountId, dest: &T::AccountId, value: T::Balance) -> Result { let from_balance = Self::free_balance(transactor); @@ -446,46 +304,6 @@ impl Module { Ok(()) } - /// 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)) - } - } /// Register a new account (with existential balance). fn new_account(who: &T::AccountId, balance: T::Balance) { @@ -534,6 +352,136 @@ impl Module { } } +impl Staking for Module +where + T::Balance: MaybeSerializeDebug +{ + type Balance = T::Balance; + + fn total_balance(who: &T::AccountId) -> Self::Balance { + Self::free_balance(who) + Self::reserved_balance(who) + } + + fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { + Self::free_balance(who) >= value + } + + fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { + if T::EnsureAccountLiquid::ensure_account_liquid(who).is_ok() { + Self::free_balance(who) >= value + } else { + false + } + } + + fn total_issuance() -> Self:: Balance { + Self::total_issuance() + } + + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::free_balance(who) + } + + fn reserved_balance(who: &T::AccountId) -> Self::Balance { + Self::reserved_balance(who) + } + + fn slash(who: &T::AccountId, value: Self::Balance) -> Option { + let free_balance = Self::free_balance(who); + let free_slash = cmp::min(free_balance, value); + Self::set_free_balance(who, free_balance - free_slash); + Self::decrease_total_stake_by(free_slash); + if free_slash < value { + Self::slash_reserved(who, value - free_slash) + } else { + None + } + } + + fn reward(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + 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(()) + } + + fn reward_creating(who: &T::AccountId, value: Self::Balance) -> UpdateBalanceOutcome { + let update = Self::set_free_balance_creating(who, Self::free_balance(who) + value); + if let UpdateBalanceOutcome::Updated = update { + Self::increase_total_stake_by(value); + } + update + } + + fn reward_with_no_stake_increase(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + if Self::total_balance(who).is_zero() { + return Err("beneficiary account must pre-exist"); + } + Self::set_free_balance(who, Self::free_balance(who) + value); + Ok(()) + } + + fn reward_with_no_stake_increase_creating(who: &T::AccountId, value: Self::Balance) -> UpdateBalanceOutcome { + Self::set_free_balance_creating(who, Self::free_balance(who) + value) + } + + fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> { + 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(()) + } + + fn unreserve(who: &T::AccountId, value: Self::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) + } + } + + fn slash_reserved(who: &T::AccountId, value: Self::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) + } + } + + fn repatriate_reserved( + slashed: &T::AccountId, + beneficiary: &T::AccountId, + value: Self::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)) + } + } +} + impl MakePayment for Module { fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result { let b = Self::free_balance(transactor); @@ -547,7 +495,10 @@ impl MakePayment for Module { } } -impl IsDeadAccount for Module { +impl IsDeadAccount for Module +where + T::Balance: MaybeSerializeDebug +{ fn is_dead_account(who: &T::AccountId) -> bool { Self::total_balance(who).is_zero() } diff --git a/srml/contract/src/account_db.rs b/srml/contract/src/account_db.rs index bac3b547d2546..4c6a24151aa03 100644 --- a/srml/contract/src/account_db.rs +++ b/srml/contract/src/account_db.rs @@ -22,6 +22,7 @@ use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; use runtime_support::{StorageMap, StorageDoubleMap}; +use runtime_primitives::traits::UpdateBalanceOutcome; pub struct ChangeEntry { balance: Option, @@ -65,7 +66,7 @@ impl AccountDb for DirectAccountDb { fn commit(&mut self, s: ChangeSet) { for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance { - if let balances::UpdateBalanceOutcome::AccountKilled = + if let UpdateBalanceOutcome::AccountKilled = balances::Module::::set_free_balance_creating(&address, balance) { // Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback diff --git a/srml/contract/src/exec.rs b/srml/contract/src/exec.rs index a4024f439a1f0..8ca75df94aa05 100644 --- a/srml/contract/src/exec.rs +++ b/srml/contract/src/exec.rs @@ -18,9 +18,8 @@ use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait}; use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}; use crate::gas::{GasMeter, Token, approx_gas_for_balance}; -use balances::{self, EnsureAccountLiquid}; use rstd::prelude::*; -use runtime_primitives::traits::{CheckedAdd, CheckedSub, Zero}; +use runtime_primitives::traits::{CheckedAdd, CheckedSub, Zero, EnsureAccountLiquid}; pub type BalanceOf = ::Balance; pub type AccountIdOf = ::AccountId; diff --git a/srml/contract/src/lib.rs b/srml/contract/src/lib.rs index 2f278b2a2c8fb..b786317ed8995 100644 --- a/srml/contract/src/lib.rs +++ b/srml/contract/src/lib.rs @@ -76,7 +76,7 @@ use crate::account_db::AccountDb; use rstd::prelude::*; use rstd::marker::PhantomData; use codec::Codec; -use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup}; +use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup, OnFreeBalanceZero}; use runtime_support::dispatch::{Result, Dispatchable}; use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap}; use system::{ensure_signed, RawOrigin}; @@ -368,7 +368,7 @@ impl StorageDoubleMap for StorageOf { } } -impl balances::OnFreeBalanceZero for Module { +impl OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { >::remove(who); >::remove_prefix(who.clone()); diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 60320c0738dc1..24e828142cfa4 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -24,6 +24,8 @@ extern crate serde; #[cfg(test)] #[macro_use] extern crate hex_literal; +#[cfg(test)] +extern crate srml_balances as balances; extern crate parity_codec as codec; #[macro_use] extern crate parity_codec_derive; @@ -33,7 +35,6 @@ extern crate sr_std as rstd; extern crate sr_io as runtime_io; #[macro_use] extern crate srml_support; extern crate sr_primitives as primitives; -extern crate srml_balances as balances; extern crate srml_democracy as democracy; extern crate srml_system as system; @@ -98,6 +99,7 @@ mod tests { type Event = Event; } impl democracy::Trait for Test { + type Staking = balances::Module; type Proposal = Call; type Event = Event; } diff --git a/srml/council/src/seats.rs b/srml/council/src/seats.rs index 6f2adc9bc7178..6a94565701463 100644 --- a/srml/council/src/seats.rs +++ b/srml/council/src/seats.rs @@ -17,11 +17,10 @@ //! Council system: Handles the voting in and maintenance of council members. use rstd::prelude::*; -use primitives::traits::{Zero, One, As, StaticLookup}; +use primitives::traits::{Zero, One, As, StaticLookup, Staking}; use runtime_io::print; use srml_support::{StorageValue, StorageMap, dispatch::Result}; use democracy; -use balances; use system::{self, ensure_signed}; // no polynomial attacks: @@ -80,6 +79,8 @@ use system::{self, ensure_signed}; pub type VoteIndex = u32; +type BalanceOf = <::Staking as Staking<::AccountId>>::Balance; + pub trait Trait: democracy::Trait { type Event: From> + Into<::Event>; } @@ -105,7 +106,7 @@ decl_module! { if !>::exists(&who) { // not yet a voter - deduct bond. // NOTE: this must be the last potential bailer, since it changes state. - >::reserve(&who, Self::voting_bond())?; + T::Staking::reserve(&who, Self::voting_bond())?; >::put({ let mut v = Self::voters(); @@ -161,10 +162,10 @@ decl_module! { if valid { // This only fails if `reporter` doesn't exist, which it clearly must do since its the origin. // Still, it's no more harmful to propagate any error at this point. - >::repatriate_reserved(&who, &reporter, Self::voting_bond())?; + T::Staking::repatriate_reserved(&who, &reporter, Self::voting_bond())?; Self::deposit_event(RawEvent::VoterReaped(who, reporter)); } else { - >::slash_reserved(&reporter, Self::voting_bond()); + T::Staking::slash_reserved(&reporter, Self::voting_bond()); Self::deposit_event(RawEvent::BadReaperSlashed(reporter)); } } @@ -181,7 +182,7 @@ decl_module! { ensure!(voters[index] == who, "retraction index mismatch"); Self::remove_voter(&who, index, voters); - >::unreserve(&who, Self::voting_bond()); + T::Staking::unreserve(&who, Self::voting_bond()); } /// Submit oneself for candidacy. @@ -200,7 +201,7 @@ decl_module! { "invalid candidate slot" ); // NOTE: This must be last as it has side-effects. - >::reserve(&who, Self::candidacy_bond()) + T::Staking::reserve(&who, Self::candidacy_bond()) .map_err(|_| "candidate has not enough funds")?; >::insert(&who, (Self::vote_index(), slot as u32)); @@ -220,7 +221,7 @@ decl_module! { fn present_winner( origin, candidate: ::Source, - #[compact] total: T::Balance, + #[compact] total: BalanceOf, #[compact] index: VoteIndex ) -> Result { let who = ensure_signed(origin)?; @@ -231,8 +232,8 @@ decl_module! { 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(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds"); + let bad_presentation_punishment = Self::present_slash_per_voter() * BalanceOf::::sa(voters.len() as u64); + ensure!(T::Staking::can_slash(&who, 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"); @@ -263,7 +264,7 @@ decl_module! { } else { // we can rest assured it will be Ok since we checked `can_slash` earlier; still // better safe than sorry. - let _ = >::slash(&who, bad_presentation_punishment); + let _ = T::Staking::slash(&who, bad_presentation_punishment); Err(if dupe { "duplicate presentation" } else { "incorrect total" }) } } @@ -313,11 +314,11 @@ decl_storage! { // parameters /// How much should be locked up in order to submit one's candidacy. - pub CandidacyBond get(candidacy_bond) config(): T::Balance = T::Balance::sa(9); + pub CandidacyBond get(candidacy_bond) config(): BalanceOf = BalanceOf::::sa(9); /// How much should be locked up in order to be able to submit votes. - pub VotingBond get(voting_bond) config(voter_bond): T::Balance; + pub VotingBond get(voting_bond) config(voter_bond): BalanceOf; /// The punishment, per voter, if you provide an invalid presentation. - pub PresentSlashPerVoter get(present_slash_per_voter) config(): T::Balance = T::Balance::sa(1); + pub PresentSlashPerVoter get(present_slash_per_voter) config(): BalanceOf = BalanceOf::::sa(1); /// How many runners-up should have their approvals persist until the next vote. pub CarryCount get(carry_count) config(): u32 = 2; /// How long to give each top candidate to present themselves after the vote ends. @@ -360,9 +361,9 @@ decl_storage! { /// The accounts holding the seats that will become free on the next tally. pub NextFinalise get(next_finalise): Option<(T::BlockNumber, u32, Vec)>; /// The stakes as they were at the point that the vote ended. - pub SnapshotedStakes get(snapshoted_stakes): Vec; + pub SnapshotedStakes get(snapshoted_stakes): Vec>; /// Get the leaderboard if we;re in the presentation phase. - pub Leaderboard get(leaderboard): Option >; // ORDERED low -> high + pub Leaderboard get(leaderboard): Option, T::AccountId)> >; // ORDERED low -> high } } @@ -466,12 +467,12 @@ impl Module { >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); let voters = Self::voters(); - let votes = voters.iter().map(>::total_balance).collect::>(); + let votes = voters.iter().map(T::Staking::total_balance).collect::>(); >::put(votes); // initialise leaderboard. let leaderboard_size = empty_seats + Self::carry_count() as usize; - >::put(vec![(T::Balance::zero(), T::AccountId::default()); leaderboard_size]); + >::put(vec![(BalanceOf::::zero(), T::AccountId::default()); leaderboard_size]); Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32)); } @@ -485,7 +486,7 @@ impl Module { >::kill(); let (_, coming, expiring): (T::BlockNumber, u32, Vec) = >::take().ok_or("finalise can only be called after a tally is started.")?; - let leaderboard: Vec<(T::Balance, T::AccountId)> = >::take().unwrap_or_default(); + let leaderboard: Vec<(BalanceOf, T::AccountId)> = >::take().unwrap_or_default(); let new_expiry = >::block_number() + Self::term_duration(); // return bond to winners. @@ -496,7 +497,7 @@ impl Module { .take(coming as usize) .map(|(_, a)| a) .cloned() - .inspect(|a| {>::unreserve(a, candidacy_bond);}) + .inspect(|a| {T::Staking::unreserve(a, candidacy_bond);}) .collect(); let active_council = Self::active_council(); let outgoing = active_council.iter().take(expiring.len()).map(|a| a.0.clone()).collect(); diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 6f9bc4630657c..7062e24b81af7 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -20,6 +20,8 @@ #[cfg(test)] extern crate substrate_primitives; +#[cfg(test)] +extern crate srml_balances as balances; #[macro_use] extern crate parity_codec_derive; @@ -31,12 +33,11 @@ extern crate srml_support; extern crate parity_codec as codec; extern crate sr_io as runtime_io; extern crate sr_primitives as primitives; -extern crate srml_balances as balances; extern crate srml_system as system; use rstd::prelude::*; use rstd::result; -use primitives::traits::{Zero, As}; +use primitives::traits::{Zero, As, Staking, OnFreeBalanceZero, EnsureAccountLiquid}; use srml_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType}; use srml_support::dispatch::Result; use system::ensure_signed; @@ -78,7 +79,11 @@ impl Vote { } } -pub trait Trait: balances::Trait + Sized { +type BalanceOf = <::Staking as Staking<::AccountId>>::Balance; + +pub trait Trait: system::Trait + Sized { + type Staking: Staking<::AccountId>; + type Proposal: Parameter + Dispatchable + IsSubType>; type Event: From> + Into<::Event>; @@ -92,12 +97,12 @@ decl_module! { fn propose( origin, proposal: Box, - #[compact] value: T::Balance + #[compact] value: BalanceOf ) { let who = ensure_signed(origin)?; ensure!(value >= Self::minimum_deposit(), "value too low"); - >::reserve(&who, value) + T::Staking::reserve(&who, value) .map_err(|_| "proposer's balance too low")?; let index = Self::public_prop_count(); @@ -114,7 +119,7 @@ decl_module! { let who = ensure_signed(origin)?; let mut deposit = Self::deposit_of(proposal) .ok_or("can only second an existing proposal")?; - >::reserve(&who, deposit.0) + T::Staking::reserve(&who, deposit.0) .map_err(|_| "seconder's balance too low")?; deposit.1.push(who); >::insert(proposal, deposit); @@ -126,7 +131,7 @@ decl_module! { let who = ensure_signed(origin)?; ensure!(vote.multiplier() <= Self::max_lock_periods(), "vote has too great a strength"); ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum."); - ensure!(!>::total_balance(&who).is_zero(), + ensure!(!T::Staking::total_balance(&who).is_zero(), "transactor must have balance to signal approval."); if !>::exists(&(ref_index, who.clone())) { >::mutate(ref_index, |voters| voters.push(who.clone())); @@ -193,11 +198,11 @@ decl_storage! { /// The public proposals. Unsorted. pub PublicProps get(public_props): Vec<(PropIndex, T::Proposal, T::AccountId)>; /// Those who have locked a deposit. - pub DepositOf get(deposit_of): map PropIndex => Option<(T::Balance, Vec)>; + pub DepositOf get(deposit_of): map PropIndex => Option<(BalanceOf, Vec)>; /// How often (in blocks) new public referenda are launched. pub LaunchPeriod get(launch_period) config(): T::BlockNumber = T::BlockNumber::sa(1000); /// The minimum amount to be used as a deposit for a public referendum proposal. - pub MinimumDeposit get(minimum_deposit) config(): T::Balance; + pub MinimumDeposit get(minimum_deposit) config(): BalanceOf; /// The delay before enactment for all public referenda. pub PublicDelay get(public_delay) config(): T::BlockNumber; /// The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`. @@ -230,7 +235,7 @@ decl_storage! { decl_event!( /// An event in this module. - pub enum Event where ::Balance, ::AccountId { + pub enum Event where Balance = <::Staking as Staking<::AccountId>>::Balance, ::AccountId { Tabled(PropIndex, Balance, Vec), Started(ReferendumIndex, VoteThreshold), Passed(ReferendumIndex), @@ -245,8 +250,8 @@ impl Module { /// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal /// index. - pub fn locked_for(proposal: PropIndex) -> Option { - Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len() as u64)) + pub fn locked_for(proposal: PropIndex) -> Option> { + Self::deposit_of(proposal).map(|(d, l)| d * BalanceOf::::sa(l.len() as u64)) } /// Return true if `ref_index` is an on-going referendum. @@ -274,17 +279,17 @@ impl Module { } /// Get the voters for the current proposal. - pub fn tally(ref_index: ReferendumIndex) -> (T::Balance, T::Balance, T::Balance) { + pub fn tally(ref_index: ReferendumIndex) -> (BalanceOf, BalanceOf, BalanceOf) { Self::voters_for(ref_index).iter() .map(|voter| ( - >::total_balance(voter), + T::Staking::total_balance(voter), Self::vote_of((ref_index, voter.clone())), )) .map(|(bal, vote)| if vote.is_aye() { - (bal * T::Balance::sa(vote.multiplier() as u64), Zero::zero(), bal) + (bal * BalanceOf::::sa(vote.multiplier() as u64), Zero::zero(), bal) } else { - (Zero::zero(), bal * T::Balance::sa(vote.multiplier() as u64), bal) + (Zero::zero(), bal * BalanceOf::::sa(vote.multiplier() as u64), bal) } ).fold((Zero::zero(), Zero::zero(), Zero::zero()), |(a, b, c), (d, e, f)| (a + d, b + e, c + f)) } @@ -346,10 +351,10 @@ impl Module { let (prop_index, proposal, _) = public_props.swap_remove(winner_index); >::put(public_props); - if let Some((deposit, depositors)) = >::take(prop_index) {//: (T::Balance, Vec) = + if let Some((deposit, depositors)) = >::take(prop_index) {//: (BalanceOf, Vec) = // refund depositors for d in &depositors { - >::unreserve(d, deposit); + T::Staking::unreserve(d, deposit); } Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors)); Self::inject_referendum(now + Self::voting_period(), proposal, VoteThreshold::SuperMajorityApprove, Self::public_delay())?; @@ -361,7 +366,7 @@ impl Module { fn bake_referendum(now: T::BlockNumber, index: ReferendumIndex, info: ReferendumInfo) -> Result { let (approve, against, capital) = Self::tally(index); - let total_issuance = >::total_issuance(); + let total_issuance = T::Staking::total_issuance(); let approved = info.threshold.approved(approve, against, capital, total_issuance); let lock_period = Self::public_delay(); @@ -415,13 +420,13 @@ impl Module { } } -impl balances::OnFreeBalanceZero for Module { +impl OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { >::remove(who); } } -impl balances::EnsureAccountLiquid for Module { +impl EnsureAccountLiquid for Module { fn ensure_account_liquid(who: &T::AccountId) -> Result { if Self::bondage(who) <= >::block_number() { Ok(()) @@ -478,6 +483,7 @@ mod tests { type Event = (); } impl Trait for Test { + type Staking = balances::Module; type Proposal = Call; type Event = (); } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index fb902ab49e63e..04eadc2ebbaa0 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -285,7 +285,7 @@ mod tests { use runtime_io::with_externalities; use substrate_primitives::{H256, Blake2Hasher}; use primitives::BuildStorage; - use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup}; + use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup, Staking}; use primitives::testing::{Digest, DigestItem, Header, Block}; use system; diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 15f0beadf7b0d..00a91fdbad680 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -33,7 +33,6 @@ extern crate parity_codec_derive; extern crate parity_codec as codec; extern crate sr_primitives as primitives; -extern crate srml_balances as balances; extern crate srml_consensus as consensus; extern crate srml_session as session; extern crate srml_system as system; @@ -44,13 +43,16 @@ extern crate substrate_primitives; extern crate sr_io as runtime_io; #[cfg(test)] extern crate srml_timestamp as timestamp; +#[cfg(test)] +extern crate srml_balances as balances; use rstd::{prelude::*, cmp}; use codec::HasCompact; use runtime_support::{Parameter, StorageValue, StorageMap, dispatch::Result}; use session::OnSessionChange; -use primitives::{Perbill, traits::{Zero, One, Bounded, As, StaticLookup}}; -use balances::OnDilution; +use primitives::Perbill; +use primitives::traits::{Zero, One, Bounded, As, StaticLookup, Staking, OnDilution, + EnsureAccountLiquid, OnFreeBalanceZero}; use system::ensure_signed; mod mock; @@ -88,9 +90,14 @@ impl Default for ValidatorPrefs { } } -pub trait Trait: balances::Trait + session::Trait { +type BalanceOf = <::Staking as Staking<::AccountId>>::Balance; + +pub trait Trait: system::Trait + session::Trait { + /// The staking balance. + type Staking: Staking; + /// Some tokens minted. - type OnRewardMinted: OnDilution<::Balance>; + type OnRewardMinted: OnDilution<>::Balance>; /// The overarching event type. type Event: From> + Into<::Event>; @@ -181,7 +188,7 @@ decl_module! { fn register_preferences( origin, #[compact] intentions_index: u32, - prefs: ValidatorPrefs + prefs: ValidatorPrefs> ) { let who = ensure_signed(origin)?; @@ -227,7 +234,7 @@ decl_module! { /// An event in this module. decl_event!( - pub enum Event where ::Balance, ::AccountId { + pub enum Event where Balance = <::Staking as Staking<::AccountId>>::Balance, ::AccountId { /// 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 @@ -265,7 +272,7 @@ decl_storage! { /// The current era index. pub CurrentEra get(current_era) config(): T::BlockNumber; /// Preferences that a validator has. - pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs; + pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs>; /// All the accounts with a desire to stake. pub Intentions get(intentions) config(): Vec; /// All nominator -> nominee relationships. @@ -276,9 +283,9 @@ decl_storage! { pub CurrentNominatorsFor get(current_nominators_for): map T::AccountId => Vec; /// Maximum reward, per validator, that is provided per acceptable session. - pub CurrentSessionReward get(current_session_reward) config(): T::Balance; + pub CurrentSessionReward get(current_session_reward) config(): BalanceOf; /// Slash, per validator that is taken for the first time they are found to be offline. - pub CurrentOfflineSlash get(current_offline_slash) config(): T::Balance; + pub CurrentOfflineSlash get(current_offline_slash) config(): BalanceOf; /// The next value of sessions per era. pub NextSessionsPerEra get(next_sessions_per_era): Option; @@ -286,7 +293,7 @@ decl_storage! { pub LastEraLengthChange get(last_era_length_change): T::BlockNumber; /// The highest and lowest staked validator slashable balances. - pub StakeRange get(stake_range): PairOf; + pub StakeRange get(stake_range): PairOf>; /// The block at which the `who`'s funds become entirely liquid. pub Bondage get(bondage): map T::AccountId => T::BlockNumber; @@ -313,17 +320,17 @@ impl Module { } /// Balance of a (potential) validator that includes all nominators. - pub fn nomination_balance(who: &T::AccountId) -> T::Balance { + pub fn nomination_balance(who: &T::AccountId) -> BalanceOf { Self::nominators_for(who).iter() - .map(>::total_balance) + .map(T::Staking::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 { + pub fn slashable_balance(who: &T::AccountId) -> BalanceOf { Self::nominators_for(who).iter() - .map(>::total_balance) - .fold(>::total_balance(who), |acc, x| acc + x) + .map(T::Staking::total_balance) + .fold(T::Staking::total_balance(who), |acc, x| acc + x) } /// The block at which the `who`'s funds become entirely liquid. @@ -344,20 +351,20 @@ impl Module { /// 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) { + fn slash_validator(v: &T::AccountId, slash: BalanceOf) { // skip the slash in degenerate case of having only 4 staking participants despite having a larger // desired number of validators (validator_count). if Self::intentions().len() <= Self::minimum_validator_count() as usize { return } - if let Some(rem) = >::slash(v, slash) { + if let Some(rem) = T::Staking::slash(v, slash) { let noms = Self::current_nominators_for(v); - let total = noms.iter().map(>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x); + let total = noms.iter().map(T::Staking::total_balance).fold(BalanceOf::::zero(), |acc, x| acc + x); if !total.is_zero() { let safe_mul_rational = |b| b * rem / total;// FIXME #1572 avoid overflow for n in noms.iter() { - let _ = >::slash(n, safe_mul_rational(>::total_balance(n))); // best effort - not much that can be done on fail. + let _ = T::Staking::slash(n, safe_mul_rational(T::Staking::total_balance(n))); // best effort - not much that can be done on fail. } } } @@ -365,7 +372,7 @@ impl Module { /// Reward a given validator by a specific amount. Add the reward to their, and their nominators' /// balance, pro-rata. - fn reward_validator(who: &T::AccountId, reward: T::Balance) { + fn reward_validator(who: &T::AccountId, reward: BalanceOf) { let off_the_table = reward.min(Self::validator_preferences(who).validator_payment); let reward = reward - off_the_table; let validator_cut = if reward.is_zero() { @@ -373,16 +380,16 @@ impl Module { } else { let noms = Self::current_nominators_for(who); let total = noms.iter() - .map(>::total_balance) - .fold(>::total_balance(who), |acc, x| acc + x) + .map(T::Staking::total_balance) + .fold(T::Staking::total_balance(who), |acc, x| acc + x) .max(One::one()); let safe_mul_rational = |b| b * reward / total;// FIXME #1572: avoid overflow for n in noms.iter() { - let _ = >::reward(n, safe_mul_rational(>::total_balance(n))); + let _ = T::Staking::reward(n, safe_mul_rational(T::Staking::total_balance(n))); } - safe_mul_rational(>::total_balance(who)) + safe_mul_rational(T::Staking::total_balance(who)) }; - let _ = >::reward(who, validator_cut + off_the_table); + let _ = T::Staking::reward(who, validator_cut + off_the_table); } /// Actually carry out the unstake operation. @@ -401,13 +408,13 @@ impl Module { } /// Get the reward for the session, assuming it ends with this block. - fn this_session_reward(actual_elapsed: T::Moment) -> T::Balance { + fn this_session_reward(actual_elapsed: T::Moment) -> BalanceOf { let ideal_elapsed = >::ideal_session_duration(); if ideal_elapsed.is_zero() { return Self::current_session_reward(); } let per65536: u64 = (T::Moment::sa(65536u64) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed)).as_(); - Self::current_session_reward() * T::Balance::sa(per65536) / T::Balance::sa(65536u64) + Self::current_session_reward() * BalanceOf::::sa(per65536) / BalanceOf::::sa(65536u64) } /// Session has just changed. We need to determine whether we pay a reward, slash and/or @@ -421,8 +428,8 @@ impl Module { Self::reward_validator(v, reward); } Self::deposit_event(RawEvent::Reward(reward)); - let total_minted = reward * >::sa(validators.len()); - let total_rewarded_stake = Self::stake_range().1 * >::sa(validators.len()); + let total_minted = reward * as As>::sa(validators.len()); + let total_rewarded_stake = Self::stake_range().1 * as As>::sa(validators.len()); T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake); } @@ -518,7 +525,7 @@ impl Module { let base_slash = Self::current_offline_slash(); let instances = slash_count - grace; - let mut total_slash = T::Balance::default(); + let mut total_slash = BalanceOf::::default(); for i in instances..(instances + count as u32) { if let Some(total) = base_slash.checked_shl(i) .and_then(|slash| total_slash.checked_add(&slash)) { @@ -572,7 +579,7 @@ impl OnSessionChange for Module { } } -impl balances::EnsureAccountLiquid for Module { +impl EnsureAccountLiquid for Module { fn ensure_account_liquid(who: &T::AccountId) -> Result { if Self::bondage(who) <= >::block_number() { Ok(()) @@ -582,7 +589,7 @@ impl balances::EnsureAccountLiquid for Module { } } -impl balances::OnFreeBalanceZero for Module { +impl OnFreeBalanceZero for Module { fn on_free_balance_zero(who: &T::AccountId) { >::remove(who); } diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 68145a7a70ae3..56034eb94ede8 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -66,6 +66,7 @@ impl timestamp::Trait for Test { type OnTimestampSet = (); } impl Trait for Test { + type Staking = balances::Module; type OnRewardMinted = (); type Event = (); } diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index e6496159a1b03..91b10df680518 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -21,6 +21,7 @@ use super::*; use runtime_io::with_externalities; use mock::{Balances, Session, Staking, System, Timestamp, Test, new_test_ext, Origin}; +use primitives::traits::Staking as StakingTrait; #[test] fn note_null_offline_should_work() { diff --git a/srml/support/src/event.rs b/srml/support/src/event.rs index a3eb9ccee383b..aeb9df5541365 100644 --- a/srml/support/src/event.rs +++ b/srml/support/src/event.rs @@ -94,18 +94,12 @@ macro_rules! decl_event { ( $(#[$attr:meta])* pub enum Event<$evt_generic_param:ident> where - $( $( $generic_rename:ident = )* <$generic:ident as $trait:path>::$trait_type:ident ),* - { - $( - $events:tt - )* - } + $( $tt:tt )* ) => { __decl_generic_event!( $( #[ $attr ] )*; $evt_generic_param; - $( $( $generic_rename = )* <$generic as $trait>::$trait_type ),*; - Events { $( $events )* }; + { $( $tt )* }; ); }; ( @@ -139,84 +133,102 @@ macro_rules! decl_event { #[macro_export] #[doc(hidden)] +// This parsing to retrieve last ident on unnamed generic could be improved. +// but user can still name it if the parsing fails. And improving parsing seems difficult. macro_rules! __decl_generic_event { ( $(#[$attr:meta])*; $event_generic_param:ident; - $generic_rename:ident = <$generic:ident as $trait:path>::$trait_type:ident - $(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*; - Events { $( $events:tt )* }; + { $( $tt:tt )* }; ) => { - __decl_generic_event!( - $( #[ $attr ] )*; + __decl_generic_event!(@format_generic + $(#[$attr])*; $event_generic_param; - $( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*; - Events { $( $events )* }; - $generic_rename; - <$generic as $trait>::$trait_type; + { $( $tt )* }; + {}; ); }; - ( + // Parse named + (@format_generic $(#[$attr:meta])*; $event_generic_param:ident; - $generic_rename:ident = <$generic:ident as $trait:path>::$trait_type:ident - $(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*; - Events { $( $events:tt )* }; - $( $parsed_generic_params:ident ),*; - $( <$parsed_generic:ident as $parsed_trait:path>::$parsed_trait_type:ident ),*; + { $generic_rename:ident = $generic_type:ty, $($rest:tt)* }; + {$( $parsed:tt)*}; ) => { - __decl_generic_event!( - $( #[ $attr ] )*; + __decl_generic_event!(@format_generic + $(#[$attr])*; $event_generic_param; - $( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*; - Events { $( $events )* }; - $( $parsed_generic_params ),*, $generic_rename; - $( <$parsed_generic as $parsed_trait>::$parsed_trait_type ),*, <$generic as $trait>::$trait_type; + { $($rest)* }; + { $($parsed)*, $generic_rename = $generic_type }; ); }; - ( + // Parse unnamed + (@format_generic $(#[$attr:meta])*; $event_generic_param:ident; - <$generic:ident as $trait:path>::$trait_type:ident - $(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*; - Events { $( $events:tt )* }; + { <$generic:ident as $trait:path>::$trait_type:ident, $($rest:tt)* }; + {$($parsed:tt)*}; ) => { - __decl_generic_event!( - $( #[ $attr ] )*; + __decl_generic_event!(@format_generic + $(#[$attr])*; $event_generic_param; - $( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*; - Events { $( $events )* }; - $trait_type; - <$generic as $trait>::$trait_type; + { $($rest)* }; + { $($parsed)*, $trait_type = <$generic as $trait>::$trait_type }; ); }; - ( + // Unnamed type can't be parsed + (@format_generic $(#[$attr:meta])*; $event_generic_param:ident; - <$generic:ident as $trait:path>::$trait_type:ident - $(, $( $rest_gen_rename:ident = )* <$rest_gen:ident as $rest_trait:path>::$rest_trait_type:ident )*; - Events { $( $events:tt )* }; - $( $parsed_generic_params:ident ),*; - $( <$parsed_generic:ident as $parsed_trait:path>::$parsed_trait_type:ident ),*; + { $generic_type:ty, $($rest:tt)* }; + {$($parsed:tt)*}; ) => { - __decl_generic_event!( - $( #[ $attr ] )*; + __decl_generic_event!(@cannot_parse $generic_type); + }; + // Finish formatting on an unnamed one + (@format_generic + $(#[$attr:meta])*; + $event_generic_param:ident; + { <$generic:ident as $trait:path>::$trait_type:ident { $( $events:tt )* } }; + {$( $parsed:tt)*}; + ) => { + __decl_generic_event!(@generate + $(#[$attr])*; $event_generic_param; - $( $( $rest_gen_rename = )* <$rest_gen as $rest_trait>::$rest_trait_type ),*; - Events { $( $events )* }; - $( $parsed_generic_params ),*, $trait_type; - $( <$parsed_generic as $parsed_trait>::$parsed_trait_type ),*, <$generic as $trait>::$trait_type; + { $($events)* }; + { $($parsed)*, $trait_type = <$generic as $trait>::$trait_type}; ); }; - ( + // Finish formatting on a named one + (@format_generic $(#[$attr:meta])*; $event_generic_param:ident; - ; - Events { $( $events:tt )* }; - $( $generic_param:ident ),*; - $( <$generic:ident as $trait:path>::$trait_type:ident ),*; + { $generic_rename:ident = $generic_type:ty { $( $events:tt )* } }; + {$( $parsed:tt)*}; ) => { - pub type Event<$event_generic_param> = RawEvent<$( <$generic as $trait>::$trait_type ),*>; + __decl_generic_event!(@generate + $(#[$attr])*; + $event_generic_param; + { $($events)* }; + { $($parsed)*, $generic_rename = $generic_type}; + ); + }; + // Final unnamed type can't be parsed + (@format_generic + $(#[$attr:meta])*; + $event_generic_param:ident; + { $generic_type:ty { $( $events:tt )* } }; + {$( $parsed:tt)*}; + ) => { + __decl_generic_event!(@cannot_parse $generic_type); + }; + (@generate + $(#[$attr:meta])*; + $event_generic_param:ident; + { $( $events:tt )* }; + { ,$( $generic_param:ident = $generic_type:ty ),* }; + ) => { + pub type Event<$event_generic_param> = RawEvent<$( $generic_type ),*>; // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] @@ -235,6 +247,9 @@ macro_rules! __decl_generic_event { __events_to_metadata!(; $( $events )* ) } } + }; + (@cannot_parse $ty:ty) => { + compile_error!(concat!("The type `", stringify!($ty), "` can't be parsed as an unnamed one, please name it `Name = ", stringify!($ty), "`")); } } diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 5437ec5f82732..fd62bcf5f1716 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -25,6 +25,11 @@ extern crate srml_support as runtime_support; #[cfg(test)] extern crate sr_io as runtime_io; +#[cfg(test)] +extern crate substrate_primitives; +#[cfg(test)] +extern crate srml_balances as balances; + #[cfg(feature = "std")] extern crate serde; @@ -32,24 +37,25 @@ extern crate serde; extern crate parity_codec_derive; extern crate parity_codec as codec; -#[cfg(test)] -extern crate substrate_primitives; extern crate sr_primitives as runtime_primitives; extern crate srml_system as system; -extern crate srml_balances as balances; use rstd::prelude::*; use runtime_support::{StorageValue, StorageMap}; -use runtime_primitives::{Permill, traits::{Zero, EnsureOrigin, StaticLookup}}; -use balances::OnDilution; +use runtime_primitives::{Permill, traits::{Zero, EnsureOrigin, StaticLookup, Staking, OnDilution}}; use system::ensure_signed; +type BalanceOf = <::Staking as Staking<::AccountId>>::Balance; + /// Our module's configuration trait. All our types and consts go in here. If the /// module is dependent on specific other modules, then their configuration traits /// should be added to our implied traits list. /// /// `system::Trait` should always be included in our implied traits. -pub trait Trait: balances::Trait { +pub trait Trait: system::Trait { + /// The staking balance. + type Staking: Staking; + /// Origin from which approvals must come. type ApproveOrigin: EnsureOrigin; @@ -73,14 +79,14 @@ decl_module! { /// proposal is awarded. fn propose_spend( origin, - #[compact] value: T::Balance, + #[compact] value: BalanceOf, beneficiary: ::Source ) { let proposer = ensure_signed(origin)?; let beneficiary = T::Lookup::lookup(beneficiary)?; let bond = Self::calculate_bond(value); - >::reserve(&proposer, bond) + T::Staking::reserve(&proposer, bond) .map_err(|_| "Proposer's balance too low")?; let c = Self::proposal_count(); @@ -91,7 +97,7 @@ decl_module! { } /// Set the balance of funds available to spend. - fn set_pot(#[compact] new_pot: T::Balance) { + fn set_pot(#[compact] new_pot: BalanceOf) { // Put the new value into storage. >::put(new_pot); } @@ -99,7 +105,7 @@ decl_module! { /// (Re-)configure this module. fn configure( #[compact] proposal_bond: Permill, - #[compact] proposal_bond_minimum: T::Balance, + #[compact] proposal_bond_minimum: BalanceOf, #[compact] spend_period: T::BlockNumber, #[compact] burn: Permill ) { @@ -115,7 +121,7 @@ decl_module! { let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; let value = proposal.bond; - let _ = >::slash_reserved(&proposal.proposer, value); + let _ = T::Staking::slash_reserved(&proposal.proposer, value); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -156,7 +162,7 @@ decl_storage! { ProposalBond get(proposal_bond) config(): Permill; /// Minimum amount of funds that should be placed in a deposit for making a proposal. - ProposalBondMinimum get(proposal_bond_minimum) config(): T::Balance; + ProposalBondMinimum get(proposal_bond_minimum) config(): BalanceOf; /// Period between successive spends. SpendPeriod get(spend_period) config(): T::BlockNumber = runtime_primitives::traits::One::one(); @@ -167,13 +173,13 @@ decl_storage! { // State... /// Total funds available to this module for spending. - Pot get(pot): T::Balance; + Pot get(pot): BalanceOf; /// Number of proposals that have been made. ProposalCount get(proposal_count): ProposalIndex; /// Proposals that have been made. - Proposals get(proposals): map ProposalIndex => Option>; + Proposals get(proposals): map ProposalIndex => Option>>; /// Proposal indices that have been approved but not yet awarded. Approvals get(approvals): Vec; @@ -182,7 +188,11 @@ decl_storage! { /// An event in this module. decl_event!( - pub enum Event where ::Balance, ::AccountId { + pub enum Event + where + Balance = <::Staking as Staking<::AccountId>>::Balance, + ::AccountId + { /// New proposal. Proposed(ProposalIndex), /// We have ended a spend period and will now allocate funds. @@ -200,7 +210,7 @@ impl Module { // Add public immutables and private mutables. /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: T::Balance) -> T::Balance { + fn calculate_bond(value: BalanceOf) -> BalanceOf { Self::proposal_bond_minimum().max(Self::proposal_bond() * value) } @@ -219,10 +229,10 @@ impl Module { >::remove(index); // return their deposit. - let _ = >::unreserve(&p.proposer, p.bond); + let _ = T::Staking::unreserve(&p.proposer, p.bond); // provide the allocation. - >::increase_free_balance_creating(&p.beneficiary, p.value); + T::Staking::reward_with_no_stake_increase_creating(&p.beneficiary, p.value); Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); false @@ -249,12 +259,12 @@ impl Module { } } -impl OnDilution for Module { - fn on_dilution(minted: T::Balance, portion: T::Balance) { +impl OnDilution> for Module { + fn on_dilution(minted: BalanceOf, portion: BalanceOf) { // Mint extra funds for the treasury to keep the ratio of portion to total_issuance equal // pre dilution and post-dilution. if !minted.is_zero() && !portion.is_zero() { - let total_issuance = >::total_issuance(); + let total_issuance = T::Staking::total_issuance(); let funding = (total_issuance - portion) / portion * minted; >::mutate(|x| *x += funding); } @@ -298,11 +308,12 @@ mod tests { type Event = (); } impl Trait for Test { + type Staking = Staking; type ApproveOrigin = system::EnsureRoot; type RejectOrigin = system::EnsureRoot; type Event = (); } - type Balances = balances::Module; + type Staking = balances::Module; type Treasury = Module; fn new_test_ext() -> runtime_io::TestExternalities { @@ -349,8 +360,8 @@ mod tests { fn spend_proposal_takes_min_deposit() { with_externalities(&mut new_test_ext(), || { assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); - assert_eq!(Balances::free_balance(&0), 99); - assert_eq!(Balances::reserved_balance(&0), 1); + assert_eq!(Staking::free_balance(&0), 99); + assert_eq!(Staking::reserved_balance(&0), 1); }); } @@ -358,8 +369,8 @@ mod tests { fn spend_proposal_takes_proportional_deposit() { with_externalities(&mut new_test_ext(), || { assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); - assert_eq!(Balances::free_balance(&0), 95); - assert_eq!(Balances::reserved_balance(&0), 5); + assert_eq!(Staking::free_balance(&0), 95); + assert_eq!(Staking::reserved_balance(&0), 5); }); } @@ -379,7 +390,7 @@ mod tests { assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalise(1); - assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Staking::free_balance(&3), 0); assert_eq!(Treasury::pot(), 100); }); } @@ -403,7 +414,7 @@ mod tests { assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); >::on_finalise(2); - assert_eq!(Balances::free_balance(&3), 0); + assert_eq!(Staking::free_balance(&3), 0); assert_eq!(Treasury::pot(), 50); }); } @@ -453,7 +464,7 @@ mod tests { assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalise(2); - assert_eq!(Balances::free_balance(&3), 100); + assert_eq!(Staking::free_balance(&3), 100); assert_eq!(Treasury::pot(), 0); }); } @@ -471,7 +482,7 @@ mod tests { Treasury::on_dilution(100, 100); >::on_finalise(4); - assert_eq!(Balances::free_balance(&3), 150); + assert_eq!(Staking::free_balance(&3), 150); assert_eq!(Treasury::pot(), 25); }); }