decouple balances from some modules (#1641)

* decouple balances from some module by creating a new traits in support/traits
* improve decl_event
This commit is contained in:
thiolliere
2019-02-08 17:43:12 +01:00
committed by GitHub
parent 83f9e1e720
commit e5dbcf6890
19 changed files with 510 additions and 341 deletions
+1 -1
View File
@@ -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
+5 -2
View File
@@ -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<Self>;
type OnRewardMinted = Treasury;
type Event = Event;
}
impl democracy::Trait for Runtime {
type Currency = balances::Module<Self>;
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<Self>;
type ApproveOrigin = council_motions::EnsureMembers<_4>;
type RejectOrigin = council_motions::EnsureMembers<_2>;
type Event = Event;
+121 -187
View File
@@ -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<AccountId> {
/// The account was the given id was killed.
fn on_free_balance_zero(who: &AccountId);
}
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
fn on_free_balance_zero(_who: &AccountId) {}
}
impl<
AccountId,
X: OnFreeBalanceZero<AccountId>,
Y: OnFreeBalanceZero<AccountId>,
> OnFreeBalanceZero<AccountId> 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<Balance> {
/// 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<Balance> OnDilution<Balance> for () {
fn on_dilution(_minted: Balance, _portion: Balance) {}
}
/// Determinator for whether a given account is able to transfer balance.
pub trait EnsureAccountLiquid<AccountId> {
/// 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<AccountId>,
Y: EnsureAccountLiquid<AccountId>,
> EnsureAccountLiquid<AccountId> for (X, Y) {
fn ensure_account_liquid(who: &AccountId) -> Result {
X::ensure_account_liquid(who)?;
Y::ensure_account_liquid(who)
}
}
impl<AccountId> EnsureAccountLiquid<AccountId> 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<usize> + As<u64>;
@@ -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<T: Trait> Module<T> {
// 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<T: Trait> Module<T> {
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<T::Balance> {
let free_balance = Self::free_balance(who);
let free_slash = cmp::min(free_balance, value);
Self::set_free_balance(who, free_balance - free_slash);
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<T::Balance> {
let b = Self::reserved_balance(who);
let actual = cmp::min(b, value);
Self::set_free_balance(who, Self::free_balance(who) + actual);
Self::set_reserved_balance(who, b - actual);
if actual == value {
None
} else {
Some(value - actual)
}
}
/// 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<T: Trait> Module<T> {
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<T::Balance> {
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<Option<T::Balance>, &'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<T: Trait> Module<T> {
}
}
impl<T: Trait> Currency<T::AccountId> for Module<T>
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<Self::Balance> {
let free_balance = Self::free_balance(who);
let free_slash = cmp::min(free_balance, value);
Self::set_free_balance(who, free_balance - free_slash);
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<Self::Balance> {
let b = Self::reserved_balance(who);
let actual = cmp::min(b, value);
Self::set_free_balance(who, Self::free_balance(who) + actual);
Self::set_reserved_balance(who, b - actual);
if actual == value {
None
} else {
Some(value - actual)
}
}
fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> Option<Self::Balance> {
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<Option<Self::Balance>, &'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<T: Trait> MakePayment<T::AccountId> for Module<T> {
fn make_payment(transactor: &T::AccountId, encoded_len: usize) -> Result {
let b = Self::free_balance(transactor);
@@ -533,7 +464,10 @@ impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
}
}
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T> {
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T>
where
T::Balance: MaybeSerializeDebug
{
fn is_dead_account(who: &T::AccountId) -> bool {
Self::total_balance(who).is_zero()
}
+2 -2
View File
@@ -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<T: Trait> {
balance: Option<T::Balance>,
@@ -65,7 +65,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
fn commit(&mut self, s: ChangeSet<T>) {
for (address, changed) in s.into_iter() {
if let Some(balance) = changed.balance {
if let balances::UpdateBalanceOutcome::AccountKilled =
if let UpdateBalanceOutcome::AccountKilled =
balances::Module::<T>::set_free_balance_creating(&address, balance)
{
// Account killed. This will ultimately lead to calling `OnFreeBalanceZero` callback
+1 -1
View File
@@ -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<T> = <T as balances::Trait>::Balance;
pub type AccountIdOf<T> = <T as system::Trait>::AccountId;
+2 -1
View File
@@ -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<T: Trait> StorageDoubleMap for StorageOf<T> {
}
}
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(who: &T::AccountId) {
<CodeHashOf<T>>::remove(who);
<StorageOf<T>>::remove_prefix(who.clone());
+3 -1
View File
@@ -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<Self>;
type Proposal = Call;
type Event = Event;
}
+21 -20
View File
@@ -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<T> = <<T as democracy::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
pub trait Trait: democracy::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
@@ -105,7 +106,7 @@ decl_module! {
if !<LastActiveOf<T>>::exists(&who) {
// not yet a voter - deduct bond.
// NOTE: this must be the last potential bailer, since it changes state.
<balances::Module<T>>::reserve(&who, Self::voting_bond())?;
T::Currency::reserve(&who, Self::voting_bond())?;
<Voters<T>>::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.
<balances::Module<T>>::repatriate_reserved(&who, &reporter, Self::voting_bond())?;
T::Currency::repatriate_reserved(&who, &reporter, Self::voting_bond())?;
Self::deposit_event(RawEvent::VoterReaped(who, reporter));
} else {
<balances::Module<T>>::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);
<balances::Module<T>>::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.
<balances::Module<T>>::reserve(&who, Self::candidacy_bond())
T::Currency::reserve(&who, Self::candidacy_bond())
.map_err(|_| "candidate has not enough funds")?;
<RegisterInfoOf<T>>::insert(&who, (Self::vote_index(), slot as u32));
@@ -220,7 +221,7 @@ decl_module! {
fn present_winner(
origin,
candidate: <T::Lookup as StaticLookup>::Source,
#[compact] total: T::Balance,
#[compact] total: BalanceOf<T>,
#[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!(<balances::Module<T>>::can_slash(&who, bad_presentation_punishment), "presenter must have sufficient slashable funds");
let bad_presentation_punishment = Self::present_slash_per_voter() * BalanceOf::<T>::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 _ = <balances::Module<T>>::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<T> = BalanceOf::<T>::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<T>;
/// 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<T> = BalanceOf::<T>::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<T::AccountId>)>;
/// The stakes as they were at the point that the vote ended.
pub SnapshotedStakes get(snapshoted_stakes): Vec<T::Balance>;
pub SnapshotedStakes get(snapshoted_stakes): Vec<BalanceOf<T>>;
/// Get the leaderboard if we;re in the presentation phase.
pub Leaderboard get(leaderboard): Option<Vec<(T::Balance, T::AccountId)> >; // ORDERED low -> high
pub Leaderboard get(leaderboard): Option<Vec<(BalanceOf<T>, T::AccountId)> >; // ORDERED low -> high
}
}
@@ -466,12 +467,12 @@ impl<T: Trait> Module<T> {
<NextFinalise<T>>::put((number + Self::presentation_duration(), empty_seats as u32, expiring));
let voters = Self::voters();
let votes = voters.iter().map(<balances::Module<T>>::total_balance).collect::<Vec<_>>();
let votes = voters.iter().map(T::Currency::total_balance).collect::<Vec<_>>();
<SnapshotedStakes<T>>::put(votes);
// initialise leaderboard.
let leaderboard_size = empty_seats + Self::carry_count() as usize;
<Leaderboard<T>>::put(vec![(T::Balance::zero(), T::AccountId::default()); leaderboard_size]);
<Leaderboard<T>>::put(vec![(BalanceOf::<T>::zero(), T::AccountId::default()); leaderboard_size]);
Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32));
}
@@ -485,7 +486,7 @@ impl<T: Trait> Module<T> {
<SnapshotedStakes<T>>::kill();
let (_, coming, expiring): (T::BlockNumber, u32, Vec<T::AccountId>) =
<NextFinalise<T>>::take().ok_or("finalise can only be called after a tally is started.")?;
let leaderboard: Vec<(T::Balance, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
let leaderboard: Vec<(BalanceOf<T>, T::AccountId)> = <Leaderboard<T>>::take().unwrap_or_default();
let new_expiry = <system::Module<T>>::block_number() + Self::term_duration();
// return bond to winners.
@@ -496,7 +497,7 @@ impl<T: Trait> Module<T> {
.take(coming as usize)
.map(|(_, a)| a)
.cloned()
.inspect(|a| {<balances::Module<T>>::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();
+27 -20
View File
@@ -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<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
pub trait Trait: system::Trait + Sized {
type Currency: Currency<<Self as system::Trait>::AccountId>;
type Proposal: Parameter + Dispatchable<Origin=Self::Origin> + IsSubType<Module<Self>>;
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
@@ -91,12 +97,12 @@ decl_module! {
fn propose(
origin,
proposal: Box<T::Proposal>,
#[compact] value: T::Balance
#[compact] value: BalanceOf<T>
) {
let who = ensure_signed(origin)?;
ensure!(value >= Self::minimum_deposit(), "value too low");
<balances::Module<T>>::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")?;
<balances::Module<T>>::reserve(&who, deposit.0)
T::Currency::reserve(&who, deposit.0)
.map_err(|_| "seconder's balance too low")?;
deposit.1.push(who);
<DepositOf<T>>::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!(!<balances::Module<T>>::total_balance(&who).is_zero(),
ensure!(!T::Currency::total_balance(&who).is_zero(),
"transactor must have balance to signal approval.");
if !<VoteOf<T>>::exists(&(ref_index, who.clone())) {
<VotersFor<T>>::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<T::AccountId>)>;
pub DepositOf get(deposit_of): map PropIndex => Option<(BalanceOf<T>, Vec<T::AccountId>)>;
/// 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<T>;
/// 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<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::AccountId {
Tabled(PropIndex, Balance, Vec<AccountId>),
Started(ReferendumIndex, VoteThreshold),
Passed(ReferendumIndex),
@@ -244,8 +250,8 @@ impl<T: Trait> Module<T> {
/// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal
/// index.
pub fn locked_for(proposal: PropIndex) -> Option<T::Balance> {
Self::deposit_of(proposal).map(|(d, l)| d * T::Balance::sa(l.len() as u64))
pub fn locked_for(proposal: PropIndex) -> Option<BalanceOf<T>> {
Self::deposit_of(proposal).map(|(d, l)| d * BalanceOf::<T>::sa(l.len() as u64))
}
/// Return true if `ref_index` is an on-going referendum.
@@ -273,17 +279,17 @@ impl<T: Trait> Module<T> {
}
/// 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<T>, BalanceOf<T>, BalanceOf<T>) {
Self::voters_for(ref_index).iter()
.map(|voter| (
<balances::Module<T>>::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::<T>::sa(vote.multiplier() as u64), Zero::zero(), bal)
} else {
(Zero::zero(), bal * T::Balance::sa(vote.multiplier() as u64), bal)
(Zero::zero(), bal * BalanceOf::<T>::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<T: Trait> Module<T> {
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
<PublicProps<T>>::put(public_props);
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (T::Balance, Vec<T::AccountId>) =
if let Some((deposit, depositors)) = <DepositOf<T>>::take(prop_index) {//: (BalanceOf<T>, Vec<T::AccountId>) =
// refund depositors
for d in &depositors {
<balances::Module<T>>::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<T: Trait> Module<T> {
fn bake_referendum(now: T::BlockNumber, index: ReferendumIndex, info: ReferendumInfo<T::BlockNumber, T::Proposal>) -> Result {
let (approve, against, capital) = Self::tally(index);
let total_issuance = <balances::Module<T>>::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<T: Trait> Module<T> {
}
}
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(who: &T::AccountId) {
<Bondage<T>>::remove(who);
}
}
impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
impl<T: Trait> EnsureAccountLiquid<T::AccountId> for Module<T> {
fn ensure_account_liquid(who: &T::AccountId) -> Result {
if Self::bondage(who) <= <system::Module<T>>::block_number() {
Ok(())
@@ -477,6 +483,7 @@ mod tests {
type Event = ();
}
impl Trait for Test {
type Currency = balances::Module<Self>;
type Proposal = Call;
type Event = ();
}
+1
View File
@@ -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! {
+40 -33
View File
@@ -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<B: Default + HasCompact + Copy> Default for ValidatorPrefs<B> {
}
}
pub trait Trait: balances::Trait + session::Trait {
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
pub trait Trait: system::Trait + session::Trait {
/// The staking balance.
type Currency: Currency<Self::AccountId>;
/// Some tokens minted.
type OnRewardMinted: OnDilution<<Self as balances::Trait>::Balance>;
type OnRewardMinted: OnDilution<BalanceOf<Self>>;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
@@ -182,7 +189,7 @@ decl_module! {
fn register_preferences(
origin,
#[compact] intentions_index: u32,
prefs: ValidatorPrefs<T::Balance>
prefs: ValidatorPrefs<BalanceOf<T>>
) {
let who = ensure_signed(origin)?;
@@ -228,7 +235,7 @@ decl_module! {
/// An event in this module.
decl_event!(
pub enum Event<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::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<T::Balance>;
pub ValidatorPreferences get(validator_preferences): map T::AccountId => ValidatorPrefs<BalanceOf<T>>;
/// All the accounts with a desire to stake.
pub Intentions get(intentions) config(): Vec<T::AccountId>;
/// All nominator -> nominee relationships.
@@ -277,9 +284,9 @@ decl_storage! {
pub CurrentNominatorsFor get(current_nominators_for): map T::AccountId => Vec<T::AccountId>;
/// 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<T>;
/// 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<T>;
/// The next value of sessions per era.
pub NextSessionsPerEra get(next_sessions_per_era): Option<T::BlockNumber>;
@@ -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<T::Balance>;
pub StakeRange get(stake_range): PairOf<BalanceOf<T>>;
/// 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<T: Trait> Module<T> {
}
/// 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<T> {
Self::nominators_for(who).iter()
.map(<balances::Module<T>>::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<T> {
Self::nominators_for(who).iter()
.map(<balances::Module<T>>::total_balance)
.fold(<balances::Module<T>>::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<T: Trait> Module<T> {
/// 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<T>) {
// 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) = <balances::Module<T>>::slash(v, slash) {
if let Some(rem) = T::Currency::slash(v, slash) {
let noms = Self::current_nominators_for(v);
let total = noms.iter().map(<balances::Module<T>>::total_balance).fold(T::Balance::zero(), |acc, x| acc + x);
let total = noms.iter().map(T::Currency::total_balance).fold(BalanceOf::<T>::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 _ = <balances::Module<T>>::slash(n, safe_mul_rational(<balances::Module<T>>::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<T: Trait> Module<T> {
/// 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<T>) {
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<T: Trait> Module<T> {
} else {
let noms = Self::current_nominators_for(who);
let total = noms.iter()
.map(<balances::Module<T>>::total_balance)
.fold(<balances::Module<T>>::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 _ = <balances::Module<T>>::reward(n, safe_mul_rational(<balances::Module<T>>::total_balance(n)));
let _ = T::Currency::reward(n, safe_mul_rational(T::Currency::total_balance(n)));
}
safe_mul_rational(<balances::Module<T>>::total_balance(who))
safe_mul_rational(T::Currency::total_balance(who))
};
let _ = <balances::Module<T>>::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<T: Trait> Module<T> {
}
/// 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<T> {
let ideal_elapsed = <session::Module<T>>::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::<T>::sa(per65536) / BalanceOf::<T>::sa(65536u64)
}
/// Session has just changed. We need to determine whether we pay a reward, slash and/or
@@ -425,8 +432,8 @@ impl<T: Trait> Module<T> {
Self::reward_validator(v, reward);
}
Self::deposit_event(RawEvent::Reward(reward));
let total_minted = reward * <T::Balance as As<usize>>::sa(validators.len());
let total_rewarded_stake = Self::stake_range().1 * <T::Balance as As<usize>>::sa(validators.len());
let total_minted = reward * <BalanceOf<T> as As<usize>>::sa(validators.len());
let total_rewarded_stake = Self::stake_range().1 * <BalanceOf<T> as As<usize>>::sa(validators.len());
T::OnRewardMinted::on_dilution(total_minted, total_rewarded_stake);
}
@@ -536,7 +543,7 @@ impl<T: Trait> Module<T> {
let base_slash = Self::current_offline_slash();
let instances = slash_count - grace;
let mut total_slash = T::Balance::default();
let mut total_slash = BalanceOf::<T>::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<T: Trait> OnSessionChange<T::Moment> for Module<T> {
}
}
impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
impl<T: Trait> EnsureAccountLiquid<T::AccountId> for Module<T> {
fn ensure_account_liquid(who: &T::AccountId) -> Result {
if Self::bondage(who) <= <system::Module<T>>::block_number() {
Ok(())
@@ -600,7 +607,7 @@ impl<T: Trait> balances::EnsureAccountLiquid<T::AccountId> for Module<T> {
}
}
impl<T: Trait> balances::OnFreeBalanceZero<T::AccountId> for Module<T> {
impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
fn on_free_balance_zero(who: &T::AccountId) {
<Bondage<T>>::remove(who);
}
+1
View File
@@ -66,6 +66,7 @@ impl timestamp::Trait for Test {
type OnTimestampSet = ();
}
impl Trait for Test {
type Currency = balances::Module<Self>;
type OnRewardMinted = ();
type Event = ();
}
+1
View File
@@ -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() {
+68 -53
View File
@@ -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), "`"));
}
}
+1
View File
@@ -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;
+183
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<AccountId> {
/// The account was the given id was killed.
fn on_free_balance_zero(who: &AccountId);
}
impl<AccountId> OnFreeBalanceZero<AccountId> for () {
fn on_free_balance_zero(_who: &AccountId) {}
}
impl<
AccountId,
X: OnFreeBalanceZero<AccountId>,
Y: OnFreeBalanceZero<AccountId>,
> OnFreeBalanceZero<AccountId> 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<Balance> {
/// 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<Balance> OnDilution<Balance> for () {
fn on_dilution(_minted: Balance, _portion: Balance) {}
}
/// Determinator for whether a given account is able to transfer balance.
pub trait EnsureAccountLiquid<AccountId> {
/// 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<AccountId>,
Y: EnsureAccountLiquid<AccountId>,
> EnsureAccountLiquid<AccountId> for (X, Y) {
fn ensure_account_liquid(who: &AccountId) -> result::Result<(), &'static str> {
X::ensure_account_liquid(who)?;
Y::ensure_account_liquid(who)
}
}
impl<AccountId> EnsureAccountLiquid<AccountId> 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<AccountId> {
/// The balance of an account.
type Balance: SimpleArithmetic + As<usize> + As<u64> + 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<Self::Balance>;
/// 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<Self::Balance>;
/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
///
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
/// then `Some(remaining)` will be returned. Full completion is given by `None`.
fn slash_reserved(who: &AccountId, value: Self::Balance) -> Option<Self::Balance>;
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
/// returned.
///
/// As much funds up to `value` will be moved as possible. If this is less than `value`, then
/// `Ok(Some(remaining))` will be returned. Full completion is given by `Ok(None)`.
fn repatriate_reserved(
slashed: &AccountId,
beneficiary: &AccountId,
value: Self::Balance
) -> result::Result<Option<Self::Balance>, &'static str>;
}
+32 -20
View File
@@ -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<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::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<Self::AccountId>;
/// Origin from which approvals must come.
type ApproveOrigin: EnsureOrigin<Self::Origin>;
@@ -73,14 +80,14 @@ decl_module! {
/// proposal is awarded.
fn propose_spend(
origin,
#[compact] value: T::Balance,
#[compact] value: BalanceOf<T>,
beneficiary: <T::Lookup as StaticLookup>::Source
) {
let proposer = ensure_signed(origin)?;
let beneficiary = T::Lookup::lookup(beneficiary)?;
let bond = Self::calculate_bond(value);
<balances::Module<T>>::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<T>) {
// Put the new value into storage.
<Pot<T>>::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<T>,
#[compact] spend_period: T::BlockNumber,
#[compact] burn: Permill
) {
@@ -115,7 +122,7 @@ decl_module! {
let proposal = <Proposals<T>>::take(proposal_id).ok_or("No proposal at that index")?;
let value = proposal.bond;
let _ = <balances::Module<T>>::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<T>;
/// 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<T>;
/// Number of proposals that have been made.
ProposalCount get(proposal_count): ProposalIndex;
/// Proposals that have been made.
Proposals get(proposals): map ProposalIndex => Option<Proposal<T::AccountId, T::Balance>>;
Proposals get(proposals): map ProposalIndex => Option<Proposal<T::AccountId, BalanceOf<T>>>;
/// Proposal indices that have been approved but not yet awarded.
Approvals get(approvals): Vec<ProposalIndex>;
@@ -182,7 +189,11 @@ decl_storage! {
/// An event in this module.
decl_event!(
pub enum Event<T> where <T as balances::Trait>::Balance, <T as system::Trait>::AccountId {
pub enum Event<T>
where
Balance = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance,
<T as system::Trait>::AccountId
{
/// New proposal.
Proposed(ProposalIndex),
/// We have ended a spend period and will now allocate funds.
@@ -200,7 +211,7 @@ impl<T: Trait> Module<T> {
// 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<T>) -> BalanceOf<T> {
Self::proposal_bond_minimum().max(Self::proposal_bond() * value)
}
@@ -219,10 +230,10 @@ impl<T: Trait> Module<T> {
<Proposals<T>>::remove(index);
// return their deposit.
let _ = <balances::Module<T>>::unreserve(&p.proposer, p.bond);
let _ = T::Currency::unreserve(&p.proposer, p.bond);
// provide the allocation.
<balances::Module<T>>::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<T: Trait> Module<T> {
}
}
impl<T: Trait> OnDilution<T::Balance> for Module<T> {
fn on_dilution(minted: T::Balance, portion: T::Balance) {
impl<T: Trait> OnDilution<BalanceOf<T>> for Module<T> {
fn on_dilution(minted: BalanceOf<T>, portion: BalanceOf<T>) {
// 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 = <balances::Module<T>>::total_issuance();
let total_issuance = T::Currency::total_issuance();
let funding = (total_issuance - portion) / portion * minted;
<Pot<T>>::mutate(|x| *x += funding);
}
@@ -298,6 +309,7 @@ mod tests {
type Event = ();
}
impl Trait for Test {
type Currency = balances::Module<Test>;
type ApproveOrigin = system::EnsureRoot<u64>;
type RejectOrigin = system::EnsureRoot<u64>;
type Event = ();