mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 04:11:07 +00:00
Composite accounts (#4820)
* Basic account composition. * Add try_mutate_exists * De-duplicate * Refactor away the UpdateBalanceOutcome * Expunge final UpdateBalanceOutcome refs * Refactor transfer * Refactor reservable currency stuff. * Test with the alternative setup. * Fixes * Test with both setups. * Fixes * Fix * Fix macros * Make indices opt-in * Remove CreationFee, and make indices opt-in. * Fix construct_runtime * Fix last few bits * Fix tests * Update trait impls * Don't hardcode the system event * Make tests build and fix some stuff. * Pointlessly bump runtime version * Fix benchmark * Another fix * Whitespace * Make indices module economically safe * Migrations for indices. * Fix * Whilespace * Trim defunct migrations * Remove unused storage item * More contains_key fixes * Docs. * Bump runtime * Remove unneeded code * Fix test * Fix test * Update frame/balances/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix ED logic * Repatriate reserved logic * Typo * Fix typo * Update frame/system/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/system/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Last few fixes * Another fix * Build fix Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Jaco Greeff <jacogr@gmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -50,7 +50,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
|
||||
// Select an account
|
||||
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
|
||||
let user = account::<T>("user", u);
|
||||
@@ -58,8 +58,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
// Give some multiple of the existential deposit + creation fee + transfer fee
|
||||
let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1;
|
||||
let mut balance = ed.saturating_mul(e.into());
|
||||
balance += T::CreationFee::get();
|
||||
let balance = ed.saturating_mul(e.into());
|
||||
let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&user, balance);
|
||||
|
||||
// Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user.
|
||||
@@ -90,7 +89,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
|
||||
// Select a sender
|
||||
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
|
||||
let user = account::<T>("user", u);
|
||||
@@ -135,7 +134,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
|
||||
// Select a sender
|
||||
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
|
||||
let user = account::<T>("user", u);
|
||||
@@ -176,7 +175,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
|
||||
// Select a sender
|
||||
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
|
||||
let user = account::<T>("user", u);
|
||||
@@ -208,7 +207,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
{
|
||||
// Constants
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
|
||||
|
||||
// Select a sender
|
||||
let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1;
|
||||
let user = account::<T>("user", u);
|
||||
@@ -275,7 +274,7 @@ impl<T: Trait> Benchmarking<BenchmarkResults> for Module<T> {
|
||||
sp_io::benchmarking::commit_db();
|
||||
sp_io::benchmarking::wipe_db();
|
||||
|
||||
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
|
||||
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
|
||||
// results go here
|
||||
let mut results: Vec<BenchmarkResults> = Vec::new();
|
||||
// Select the component we will be benchmarking. Each component will be benchmarked.
|
||||
|
||||
+271
-303
@@ -149,21 +149,24 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
mod tests_local;
|
||||
#[cfg(test)]
|
||||
mod tests_composite;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests;
|
||||
mod migration;
|
||||
mod benchmarking;
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr};
|
||||
use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr, convert::Infallible};
|
||||
use codec::{Codec, Encode, Decode};
|
||||
use frame_support::{
|
||||
StorageValue, Parameter, decl_event, decl_storage, decl_module, decl_error, ensure,
|
||||
weights::SimpleDispatchInfo, traits::{
|
||||
UpdateBalanceOutcome, Currency, OnReapAccount, OnUnbalanced, TryDrop,
|
||||
weights::SimpleDispatchInfo, traits::{
|
||||
Currency, OnReapAccount, OnUnbalanced, TryDrop, StoredMap,
|
||||
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
|
||||
Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive
|
||||
Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive,
|
||||
ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status
|
||||
}
|
||||
};
|
||||
use sp_runtime::{
|
||||
@@ -173,8 +176,10 @@ use sp_runtime::{
|
||||
MaybeSerializeDeserialize, Saturating, Bounded,
|
||||
},
|
||||
};
|
||||
use frame_system::{self as system, IsDeadAccount, OnNewAccount, ensure_signed, ensure_root};
|
||||
use migration::{get_storage_value, put_storage_value, StorageIterator};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
use frame_support::storage::migration::{
|
||||
get_storage_value, take_storage_value, put_storage_value, StorageIterator
|
||||
};
|
||||
|
||||
pub use self::imbalances::{PositiveImbalance, NegativeImbalance};
|
||||
|
||||
@@ -183,21 +188,11 @@ pub trait Subtrait<I: Instance = DefaultInstance>: frame_system::Trait {
|
||||
type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy +
|
||||
MaybeSerializeDeserialize + Debug;
|
||||
|
||||
/// A function that is invoked when the free-balance and the reserved-balance has fallen below
|
||||
/// the existential deposit and both have been reduced to zero.
|
||||
///
|
||||
/// All resources should be cleaned up all resources associated with the given account.
|
||||
type OnReapAccount: OnReapAccount<Self::AccountId>;
|
||||
|
||||
/// Handler for when a new account is created.
|
||||
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
||||
|
||||
/// The minimum amount required to keep an account open.
|
||||
type ExistentialDeposit: Get<Self::Balance>;
|
||||
|
||||
/// The fee required to create an account. If you're doing significant stuff with `OnNewAccount`
|
||||
/// then you'll probably want to make this non-zero.
|
||||
type CreationFee: Get<Self::Balance>;
|
||||
/// The means of storing the balances of an account.
|
||||
type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
|
||||
}
|
||||
|
||||
pub trait Trait<I: Instance = DefaultInstance>: frame_system::Trait {
|
||||
@@ -205,19 +200,6 @@ pub trait Trait<I: Instance = DefaultInstance>: frame_system::Trait {
|
||||
type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy +
|
||||
MaybeSerializeDeserialize + Debug;
|
||||
|
||||
/// A function that is invoked when the free-balance and the reserved-balance has fallen below
|
||||
/// the existential deposit and both have been reduced to zero.
|
||||
///
|
||||
/// All resources should be cleaned up all resources associated with the given account.
|
||||
type OnReapAccount: OnReapAccount<Self::AccountId>;
|
||||
|
||||
/// Handler for when a new account is created.
|
||||
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
||||
|
||||
/// Handler for the unbalanced reduction when taking fees associated with balance
|
||||
/// transfer (which may also include account creation).
|
||||
type TransferPayment: OnUnbalanced<NegativeImbalance<Self, I>>;
|
||||
|
||||
/// Handler for the unbalanced reduction when removing a dust account.
|
||||
type DustRemoval: OnUnbalanced<NegativeImbalance<Self, I>>;
|
||||
|
||||
@@ -227,16 +209,14 @@ pub trait Trait<I: Instance = DefaultInstance>: frame_system::Trait {
|
||||
/// The minimum amount required to keep an account open.
|
||||
type ExistentialDeposit: Get<Self::Balance>;
|
||||
|
||||
/// The fee required to create an account.
|
||||
type CreationFee: Get<Self::Balance>;
|
||||
/// The means of storing the balances of an account.
|
||||
type AccountStore: StoredMap<Self::AccountId, AccountData<Self::Balance>>;
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> Subtrait<I> for T {
|
||||
type Balance = T::Balance;
|
||||
type OnReapAccount = T::OnReapAccount;
|
||||
type OnNewAccount = T::OnNewAccount;
|
||||
type ExistentialDeposit = T::ExistentialDeposit;
|
||||
type CreationFee = T::CreationFee;
|
||||
type AccountStore = T::AccountStore;
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
@@ -244,12 +224,13 @@ decl_event!(
|
||||
<T as frame_system::Trait>::AccountId,
|
||||
<T as Trait<I>>::Balance
|
||||
{
|
||||
/// A new account was created.
|
||||
NewAccount(AccountId, Balance),
|
||||
/// An account was reaped.
|
||||
ReapedAccount(AccountId, Balance),
|
||||
/// Transfer succeeded (from, to, value, fees).
|
||||
Transfer(AccountId, AccountId, Balance, Balance),
|
||||
/// An account was created with some free balance.
|
||||
Endowed(AccountId, Balance),
|
||||
/// An account was removed whose balance was non-zero but below ExistentialDeposit,
|
||||
/// resulting in an outright loss.
|
||||
DustLost(AccountId, Balance),
|
||||
/// Transfer succeeded (from, to, value).
|
||||
Transfer(AccountId, AccountId, Balance),
|
||||
/// A balance was set by root (who, free, reserved).
|
||||
BalanceSet(AccountId, Balance, Balance),
|
||||
/// Some amount was deposited (e.g. for transaction fees).
|
||||
@@ -376,11 +357,9 @@ decl_storage! {
|
||||
///
|
||||
/// NOTE: THIS MAY NEVER BE IN EXISTENCE AND YET HAVE A `total().is_zero()`. If the total
|
||||
/// is ever zero, then the entry *MUST* be removed.
|
||||
pub Account get(fn account)
|
||||
build(|config: &GenesisConfig<T, I>| config.balances.iter()
|
||||
.map(|&(ref who, free)| (who.clone(), AccountData { free, .. Default::default() }))
|
||||
.collect::<Vec<_>>()
|
||||
): map hasher(blake2_256) T::AccountId => AccountData<T::Balance>;
|
||||
///
|
||||
/// NOTE: This is only used in the case that this module is used to store balances.
|
||||
pub Account: map hasher(blake2_256) T::AccountId => AccountData<T::Balance>;
|
||||
|
||||
/// Any liquidity locks on some account balances.
|
||||
/// NOTE: Should only be accessed when setting, changing and freeing a lock.
|
||||
@@ -405,6 +384,9 @@ decl_storage! {
|
||||
"the balance of any account should always be more than existential deposit.",
|
||||
)
|
||||
}
|
||||
for &(ref who, free) in config.balances.iter() {
|
||||
T::AccountStore::insert(who, AccountData { free, .. Default::default() });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -416,9 +398,6 @@ decl_module! {
|
||||
/// The minimum amount required to keep an account open.
|
||||
const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get();
|
||||
|
||||
/// The fee required to create an account.
|
||||
const CreationFee: T::Balance = T::CreationFee::get();
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
/// Transfer some liquid free balance to another account.
|
||||
@@ -484,24 +463,25 @@ decl_module! {
|
||||
let new_free = if wipeout { Zero::zero() } else { new_free };
|
||||
let new_reserved = if wipeout { Zero::zero() } else { new_reserved };
|
||||
|
||||
let old_account = Account::<T, I>::get(&who);
|
||||
let (free, reserved) = Self::mutate_account(&who, |account| {
|
||||
if new_free > account.free {
|
||||
mem::drop(PositiveImbalance::<T, I>::new(new_free - account.free));
|
||||
} else if new_free < account.free {
|
||||
mem::drop(NegativeImbalance::<T, I>::new(account.free - new_free));
|
||||
}
|
||||
|
||||
if new_free > old_account.free {
|
||||
mem::drop(PositiveImbalance::<T, I>::new(new_free - old_account.free));
|
||||
} else if new_free < old_account.free {
|
||||
mem::drop(NegativeImbalance::<T, I>::new(old_account.free - new_free));
|
||||
}
|
||||
if new_reserved > account.reserved {
|
||||
mem::drop(PositiveImbalance::<T, I>::new(new_reserved - account.reserved));
|
||||
} else if new_reserved < account.reserved {
|
||||
mem::drop(NegativeImbalance::<T, I>::new(account.reserved - new_reserved));
|
||||
}
|
||||
|
||||
if new_reserved > old_account.reserved {
|
||||
mem::drop(PositiveImbalance::<T, I>::new(new_reserved - old_account.reserved));
|
||||
} else if new_reserved < old_account.reserved {
|
||||
mem::drop(NegativeImbalance::<T, I>::new(old_account.reserved - new_reserved));
|
||||
}
|
||||
account.free = new_free;
|
||||
account.reserved = new_reserved;
|
||||
|
||||
let account = AccountData { free: new_free, reserved: new_reserved, ..old_account };
|
||||
Self::set_account(&who, &account, &old_account);
|
||||
|
||||
Self::deposit_event(RawEvent::BalanceSet(who, account.free, account.reserved));
|
||||
(account.free, account.reserved)
|
||||
});
|
||||
Self::deposit_event(RawEvent::BalanceSet(who, free, reserved));
|
||||
}
|
||||
|
||||
/// Exactly as `transfer`, except the origin must be root and the source account may be
|
||||
@@ -626,87 +606,110 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
|
||||
put_storage_value(b"Balances", b"Locks", &hash, locks);
|
||||
put_storage_value(b"Balances", b"Account", &hash, account);
|
||||
}
|
||||
|
||||
for (hash, balances) in StorageIterator::<AccountData<T::Balance>>::new(b"Balances", b"Account").drain() {
|
||||
let nonce = take_storage_value::<T::Index>(b"System", b"AccountNonce", &hash).unwrap_or_default();
|
||||
put_storage_value(b"System", b"Account", &hash, (nonce, balances));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the free balance of an account.
|
||||
pub fn free_balance(who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
|
||||
Account::<T, I>::get(who.borrow()).free
|
||||
Self::account(who.borrow()).free
|
||||
}
|
||||
|
||||
/// Get the balance of an account that can be used for transfers, reservations, or any other
|
||||
/// non-locking, non-transaction-fee activity. Will be at most `free_balance`.
|
||||
pub fn usable_balance(who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
|
||||
Account::<T, I>::get(who.borrow()).usable(Reasons::Misc)
|
||||
Self::account(who.borrow()).usable(Reasons::Misc)
|
||||
}
|
||||
|
||||
/// Get the balance of an account that can be used for paying transaction fees (not tipping,
|
||||
/// or any other kind of fees, though). Will be at most `free_balance`.
|
||||
pub fn usable_balance_for_fees(who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
|
||||
Account::<T, I>::get(who.borrow()).usable(Reasons::Fee)
|
||||
Self::account(who.borrow()).usable(Reasons::Fee)
|
||||
}
|
||||
|
||||
/// Get the reserved balance of an account.
|
||||
pub fn reserved_balance(who: impl sp_std::borrow::Borrow<T::AccountId>) -> T::Balance {
|
||||
Account::<T, I>::get(who.borrow()).reserved
|
||||
Self::account(who.borrow()).reserved
|
||||
}
|
||||
|
||||
/// Set both the free and reserved balance of an account to some new value. Will enforce
|
||||
/// Get both the free and reserved balances of an account.
|
||||
fn account(who: &T::AccountId) -> AccountData<T::Balance> {
|
||||
T::AccountStore::get(&who)
|
||||
}
|
||||
|
||||
/// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed
|
||||
/// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new`
|
||||
/// account's total balance is non-zero but below ED.
|
||||
///
|
||||
/// Returns the final free balance, iff the account was previously of total balance zero, known
|
||||
/// as its "endowment".
|
||||
fn post_mutation(
|
||||
who: &T::AccountId,
|
||||
new: AccountData<T::Balance>,
|
||||
) -> Option<AccountData<T::Balance>> {
|
||||
let total = new.total();
|
||||
if total < T::ExistentialDeposit::get() {
|
||||
if !total.is_zero() {
|
||||
T::DustRemoval::on_unbalanced(NegativeImbalance::new(total));
|
||||
Self::deposit_event(RawEvent::DustLost(who.clone(), total));
|
||||
}
|
||||
None
|
||||
} else {
|
||||
Some(new)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutate an account to some new value, or delete it entirely with `None`. Will enforce
|
||||
/// `ExistentialDeposit` law, annulling the account as needed.
|
||||
///
|
||||
/// Will return `AccountKilled` if either reserved or free are too low.
|
||||
///
|
||||
/// NOTE: This assumes that `account` is the same as `Self::account(who)` except for altered
|
||||
/// values of `free` and `balance`.
|
||||
///
|
||||
/// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used
|
||||
/// when it is known that the account already exists.
|
||||
///
|
||||
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
|
||||
/// the caller will do this.
|
||||
fn set_account(
|
||||
fn mutate_account<R>(
|
||||
who: &T::AccountId,
|
||||
account: &AccountData<T::Balance>,
|
||||
old: &AccountData<T::Balance>,
|
||||
) -> UpdateBalanceOutcome {
|
||||
let total = account.free + account.reserved;
|
||||
if total < T::ExistentialDeposit::get() {
|
||||
T::DustRemoval::on_unbalanced(NegativeImbalance::new(total));
|
||||
if !old.total().is_zero() {
|
||||
Self::reap_account(who, total);
|
||||
UpdateBalanceOutcome::AccountKilled
|
||||
} else {
|
||||
UpdateBalanceOutcome::StillDead
|
||||
}
|
||||
} else {
|
||||
if old.total().is_zero() {
|
||||
Self::about_to_create_account(who, account.free);
|
||||
}
|
||||
Account::<T, I>::insert(who, account);
|
||||
UpdateBalanceOutcome::Updated
|
||||
}
|
||||
f: impl FnOnce(&mut AccountData<T::Balance>) -> R
|
||||
) -> R {
|
||||
Self::try_mutate_account(who, |a| -> Result<R, Infallible> { Ok(f(a)) })
|
||||
.expect("Error is infallible; qed")
|
||||
}
|
||||
|
||||
/// Register a new account (with existential balance).
|
||||
/// Mutate an account to some new value, or delete it entirely with `None`. Will enforce
|
||||
/// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the
|
||||
/// result of `f` is an `Err`.
|
||||
///
|
||||
/// This just calls appropriate hooks. It doesn't (necessarily) make any state changes.
|
||||
fn about_to_create_account(who: &T::AccountId, balance: T::Balance) {
|
||||
T::OnNewAccount::on_new_account(&who);
|
||||
Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone()));
|
||||
}
|
||||
|
||||
/// Unregister an account.
|
||||
/// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used
|
||||
/// when it is known that the account already exists.
|
||||
///
|
||||
/// This just removes the nonce and leaves an event.
|
||||
fn reap_account(who: &T::AccountId, dust: T::Balance) {
|
||||
Locks::<T, I>::remove(who);
|
||||
Account::<T, I>::remove(who);
|
||||
T::OnReapAccount::on_reap_account(who);
|
||||
Self::deposit_event(RawEvent::ReapedAccount(who.clone(), dust));
|
||||
/// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that
|
||||
/// the caller will do this.
|
||||
fn try_mutate_account<R, E>(
|
||||
who: &T::AccountId,
|
||||
f: impl FnOnce(&mut AccountData<T::Balance>) -> Result<R, E>
|
||||
) -> Result<R, E> {
|
||||
T::AccountStore::try_mutate_exists(who, |maybe_account| {
|
||||
let mut account = maybe_account.take().unwrap_or_default();
|
||||
let was_zero = account.total().is_zero();
|
||||
f(&mut account).map(move |result| {
|
||||
let maybe_endowed = if was_zero { Some(account.free) } else { None };
|
||||
*maybe_account = Self::post_mutation(who, account);
|
||||
(maybe_endowed, result)
|
||||
})
|
||||
}).map(|(maybe_endowed, result)| {
|
||||
if let Some(endowed) = maybe_endowed {
|
||||
Self::deposit_event(RawEvent::Endowed(who.clone(), endowed));
|
||||
}
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// Update the account entry for `who`, given the locks.
|
||||
fn update_locks(who: &T::AccountId, locks: &[BalanceLock<T::Balance>]) {
|
||||
Account::<T, I>::mutate(who, |b| {
|
||||
Self::mutate_account(who, |b| {
|
||||
b.misc_frozen = Zero::zero();
|
||||
b.fee_frozen = Zero::zero();
|
||||
for l in locks.iter() {
|
||||
@@ -722,6 +725,13 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> OnReapAccount<T::AccountId> for Module<T, I> {
|
||||
fn on_reap_account(who: &T::AccountId) {
|
||||
Locks::<T, I>::remove(who);
|
||||
Account::<T, I>::remove(who);
|
||||
}
|
||||
}
|
||||
|
||||
// wrapping these imbalances in a private module is necessary to ensure absolute privacy
|
||||
// of the inner member.
|
||||
mod imbalances {
|
||||
@@ -885,9 +895,8 @@ mod imbalances {
|
||||
// its type declaration).
|
||||
// This works as long as `increase_total_issuance_by` doesn't use the Imbalance
|
||||
// types (basically for charging fees).
|
||||
// This should eventually be refactored so that the three type items that do
|
||||
// depend on the Imbalance type (TransferPayment, DustRemoval)
|
||||
// are placed in their own SRML module.
|
||||
// This should eventually be refactored so that the type item that
|
||||
// depends on the Imbalance type (DustRemoval) is placed in its own SRML module.
|
||||
struct ElevatedTrait<T: Subtrait<I>, I: Instance>(T, I);
|
||||
impl<T: Subtrait<I>, I: Instance> Clone for ElevatedTrait<T, I> {
|
||||
fn clone(&self) -> Self { unimplemented!() }
|
||||
@@ -913,16 +922,16 @@ impl<T: Subtrait<I>, I: Instance> frame_system::Trait for ElevatedTrait<T, I> {
|
||||
type AvailableBlockRatio = T::AvailableBlockRatio;
|
||||
type Version = T::Version;
|
||||
type ModuleToIndex = T::ModuleToIndex;
|
||||
type OnNewAccount = T::OnNewAccount;
|
||||
type OnReapAccount = T::OnReapAccount;
|
||||
type AccountData = T::AccountData;
|
||||
}
|
||||
impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
|
||||
type Balance = T::Balance;
|
||||
type OnReapAccount = T::OnReapAccount;
|
||||
type OnNewAccount = T::OnNewAccount;
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = T::ExistentialDeposit;
|
||||
type CreationFee = T::CreationFee;
|
||||
type AccountStore = T::AccountStore;
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
@@ -950,10 +959,6 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
T::ExistentialDeposit::get()
|
||||
}
|
||||
|
||||
fn free_balance(who: &T::AccountId) -> Self::Balance {
|
||||
Account::<T, I>::get(who).free
|
||||
}
|
||||
|
||||
// Burn funds from the total issuance, returning a positive imbalance for the amount burned.
|
||||
// Is a no-op if amount to be burned is zero.
|
||||
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
|
||||
@@ -981,6 +986,10 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
NegativeImbalance::new(amount)
|
||||
}
|
||||
|
||||
fn free_balance(who: &T::AccountId) -> Self::Balance {
|
||||
Self::account(who).free
|
||||
}
|
||||
|
||||
// Ensure that an account can withdraw from their free balance given any existing withdrawal
|
||||
// restrictions like locks and vesting balance.
|
||||
// Is a no-op if amount to be withdrawn is zero.
|
||||
@@ -996,7 +1005,7 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
new_balance: T::Balance,
|
||||
) -> DispatchResult {
|
||||
if amount.is_zero() { return Ok(()) }
|
||||
let min_balance = Account::<T, I>::get(who).frozen(reasons.into());
|
||||
let min_balance = Self::account(who).frozen(reasons.into());
|
||||
ensure!(new_balance >= min_balance, Error::<T, I>::LiquidityRestrictions);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1011,80 +1020,38 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
) -> DispatchResult {
|
||||
if value.is_zero() || transactor == dest { return Ok(()) }
|
||||
|
||||
let old_from_account = Self::account(transactor);
|
||||
let mut from_account = old_from_account.clone();
|
||||
let old_to_account = Self::account(dest);
|
||||
let mut to_account = old_to_account.clone();
|
||||
Self::try_mutate_account(dest, |to_account| -> DispatchResult {
|
||||
Self::try_mutate_account(transactor, |from_account| -> DispatchResult {
|
||||
from_account.free = from_account.free.checked_sub(&value)
|
||||
.ok_or(Error::<T, I>::InsufficientBalance)?;
|
||||
|
||||
let would_create = to_account.total().is_zero();
|
||||
let fee = if would_create { T::CreationFee::get() } else { Zero::zero() };
|
||||
let liability = value.checked_add(&fee).ok_or(Error::<T, I>::Overflow)?;
|
||||
// NOTE: total stake being stored in the same type means that this could never overflow
|
||||
// but better to be safe than sorry.
|
||||
to_account.free = to_account.free.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
|
||||
from_account.free = from_account.free.checked_sub(&liability)
|
||||
.ok_or(Error::<T, I>::InsufficientBalance)?;
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
ensure!(to_account.total() >= ed, Error::<T, I>::ExistentialDeposit);
|
||||
|
||||
// NOTE: total stake being stored in the same type means that this could never overflow
|
||||
// but better to be safe than sorry.
|
||||
to_account.free = to_account.free.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
Self::ensure_can_withdraw(
|
||||
transactor,
|
||||
value,
|
||||
WithdrawReason::Transfer.into(),
|
||||
from_account.free,
|
||||
)?;
|
||||
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
ensure!(to_account.free >= ed, Error::<T, I>::ExistentialDeposit);
|
||||
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
|
||||
ensure!(allow_death || from_account.free >= ed, Error::<T, I>::KeepAlive);
|
||||
|
||||
Self::ensure_can_withdraw(
|
||||
transactor,
|
||||
value,
|
||||
WithdrawReason::Transfer.into(),
|
||||
from_account.free,
|
||||
)?;
|
||||
|
||||
let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
|
||||
ensure!(allow_death || from_account.free >= ed, Error::<T, I>::KeepAlive);
|
||||
|
||||
Self::set_account(transactor, &from_account, &old_from_account);
|
||||
|
||||
// Take action on the set_account call.
|
||||
// This will emit events that _resulted_ from the transfer.
|
||||
Self::set_account(dest, &to_account, &old_to_account);
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
|
||||
// Emit transfer event.
|
||||
Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee));
|
||||
|
||||
T::TransferPayment::on_unbalanced(NegativeImbalance::new(fee));
|
||||
Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Withdraw some free balance from an account, respecting existence requirements.
|
||||
// Is a no-op if value to be withdrawn is zero.
|
||||
fn withdraw(
|
||||
who: &T::AccountId,
|
||||
value: Self::Balance,
|
||||
reasons: WithdrawReasons,
|
||||
liveness: ExistenceRequirement,
|
||||
) -> result::Result<Self::NegativeImbalance, DispatchError> {
|
||||
if value.is_zero() { return Ok(NegativeImbalance::zero()); }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
if let Some(new_free_account) = account.free.checked_sub(&value) {
|
||||
// if we need to keep the account alive...
|
||||
if liveness == ExistenceRequirement::KeepAlive
|
||||
// ...and it would be dead afterwards...
|
||||
&& new_free_account < T::ExistentialDeposit::get()
|
||||
// ...yet is was alive before
|
||||
&& account.free >= T::ExistentialDeposit::get()
|
||||
{
|
||||
Err(Error::<T, I>::KeepAlive)?
|
||||
}
|
||||
Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
|
||||
account.free = new_free_account;
|
||||
Self::set_account(who, &account, &old_account);
|
||||
Ok(NegativeImbalance::new(value))
|
||||
} else {
|
||||
Err(Error::<T, I>::InsufficientBalance)?
|
||||
}
|
||||
}
|
||||
|
||||
/// Slash a target account `who`, returning the negative imbalance created and any left over
|
||||
/// amount that could not be slashed.
|
||||
///
|
||||
@@ -1100,22 +1067,19 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
) -> (Self::NegativeImbalance, Self::Balance) {
|
||||
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
Self::mutate_account(who, |account| {
|
||||
let free_slash = cmp::min(account.free, value);
|
||||
account.free -= free_slash;
|
||||
|
||||
let free_slash = cmp::min(account.free, value);
|
||||
account.free -= free_slash;
|
||||
|
||||
let remaining_slash = value - free_slash;
|
||||
let result = if !remaining_slash.is_zero() {
|
||||
let reserved_slash = cmp::min(account.reserved, remaining_slash);
|
||||
account.reserved -= reserved_slash;
|
||||
(NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash)
|
||||
} else {
|
||||
(NegativeImbalance::new(value), Zero::zero())
|
||||
};
|
||||
Self::set_account(who, &account, &old_account);
|
||||
result
|
||||
let remaining_slash = value - free_slash;
|
||||
if !remaining_slash.is_zero() {
|
||||
let reserved_slash = cmp::min(account.reserved, remaining_slash);
|
||||
account.reserved -= reserved_slash;
|
||||
(NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash)
|
||||
} else {
|
||||
(NegativeImbalance::new(value), Zero::zero())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Deposit some `value` into the free balance of an existing target account `who`.
|
||||
@@ -1124,16 +1088,14 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
fn deposit_into_existing(
|
||||
who: &T::AccountId,
|
||||
value: Self::Balance
|
||||
) -> result::Result<Self::PositiveImbalance, DispatchError> {
|
||||
) -> Result<Self::PositiveImbalance, DispatchError> {
|
||||
if value.is_zero() { return Ok(PositiveImbalance::zero()) }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
ensure!(!account.total().is_zero(), Error::<T, I>::DeadAccount);
|
||||
account.free = account.free.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
|
||||
Self::set_account(who, &account, &old_account);
|
||||
Ok(PositiveImbalance::new(value))
|
||||
Self::try_mutate_account(who, |account| -> Result<Self::PositiveImbalance, DispatchError> {
|
||||
ensure!(!account.total().is_zero(), Error::<T, I>::DeadAccount);
|
||||
account.free = account.free.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
Ok(PositiveImbalance::new(value))
|
||||
})
|
||||
}
|
||||
|
||||
/// Deposit some `value` into the free balance of `who`, possibly creating a new account.
|
||||
@@ -1148,34 +1110,58 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
) -> Self::PositiveImbalance {
|
||||
if value.is_zero() { return Self::PositiveImbalance::zero() }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
Self::try_mutate_account(who, |account| -> Result<Self::PositiveImbalance, Self::PositiveImbalance> {
|
||||
// bail if not yet created and this operation wouldn't be enough to create it.
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
ensure!(value >= ed || !account.total().is_zero(), Self::PositiveImbalance::zero());
|
||||
|
||||
// bail if not yet created and this operation wouldn't be enough to create it.
|
||||
if value < ed && account.total().is_zero() { return Self::PositiveImbalance::zero() }
|
||||
// defensive only: overflow should never happen, however in case it does, then this
|
||||
// operation is a no-op.
|
||||
account.free = account.free.checked_add(&value).ok_or(Self::PositiveImbalance::zero())?;
|
||||
|
||||
// defensive only: overflow should never happen, however in case it does, then this
|
||||
// operation is a no-op.
|
||||
account.free = match account.free.checked_add(&value) {
|
||||
Some(f) => f,
|
||||
None => return Self::PositiveImbalance::zero(),
|
||||
};
|
||||
Ok(PositiveImbalance::new(value))
|
||||
}).unwrap_or_else(|x| x)
|
||||
}
|
||||
|
||||
Self::set_account(who, &account, &old_account);
|
||||
/// Withdraw some free balance from an account, respecting existence requirements.
|
||||
///
|
||||
/// Is a no-op if value to be withdrawn is zero.
|
||||
fn withdraw(
|
||||
who: &T::AccountId,
|
||||
value: Self::Balance,
|
||||
reasons: WithdrawReasons,
|
||||
liveness: ExistenceRequirement,
|
||||
) -> result::Result<Self::NegativeImbalance, DispatchError> {
|
||||
if value.is_zero() { return Ok(NegativeImbalance::zero()); }
|
||||
|
||||
PositiveImbalance::new(value)
|
||||
Self::try_mutate_account(who, |account|
|
||||
-> Result<Self::NegativeImbalance, DispatchError>
|
||||
{
|
||||
let new_free_account = account.free.checked_sub(&value)
|
||||
.ok_or(Error::<T, I>::InsufficientBalance)?;
|
||||
|
||||
// bail if we need to keep the account alive and this would kill it.
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
let would_be_dead = new_free_account + account.reserved < ed;
|
||||
let would_kill = would_be_dead && account.free + account.reserved >= ed;
|
||||
ensure!(liveness == AllowDeath || !would_kill, Error::<T, I>::KeepAlive);
|
||||
|
||||
Self::ensure_can_withdraw(who, value, reasons, new_free_account)?;
|
||||
|
||||
account.free = new_free_account;
|
||||
|
||||
Ok(NegativeImbalance::new(value))
|
||||
})
|
||||
}
|
||||
|
||||
/// Force the new free balance of a target account `who` to some new value `balance`.
|
||||
fn make_free_balance_be(who: &T::AccountId, value: Self::Balance) -> (
|
||||
SignedImbalance<Self::Balance, Self::PositiveImbalance>,
|
||||
UpdateBalanceOutcome
|
||||
) {
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
|
||||
if value < T::ExistentialDeposit::get() && account.free.is_zero() {
|
||||
fn make_free_balance_be(who: &T::AccountId, value: Self::Balance)
|
||||
-> SignedImbalance<Self::Balance, Self::PositiveImbalance>
|
||||
{
|
||||
Self::try_mutate_account(who, |account|
|
||||
-> Result<SignedImbalance<Self::Balance, Self::PositiveImbalance>, ()>
|
||||
{
|
||||
let ed = T::ExistentialDeposit::get();
|
||||
// If we're attempting to set an existing account to less than ED, then
|
||||
// bypass the entire operation. It's a no-op if you follow it through, but
|
||||
// since this is an instance where we might account for a negative imbalance
|
||||
@@ -1183,24 +1169,16 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
|
||||
// equal and opposite cause (returned as an Imbalance), then in the
|
||||
// instance that there's no other accounts on the system at all, we might
|
||||
// underflow the issuance and our arithmetic will be off.
|
||||
return (
|
||||
SignedImbalance::Positive(Self::PositiveImbalance::zero()),
|
||||
UpdateBalanceOutcome::AccountKilled,
|
||||
)
|
||||
}
|
||||
let imbalance = if account.free <= value {
|
||||
SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
|
||||
} else {
|
||||
SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
|
||||
};
|
||||
account.free = value;
|
||||
ensure!(value + account.reserved >= ed || !account.total().is_zero(), ());
|
||||
|
||||
// If the balance is too low, then the account is reaped.
|
||||
// Free balance can never be less than ED. If that happens, it gets reduced to zero
|
||||
// and the account information relevant to this subsystem is deleted (i.e. the
|
||||
// account is reaped).
|
||||
let outcome = Self::set_account(who, &account, &old_account);
|
||||
(imbalance, outcome)
|
||||
let imbalance = if account.free <= value {
|
||||
SignedImbalance::Positive(PositiveImbalance::new(value - account.free))
|
||||
} else {
|
||||
SignedImbalance::Negative(NegativeImbalance::new(account.free - value))
|
||||
};
|
||||
account.free = value;
|
||||
Ok(imbalance)
|
||||
}).unwrap_or(SignedImbalance::Positive(Self::PositiveImbalance::zero()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1226,18 +1204,14 @@ impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
|
||||
/// Move `value` from the free balance from `who` to their reserved balance.
|
||||
///
|
||||
/// Is a no-op if value to be reserved is zero.
|
||||
fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), DispatchError> {
|
||||
fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
|
||||
if value.is_zero() { return Ok(()) }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
|
||||
account.free = account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
|
||||
account.reserved = account.reserved.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free)?;
|
||||
|
||||
Self::set_account(who, &account, &old_account);
|
||||
Ok(())
|
||||
Self::try_mutate_account(who, |account| -> DispatchResult {
|
||||
account.free = account.free.checked_sub(&value).ok_or(Error::<T, I>::InsufficientBalance)?;
|
||||
account.reserved = account.reserved.checked_add(&value).ok_or(Error::<T, I>::Overflow)?;
|
||||
Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free)
|
||||
})
|
||||
}
|
||||
|
||||
/// Unreserve some funds, returning any amount that was unable to be unreserved.
|
||||
@@ -1246,18 +1220,14 @@ impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
|
||||
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
|
||||
if value.is_zero() { return Zero::zero() }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
|
||||
let actual = cmp::min(account.reserved, value);
|
||||
account.reserved -= actual;
|
||||
// defensive only: this can never fail since total issuance which is at least free+reserved
|
||||
// fits into the same datatype.
|
||||
account.free = account.free.saturating_add(actual);
|
||||
|
||||
Self::set_account(who, &account, &old_account);
|
||||
|
||||
value - actual
|
||||
Self::mutate_account(who, |account| {
|
||||
let actual = cmp::min(account.reserved, value);
|
||||
account.reserved -= actual;
|
||||
// defensive only: this can never fail since total issuance which is at least free+reserved
|
||||
// fits into the same datatype.
|
||||
account.free = account.free.saturating_add(actual);
|
||||
value - actual
|
||||
})
|
||||
}
|
||||
|
||||
/// Slash from reserved balance, returning the negative imbalance created,
|
||||
@@ -1270,47 +1240,46 @@ impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
|
||||
) -> (Self::NegativeImbalance, Self::Balance) {
|
||||
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
|
||||
|
||||
let old_account = Self::account(who);
|
||||
let mut account = old_account.clone();
|
||||
|
||||
// underflow should never happen, but it if does, there's nothing to be done here.
|
||||
let actual = cmp::min(account.reserved, value);
|
||||
account.reserved -= actual;
|
||||
|
||||
Self::set_account(who, &account, &old_account);
|
||||
|
||||
(NegativeImbalance::new(actual), value - actual)
|
||||
Self::mutate_account(who, |account| {
|
||||
// underflow should never happen, but it if does, there's nothing to be done here.
|
||||
let actual = cmp::min(account.reserved, value);
|
||||
account.reserved -= actual;
|
||||
(NegativeImbalance::new(actual), value - actual)
|
||||
})
|
||||
}
|
||||
|
||||
/// Move the reserved balance of one account into the free balance of another.
|
||||
/// Move the reserved balance of one account into the balance of another, according to `status`.
|
||||
///
|
||||
/// Is a no-op if the value to be moved is zero.
|
||||
/// Is a no-op if:
|
||||
/// - the value to be moved is zero; or
|
||||
/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
|
||||
fn repatriate_reserved(
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: Self::Balance,
|
||||
) -> result::Result<Self::Balance, DispatchError> {
|
||||
if value.is_zero() { return Ok (Zero::zero()) }
|
||||
status: Status,
|
||||
) -> Result<Self::Balance, DispatchError> {
|
||||
if value.is_zero() { return Ok(Zero::zero()) }
|
||||
|
||||
if slashed == beneficiary {
|
||||
return Ok(Self::unreserve(slashed, value));
|
||||
return match status {
|
||||
Status::Free => Ok(Self::unreserve(slashed, value)),
|
||||
Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))),
|
||||
};
|
||||
}
|
||||
|
||||
let old_to_account = Self::account(beneficiary);
|
||||
let mut to_account = old_to_account.clone();
|
||||
ensure!(!to_account.total().is_zero(), Error::<T, I>::DeadAccount);
|
||||
|
||||
let old_from_account = Self::account(slashed);
|
||||
let mut from_account = old_from_account.clone();
|
||||
let actual = cmp::min(from_account.reserved, value);
|
||||
|
||||
to_account.free = to_account.free.checked_add(&actual).ok_or(Error::<T, I>::Overflow)?;
|
||||
from_account.reserved -= actual;
|
||||
|
||||
Self::set_account(slashed, &from_account, &old_from_account);
|
||||
Self::set_account(beneficiary, &to_account, &old_to_account);
|
||||
|
||||
Ok(value - actual)
|
||||
Self::try_mutate_account(beneficiary, |to_account| -> Result<Self::Balance, DispatchError> {
|
||||
ensure!(!to_account.total().is_zero(), Error::<T, I>::DeadAccount);
|
||||
Self::try_mutate_account(slashed, |from_account| -> Result<Self::Balance, DispatchError> {
|
||||
let actual = cmp::min(from_account.reserved, value);
|
||||
match status {
|
||||
Status::Free => to_account.free = to_account.free.checked_add(&actual).ok_or(Error::<T, I>::Overflow)?,
|
||||
Status::Reserved => to_account.reserved = to_account.reserved.checked_add(&actual).ok_or(Error::<T, I>::Overflow)?,
|
||||
}
|
||||
from_account.reserved -= actual;
|
||||
Ok(value - actual)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1380,12 +1349,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait<I>, I: Instance> IsDeadAccount<T::AccountId> for Module<T, I>
|
||||
where
|
||||
impl<T: Trait<I>, I: Instance> IsDeadAccount<T::AccountId> for Module<T, I> where
|
||||
T::Balance: MaybeSerializeDeserialize + Debug
|
||||
{
|
||||
fn is_dead_account(who: &T::AccountId) -> bool {
|
||||
// this should always be exactly equivalent to `Self::account(who).total().is_zero()`
|
||||
!Account::<T, I>::contains_key(who)
|
||||
!T::AccountStore::is_explicit(who)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2017-2020 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/>.
|
||||
|
||||
//! Some utilities for helping access storage with arbitrary key types.
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
use frame_support::{StorageHasher, Twox128};
|
||||
|
||||
pub struct StorageIterator<T> {
|
||||
prefix: [u8; 32],
|
||||
previous_key: Vec<u8>,
|
||||
drain: bool,
|
||||
_phantom: ::sp_std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> StorageIterator<T> {
|
||||
pub fn new(module: &[u8], item: &[u8]) -> Self {
|
||||
let mut prefix = [0u8; 32];
|
||||
prefix[0..16].copy_from_slice(&Twox128::hash(module));
|
||||
prefix[16..32].copy_from_slice(&Twox128::hash(item));
|
||||
Self { prefix, previous_key: prefix[..].to_vec(), drain: false, _phantom: Default::default() }
|
||||
}
|
||||
pub fn drain(mut self) -> Self {
|
||||
self.drain = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Decode + Sized> Iterator for StorageIterator<T> {
|
||||
type Item = (Vec<u8>, T);
|
||||
|
||||
fn next(&mut self) -> Option<(Vec<u8>, T)> {
|
||||
loop {
|
||||
let maybe_next = sp_io::storage::next_key(&self.previous_key)
|
||||
.filter(|n| n.starts_with(&self.prefix));
|
||||
break match maybe_next {
|
||||
Some(next) => {
|
||||
self.previous_key = next.clone();
|
||||
let maybe_value = frame_support::storage::unhashed::get::<T>(&next);
|
||||
match maybe_value {
|
||||
Some(value) => {
|
||||
if self.drain {
|
||||
frame_support::storage::unhashed::kill(&next);
|
||||
}
|
||||
Some((self.previous_key[32..].to_vec(), value))
|
||||
}
|
||||
None => continue,
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[u8]) -> Option<T> {
|
||||
let mut key = vec![0u8; 32 + hash.len()];
|
||||
key[0..16].copy_from_slice(&Twox128::hash(module));
|
||||
key[16..32].copy_from_slice(&Twox128::hash(item));
|
||||
key[32..].copy_from_slice(hash);
|
||||
frame_support::storage::unhashed::get::<T>(&key)
|
||||
}
|
||||
|
||||
pub fn put_storage_value<T: Encode>(module: &[u8], item: &[u8], hash: &[u8], value: T) {
|
||||
let mut key = vec![0u8; 32 + hash.len()];
|
||||
key[0..16].copy_from_slice(&Twox128::hash(module));
|
||||
key[16..32].copy_from_slice(&Twox128::hash(item));
|
||||
key[32..].copy_from_slice(hash);
|
||||
frame_support::storage::unhashed::put(&key, &value);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+8
-29
@@ -23,7 +23,7 @@ use frame_support::{impl_outer_origin, parameter_types};
|
||||
use frame_support::traits::Get;
|
||||
use frame_support::weights::{Weight, DispatchInfo};
|
||||
use std::cell::RefCell;
|
||||
use crate::{GenesisConfig, Module, Trait};
|
||||
use crate::{GenesisConfig, Module, Trait, decl_tests};
|
||||
|
||||
use frame_system as system;
|
||||
impl_outer_origin!{
|
||||
@@ -31,8 +31,7 @@ impl_outer_origin!{
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub(crate) static EXISTENTIAL_DEPOSIT: RefCell<u64> = RefCell::new(0);
|
||||
static CREATION_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static EXISTENTIAL_DEPOSIT: RefCell<u64> = RefCell::new(0);
|
||||
}
|
||||
|
||||
pub struct ExistentialDeposit;
|
||||
@@ -40,11 +39,6 @@ impl Get<u64> for ExistentialDeposit {
|
||||
fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct CreationFee;
|
||||
impl Get<u64> for CreationFee {
|
||||
fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Test;
|
||||
@@ -71,6 +65,9 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = super::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Module<Test>;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const TransactionBaseFee: u64 = 0;
|
||||
@@ -86,25 +83,20 @@ impl pallet_transaction_payment::Trait for Test {
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type DustRemoval = ();
|
||||
type TransferPayment = ();
|
||||
type Event = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = system::Module<Test>;
|
||||
}
|
||||
|
||||
pub struct ExtBuilder {
|
||||
existential_deposit: u64,
|
||||
creation_fee: u64,
|
||||
monied: bool,
|
||||
}
|
||||
impl Default for ExtBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
existential_deposit: 1,
|
||||
creation_fee: 0,
|
||||
monied: false,
|
||||
}
|
||||
}
|
||||
@@ -114,17 +106,12 @@ impl ExtBuilder {
|
||||
self.existential_deposit = existential_deposit;
|
||||
self
|
||||
}
|
||||
pub fn creation_fee(mut self, creation_fee: u64) -> Self {
|
||||
self.creation_fee = creation_fee;
|
||||
self
|
||||
}
|
||||
pub fn monied(mut self, monied: bool) -> Self {
|
||||
self.monied = monied;
|
||||
self
|
||||
}
|
||||
pub fn set_associated_consts(&self) {
|
||||
EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
|
||||
CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee);
|
||||
}
|
||||
pub fn build(self) -> sp_io::TestExternalities {
|
||||
self.set_associated_consts();
|
||||
@@ -146,12 +133,4 @@ impl ExtBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
pub type System = frame_system::Module<Test>;
|
||||
pub type Balances = Module<Test>;
|
||||
|
||||
pub const CALL: &<Test as frame_system::Trait>::Call = &();
|
||||
|
||||
/// create a transaction info struct from weight. Handy to avoid building the whole struct.
|
||||
pub fn info_from_weight(w: Weight) -> DispatchInfo {
|
||||
DispatchInfo { weight: w, pays_fee: true, ..Default::default() }
|
||||
}
|
||||
decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT }
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright 2018-2020 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/>.
|
||||
|
||||
//! Test utilities
|
||||
|
||||
use sp_runtime::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header};
|
||||
use sp_core::H256;
|
||||
use sp_io;
|
||||
use frame_support::{impl_outer_origin, parameter_types};
|
||||
use frame_support::traits::{Get, StorageMapShim};
|
||||
use frame_support::weights::{Weight, DispatchInfo};
|
||||
use std::cell::RefCell;
|
||||
use crate::{GenesisConfig, Module, Trait, decl_tests};
|
||||
|
||||
use frame_system as system;
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static EXISTENTIAL_DEPOSIT: RefCell<u64> = RefCell::new(0);
|
||||
}
|
||||
|
||||
pub struct ExistentialDeposit;
|
||||
impl Get<u64> for ExistentialDeposit {
|
||||
fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Test;
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const MaximumBlockWeight: Weight = 1024;
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
}
|
||||
impl frame_system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Call = ();
|
||||
type Hash = H256;
|
||||
type Hashing = ::sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = super::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Module<Test>;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const TransactionBaseFee: u64 = 0;
|
||||
pub const TransactionByteFee: u64 = 1;
|
||||
}
|
||||
impl pallet_transaction_payment::Trait for Test {
|
||||
type Currency = Module<Test>;
|
||||
type OnTransactionPayment = ();
|
||||
type TransactionBaseFee = TransactionBaseFee;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type WeightToFee = ConvertInto;
|
||||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Balance = u64;
|
||||
type DustRemoval = ();
|
||||
type Event = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = StorageMapShim<
|
||||
super::Account<Test>,
|
||||
system::CallOnCreatedAccount<Test>,
|
||||
system::CallKillAccount<Test>,
|
||||
u64, super::AccountData<u64>
|
||||
>;
|
||||
}
|
||||
|
||||
pub struct ExtBuilder {
|
||||
existential_deposit: u64,
|
||||
monied: bool,
|
||||
}
|
||||
impl Default for ExtBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
existential_deposit: 1,
|
||||
monied: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ExtBuilder {
|
||||
pub fn existential_deposit(mut self, existential_deposit: u64) -> Self {
|
||||
self.existential_deposit = existential_deposit;
|
||||
self
|
||||
}
|
||||
pub fn monied(mut self, monied: bool) -> Self {
|
||||
self.monied = monied;
|
||||
if self.existential_deposit == 0 {
|
||||
self.existential_deposit = 1;
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn set_associated_consts(&self) {
|
||||
EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
|
||||
}
|
||||
pub fn build(self) -> sp_io::TestExternalities {
|
||||
self.set_associated_consts();
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
GenesisConfig::<Test> {
|
||||
balances: if self.monied {
|
||||
vec![
|
||||
(1, 10 * self.existential_deposit),
|
||||
(2, 20 * self.existential_deposit),
|
||||
(3, 30 * self.existential_deposit),
|
||||
(4, 40 * self.existential_deposit),
|
||||
(12, 10 * self.existential_deposit)
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
t.into()
|
||||
}
|
||||
}
|
||||
|
||||
decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT }
|
||||
Reference in New Issue
Block a user