Introduce ReservableCurrency Trait in Balances Module (#2124)

* Introduce `ReservableCurrency`

* Update Docs for `ReservableCurrency`

* Update Tests

* Bump spec and rebuild wasm

* Extra: Add a note to `slash()`

Discussion in Riot clarified the behavior of `slash()` and `can_slash()`. Trying to sneak clarifying comments about it into this PR

* Update lib.rs

* Don't drop the periods!

CC @shawntabrizi
This commit is contained in:
Shawn Tabrizi
2019-03-27 16:48:38 +01:00
committed by Gav Wood
parent b32653d0a8
commit 1c1609342c
10 changed files with 66 additions and 50 deletions
+24 -13
View File
@@ -62,6 +62,8 @@
//!
//! - [`Currency`](https://crates.parity.io/srml_support/traits/trait.Currency.html): Functions for dealing with a
//! fungible assets system.
//! - [`ReservableCurrency`](https://crates.parity.io/srml_support/traits/trait.ReservableCurrency.html):
//! Functions for dealing with assets that can be reserved from an account.
//! - [`LockableCurrency`](https://crates.parity.io/srml_support/traits/trait.LockableCurrency.html): Functions for
//! dealing with accounts that allow liquidity restrictions.
//! - [`Imbalance`](https://crates.parity.io/srml_support/traits/trait.Imbalance.html): Functions for handling
@@ -176,7 +178,7 @@ use srml_support::{StorageValue, StorageMap, Parameter, decl_event, decl_storage
use srml_support::traits::{
UpdateBalanceOutcome, Currency, OnFreeBalanceZero, MakePayment, OnUnbalanced,
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
Imbalance, SignedImbalance
Imbalance, SignedImbalance, ReservableCurrency
};
use srml_support::dispatch::Result;
use primitives::traits::{
@@ -661,14 +663,6 @@ where
Self::free_balance(who) >= value
}
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
Self::free_balance(who)
.checked_sub(&value)
.map_or(false, |new_balance|
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok()
)
}
fn total_issuance() -> Self::Balance {
<TotalIssuance<T, I>>::get()
}
@@ -681,10 +675,6 @@ where
<FreeBalance<T, I>>::get(who)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
<ReservedBalance<T, I>>::get(who)
}
fn ensure_can_withdraw(
who: &T::AccountId,
_amount: T::Balance,
@@ -775,6 +765,10 @@ where
let free_slash = cmp::min(free_balance, value);
Self::set_free_balance(who, free_balance - free_slash);
let remaining_slash = value - free_slash;
// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn
// from in extreme circumstances. `can_slash()` should be used prior to `slash()` is avoid having
// to draw from reserved funds, however we err on the side of punishment if things are inconsistent
// or `can_slash` wasn't used appropriately.
if !remaining_slash.is_zero() {
let reserved_balance = Self::reserved_balance(who);
let reserved_slash = cmp::min(reserved_balance, remaining_slash);
@@ -852,6 +846,23 @@ where
};
(imbalance, outcome)
}
}
impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
where
T::Balance: MaybeSerializeDebug
{
fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
Self::free_balance(who)
.checked_sub(&value)
.map_or(false, |new_balance|
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve, new_balance).is_ok()
)
}
fn reserved_balance(who: &T::AccountId) -> Self::Balance {
<ReservedBalance<T, I>>::get(who)
}
fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), &'static str> {
let b = Self::free_balance(who);
+5 -4
View File
@@ -23,7 +23,8 @@ use mock::{Balances, ExtBuilder, Runtime, System};
use runtime_io::with_externalities;
use srml_support::{
assert_noop, assert_ok, assert_err,
traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, Currency, MakePayment}
traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons,
Currency, MakePayment, ReservableCurrency}
};
const ID_1: LockIdentifier = *b"1 ";
@@ -101,17 +102,17 @@ fn lock_reasons_should_work() {
with_externalities(&mut ExtBuilder::default().existential_deposit(1).monied(true).transaction_fees(0, 1).build(), || {
Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Transfer.into());
assert_noop!(<Balances as Currency<_>>::transfer(&1, &2, 1), "account liquidity restrictions prevent withdrawal");
assert_ok!(<Balances as Currency<_>>::reserve(&1, 1));
assert_ok!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
assert_ok!(<Balances as MakePayment<_>>::make_payment(&1, 1));
Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::Reserve.into());
assert_ok!(<Balances as Currency<_>>::transfer(&1, &2, 1));
assert_noop!(<Balances as Currency<_>>::reserve(&1, 1), "account liquidity restrictions prevent withdrawal");
assert_noop!(<Balances as ReservableCurrency<_>>::reserve(&1, 1), "account liquidity restrictions prevent withdrawal");
assert_ok!(<Balances as MakePayment<_>>::make_payment(&1, 1));
Balances::set_lock(ID_1, &1, 10, u64::max_value(), WithdrawReason::TransactionPayment.into());
assert_ok!(<Balances as Currency<_>>::transfer(&1, &2, 1));
assert_ok!(<Balances as Currency<_>>::reserve(&1, 1));
assert_ok!(<Balances as ReservableCurrency<_>>::reserve(&1, 1));
assert_noop!(<Balances as MakePayment<_>>::make_payment(&1, 1), "account liquidity restrictions prevent withdrawal");
});
}