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
+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()
}