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 8e2be26970eb2..4da496043fee1 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 eb2fe2e7b888a..d6701266d80d9 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -32,7 +32,7 @@ mod tests { use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; use keyring::Keyring; - use runtime_support::{Hashable, StorageValue, StorageMap}; + use runtime_support::{Hashable, StorageValue, StorageMap, traits::Currency}; use state_machine::{CodeExecutor, Externalities, TestExternalities}; use primitives::{ twox_128, Blake2Hasher, ChangesTrieConfiguration, ed25519::{Public, Pair}, NeverNativeValue diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 9cddd0f9b2e56..4328ac7f3a1cc 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -65,8 +65,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 24, - impl_version: 24, + spec_version: 25, + impl_version: 25, apis: RUNTIME_API_VERSIONS, }; @@ -141,11 +141,13 @@ impl session::Trait for Runtime { } impl staking::Trait for Runtime { + type Currency = balances::Module; type OnRewardMinted = Treasury; type Event = Event; } impl democracy::Trait for Runtime { + type Currency = balances::Module; type Proposal = Call; type Event = Event; } @@ -165,6 +167,7 @@ impl council::motions::Trait for Runtime { } impl treasury::Trait for Runtime { + type Currency = 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 a1c8643063356..a0162190b29be 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 a023dd0d59b46..5449901f7543b 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -31,65 +31,15 @@ use rstd::prelude::*; use rstd::{cmp, result}; use parity_codec::Codec; use runtime_support::{StorageValue, StorageMap, Parameter}; +use runtime_support::traits::{UpdateBalanceOutcome, Currency, EnsureAccountLiquid, OnFreeBalanceZero}; use runtime_support::dispatch::Result; use primitives::traits::{Zero, SimpleArithmetic, MakePayment, - As, StaticLookup, Member, CheckedAdd, CheckedSub}; + As, StaticLookup, Member, CheckedAdd, CheckedSub, MaybeSerializeDebug}; 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; @@ -205,40 +155,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 funding methods, see Currency 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. @@ -334,67 +252,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); @@ -432,46 +289,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) { @@ -520,6 +337,120 @@ impl Module { } } +impl Currency 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 increase_free_balance_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); @@ -533,7 +464,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..cc252586ea206 100644 --- a/srml/contract/src/account_db.rs +++ b/srml/contract/src/account_db.rs @@ -21,7 +21,7 @@ use {balances, system}; use rstd::cell::RefCell; use rstd::collections::btree_map::{BTreeMap, Entry}; use rstd::prelude::*; -use runtime_support::{StorageMap, StorageDoubleMap}; +use runtime_support::{StorageMap, StorageDoubleMap, traits::UpdateBalanceOutcome}; pub struct ChangeEntry { balance: Option, @@ -65,7 +65,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 67ba3b99def0c..5d96b5eb10cab 100644 --- a/srml/contract/src/exec.rs +++ b/srml/contract/src/exec.rs @@ -18,10 +18,10 @@ 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 timestamp; +use runtime_support::traits::EnsureAccountLiquid; pub type BalanceOf = ::Balance; pub type AccountIdOf = ::AccountId; diff --git a/srml/contract/src/lib.rs b/srml/contract/src/lib.rs index d0dd6a1770d5c..e1b51eb753f7e 100644 --- a/srml/contract/src/lib.rs +++ b/srml/contract/src/lib.rs @@ -79,6 +79,7 @@ use codec::Codec; use runtime_primitives::traits::{Hash, As, SimpleArithmetic,Bounded, StaticLookup}; use runtime_support::dispatch::{Result, Dispatchable}; use runtime_support::{Parameter, StorageMap, StorageValue, StorageDoubleMap}; +use runtime_support::traits::OnFreeBalanceZero; use system::{ensure_signed, RawOrigin}; use runtime_io::{blake2_256, twox_128}; use timestamp; @@ -369,7 +370,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 3eca2ffca0af6..03e6caef8c147 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; extern crate parity_codec_derive; @@ -32,7 +34,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; @@ -97,6 +98,7 @@ mod tests { type Event = Event; } impl democracy::Trait for Test { + type Currency = balances::Module; type Proposal = Call; type Event = Event; } diff --git a/srml/council/src/seats.rs b/srml/council/src/seats.rs index 6f2adc9bc7178..2f6716d70d04f 100644 --- a/srml/council/src/seats.rs +++ b/srml/council/src/seats.rs @@ -19,9 +19,8 @@ use rstd::prelude::*; use primitives::traits::{Zero, One, As, StaticLookup}; use runtime_io::print; -use srml_support::{StorageValue, StorageMap, dispatch::Result}; +use srml_support::{StorageValue, StorageMap, dispatch::Result, traits::Currency}; 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 = <::Currency as Currency<::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::Currency::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::Currency::repatriate_reserved(&who, &reporter, Self::voting_bond())?; Self::deposit_event(RawEvent::VoterReaped(who, reporter)); } else { - >::slash_reserved(&reporter, Self::voting_bond()); + T::Currency::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::Currency::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::Currency::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::Currency::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::Currency::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::Currency::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::Currency::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 f37e1e22340a7..15323bbcaa7f0 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; @@ -30,13 +32,13 @@ 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 srml_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType}; +use srml_support::traits::{Currency, OnFreeBalanceZero, EnsureAccountLiquid}; use srml_support::dispatch::Result; use system::ensure_signed; @@ -77,7 +79,11 @@ impl Vote { } } -pub trait Trait: balances::Trait + Sized { +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + +pub trait Trait: system::Trait + Sized { + type Currency: Currency<::AccountId>; + type Proposal: Parameter + Dispatchable + IsSubType>; type Event: From> + Into<::Event>; @@ -91,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::Currency::reserve(&who, value) .map_err(|_| "proposer's balance too low")?; let index = Self::public_prop_count(); @@ -113,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::Currency::reserve(&who, deposit.0) .map_err(|_| "seconder's balance too low")?; deposit.1.push(who); >::insert(proposal, deposit); @@ -125,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::Currency::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())); @@ -192,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`. @@ -229,7 +235,7 @@ decl_storage! { decl_event!( /// An event in this module. - pub enum Event where ::Balance, ::AccountId { + pub enum Event where Balance = BalanceOf, ::AccountId { Tabled(PropIndex, Balance, Vec), Started(ReferendumIndex, VoteThreshold), Passed(ReferendumIndex), @@ -244,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. @@ -273,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::Currency::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)) } @@ -345,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::Currency::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())?; @@ -360,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::Currency::total_issuance(); let approved = info.threshold.approved(approve, against, capital, total_issuance); let lock_period = Self::public_delay(); @@ -414,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(()) @@ -477,6 +483,7 @@ mod tests { type Event = (); } impl Trait for Test { + type Currency = balances::Module; type Proposal = Call; type Event = (); } diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 837672237f34d..725d5149a6462 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -304,6 +304,7 @@ mod tests { use primitives::BuildStorage; use primitives::traits::{Header as HeaderT, BlakeTwo256, IdentityLookup}; use primitives::testing::{Digest, DigestItem, Header, Block}; + use runtime_support::traits::Currency; use system; impl_outer_origin! { diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 554a8eba8fc99..6864f32989c18 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 runtime_support::traits::{Currency, OnDilution, EnsureAccountLiquid, OnFreeBalanceZero}; 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}; use system::ensure_signed; mod mock; @@ -89,9 +91,14 @@ impl Default for ValidatorPrefs { } } -pub trait Trait: balances::Trait + session::Trait { +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + +pub trait Trait: system::Trait + session::Trait { + /// The staking balance. + type Currency: Currency; + /// Some tokens minted. - type OnRewardMinted: OnDilution<::Balance>; + type OnRewardMinted: OnDilution>; /// The overarching event type. type Event: From> + Into<::Event>; @@ -182,7 +189,7 @@ decl_module! { fn register_preferences( origin, #[compact] intentions_index: u32, - prefs: ValidatorPrefs + prefs: ValidatorPrefs> ) { let who = ensure_signed(origin)?; @@ -228,7 +235,7 @@ decl_module! { /// An event in this module. decl_event!( - pub enum Event where ::Balance, ::AccountId { + pub enum Event where Balance = BalanceOf, ::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 @@ -266,7 +273,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. @@ -277,9 +284,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; @@ -287,7 +294,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; @@ -317,17 +324,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::Currency::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::Currency::total_balance) + .fold(T::Currency::total_balance(who), |acc, x| acc + x) } /// The block at which the `who`'s funds become entirely liquid. @@ -348,20 +355,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::Currency::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::Currency::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::Currency::slash(n, safe_mul_rational(T::Currency::total_balance(n))); // best effort - not much that can be done on fail. } } } @@ -369,7 +376,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() { @@ -377,16 +384,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::Currency::total_balance) + .fold(T::Currency::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::Currency::reward(n, safe_mul_rational(T::Currency::total_balance(n))); } - safe_mul_rational(>::total_balance(who)) + safe_mul_rational(T::Currency::total_balance(who)) }; - let _ = >::reward(who, validator_cut + off_the_table); + let _ = T::Currency::reward(who, validator_cut + off_the_table); } /// Actually carry out the unstake operation. @@ -405,13 +412,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 @@ -425,8 +432,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); } @@ -536,7 +543,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)) { @@ -590,7 +597,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(()) @@ -600,7 +607,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..ccf27ac432330 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 Currency = balances::Module; type OnRewardMinted = (); type Event = (); } diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index e6496159a1b03..79dfb98a60926 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 runtime_support::traits::Currency; #[test] fn note_null_offline_should_work() { diff --git a/srml/support/src/event.rs b/srml/support/src/event.rs index c97bbbb8bf900..aa438c22f9130 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 )* ) => { $crate::__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 )* }; ) => { - $crate::__decl_generic_event!( + $crate::__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)*}; ) => { - $crate::__decl_generic_event!( + $crate::__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)*}; ) => { - $crate::__decl_generic_event!( + $crate::__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)*}; ) => { - $crate::__decl_generic_event!( + $crate::__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)*}; + ) => { + $crate::__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 ),*>; + $crate::__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)*}; + ) => { + $crate::__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, $crate::parity_codec_derive::Encode, $crate::parity_codec_derive::Decode)] #[cfg_attr(feature = "std", derive(Debug))] @@ -235,6 +247,9 @@ macro_rules! __decl_generic_event { $crate::__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/support/src/lib.rs b/srml/support/src/lib.rs index 47551646eca3f..2c796311729a8 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -52,6 +52,7 @@ mod runtime; #[macro_use] pub mod inherent; mod double_map; +pub mod traits; pub use self::storage::{StorageVec, StorageList, StorageValue, StorageMap}; pub use self::hashable::Hashable; diff --git a/srml/support/src/traits.rs b/srml/support/src/traits.rs new file mode 100644 index 0000000000000..416ed4e959c41 --- /dev/null +++ b/srml/support/src/traits.rs @@ -0,0 +1,183 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Traits for SRML + +use crate::rstd::result; +use crate::codec::Codec; +use crate::runtime_primitives::traits::{MaybeSerializeDebug, SimpleArithmetic, As}; + +/// 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 a fungible assets system. +pub trait Currency { + /// 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. + /// + /// NOTE: This assumes that the total stake remains unchanged after this operation. + fn increase_free_balance_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>; +} diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 5437ec5f82732..8f0c7f5e8ee97 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,26 @@ 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_support::traits::{Currency, OnDilution}; use runtime_primitives::{Permill, traits::{Zero, EnsureOrigin, StaticLookup}}; -use balances::OnDilution; use system::ensure_signed; +type BalanceOf = <::Currency as Currency<::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 Currency: Currency; + /// Origin from which approvals must come. type ApproveOrigin: EnsureOrigin; @@ -73,14 +80,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::Currency::reserve(&proposer, bond) .map_err(|_| "Proposer's balance too low")?; let c = Self::proposal_count(); @@ -91,7 +98,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 +106,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 +122,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::Currency::slash_reserved(&proposal.proposer, value); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -156,7 +163,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 +174,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 +189,11 @@ decl_storage! { /// An event in this module. decl_event!( - pub enum Event where ::Balance, ::AccountId { + pub enum Event + where + Balance = <::Currency as Currency<::AccountId>>::Balance, + ::AccountId + { /// New proposal. Proposed(ProposalIndex), /// We have ended a spend period and will now allocate funds. @@ -200,7 +211,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 +230,10 @@ impl Module { >::remove(index); // return their deposit. - let _ = >::unreserve(&p.proposer, p.bond); + let _ = T::Currency::unreserve(&p.proposer, p.bond); // provide the allocation. - >::increase_free_balance_creating(&p.beneficiary, p.value); + T::Currency::increase_free_balance_creating(&p.beneficiary, p.value); Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); false @@ -249,12 +260,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::Currency::total_issuance(); let funding = (total_issuance - portion) / portion * minted; >::mutate(|x| *x += funding); } @@ -298,6 +309,7 @@ mod tests { type Event = (); } impl Trait for Test { + type Currency = balances::Module; type ApproveOrigin = system::EnsureRoot; type RejectOrigin = system::EnsureRoot; type Event = ();