mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +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:
@@ -293,6 +293,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Event = ();
|
||||
|
||||
@@ -61,6 +61,9 @@ impl frame_system::Trait for Test {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Trait for Test {
|
||||
|
||||
@@ -157,6 +157,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl_outer_origin! {
|
||||
|
||||
@@ -431,6 +431,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
|
||||
@@ -66,6 +66,9 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl_opaque_keys! {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -433,6 +433,9 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
impl Trait<Instance1> for Test {
|
||||
type Origin = Origin;
|
||||
@@ -454,7 +457,7 @@ mod tests {
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Collective: collective::<Instance1>::{Module, Call, Event<T>, Origin<T>, Config<T>},
|
||||
DefaultCollective: collective::{Module, Call, Event<T>, Origin<T>, Config<T>},
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ use sp_std::collections::btree_map::{BTreeMap, Entry};
|
||||
use sp_std::prelude::*;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::traits::{Bounded, Zero};
|
||||
use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance, UpdateBalanceOutcome};
|
||||
use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance};
|
||||
use frame_support::{storage::child, StorageMap};
|
||||
use frame_system;
|
||||
|
||||
@@ -146,9 +146,11 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
|
||||
let mut total_imbalance = SignedImbalance::zero();
|
||||
for (address, changed) in s.into_iter() {
|
||||
if let Some(balance) = changed.balance() {
|
||||
let (imbalance, outcome) = T::Currency::make_free_balance_be(&address, balance);
|
||||
let existed = !T::Currency::total_balance(&address).is_zero();
|
||||
let imbalance = T::Currency::make_free_balance_be(&address, balance);
|
||||
let exists = !T::Currency::total_balance(&address).is_zero();
|
||||
total_imbalance = total_imbalance.merge(imbalance);
|
||||
if let UpdateBalanceOutcome::AccountKilled = outcome {
|
||||
if existed && !exists {
|
||||
// Account killed. This will ultimately lead to calling `OnReapAccount` callback
|
||||
// which will make removal of CodeHashOf and AccountStorage for this account.
|
||||
// In order to avoid writing over the deleted properties we `continue` here.
|
||||
|
||||
@@ -554,7 +554,6 @@ where
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TransferFeeKind {
|
||||
ContractInstantiate,
|
||||
AccountCreate,
|
||||
Transfer,
|
||||
}
|
||||
|
||||
@@ -572,7 +571,6 @@ impl<T: Trait> Token<T> for TransferFeeToken<BalanceOf<T>> {
|
||||
fn calculate_amount(&self, metadata: &Config<T>) -> Gas {
|
||||
let balance_fee = match self.kind {
|
||||
TransferFeeKind::ContractInstantiate => metadata.contract_account_instantiate_fee,
|
||||
TransferFeeKind::AccountCreate => metadata.account_create_fee,
|
||||
TransferFeeKind::Transfer => return metadata.schedule.transfer_cost,
|
||||
};
|
||||
approx_gas_for_balance(self.gas_price, balance_fee)
|
||||
@@ -612,28 +610,14 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
use self::TransferCause::*;
|
||||
use self::TransferFeeKind::*;
|
||||
|
||||
let to_balance = ctx.overlay.get_balance(dest);
|
||||
|
||||
// `would_create` indicates whether the account will be created if this transfer gets executed.
|
||||
// This flag is orthogonal to `cause.
|
||||
// For example, we can instantiate a contract at the address which already has some funds. In this
|
||||
// `would_create` will be `false`. Another example would be when this function is called from `call`,
|
||||
// and account with the address `dest` doesn't exist yet `would_create` will be `true`.
|
||||
let would_create = to_balance.is_zero();
|
||||
|
||||
let token = {
|
||||
let kind: TransferFeeKind = match cause {
|
||||
// If this function is called from `Instantiate` routine, then we always
|
||||
// charge contract account creation fee.
|
||||
Instantiate => ContractInstantiate,
|
||||
|
||||
// Otherwise the fee depends on whether we create a new account or transfer
|
||||
// to an existing one.
|
||||
Call => if would_create {
|
||||
TransferFeeKind::AccountCreate
|
||||
} else {
|
||||
TransferFeeKind::Transfer
|
||||
},
|
||||
// Otherwise the fee is to transfer to an account.
|
||||
Call => TransferFeeKind::Transfer,
|
||||
};
|
||||
TransferFeeToken {
|
||||
kind,
|
||||
@@ -651,7 +635,8 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
|
||||
Some(b) => b,
|
||||
None => Err("balance too low to send value")?,
|
||||
};
|
||||
if would_create && value < ctx.config.existential_deposit {
|
||||
let to_balance = ctx.overlay.get_balance(dest);
|
||||
if to_balance.is_zero() && value < ctx.config.existential_deposit {
|
||||
Err("value too low to create account")?
|
||||
}
|
||||
T::Currency::ensure_can_withdraw(
|
||||
@@ -1105,7 +1090,7 @@ mod tests {
|
||||
toks,
|
||||
ExecFeeToken::Call,
|
||||
TransferFeeToken {
|
||||
kind: TransferFeeKind::AccountCreate,
|
||||
kind: TransferFeeKind::Transfer,
|
||||
gas_price: 1u64
|
||||
},
|
||||
);
|
||||
|
||||
@@ -401,9 +401,6 @@ pub trait Trait: frame_system::Trait {
|
||||
/// to removal of a contract.
|
||||
type SurchargeReward: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The fee required to create an account.
|
||||
type CreationFee: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
type TransactionBaseFee: Get<BalanceOf<Self>>;
|
||||
|
||||
@@ -517,9 +514,6 @@ decl_module! {
|
||||
/// to removal of a contract.
|
||||
const SurchargeReward: BalanceOf<T> = T::SurchargeReward::get();
|
||||
|
||||
/// The fee required to create an account.
|
||||
const CreationFee: BalanceOf<T> = T::CreationFee::get();
|
||||
|
||||
/// The fee to be paid for making a transaction; the base.
|
||||
const TransactionBaseFee: BalanceOf<T> = T::TransactionBaseFee::get();
|
||||
|
||||
@@ -966,7 +960,6 @@ pub struct Config<T: Trait> {
|
||||
pub max_depth: u32,
|
||||
pub max_value_size: u32,
|
||||
pub contract_account_instantiate_fee: BalanceOf<T>,
|
||||
pub account_create_fee: BalanceOf<T>,
|
||||
}
|
||||
|
||||
impl<T: Trait> Config<T> {
|
||||
@@ -978,7 +971,6 @@ impl<T: Trait> Config<T> {
|
||||
max_depth: T::MaxDepth::get(),
|
||||
max_value_size: T::MaxValueSize::get(),
|
||||
contract_account_instantiate_fee: T::ContractFee::get(),
|
||||
account_create_fee: T::CreationFee::get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}};
|
||||
use sp_core::storage::well_known_keys;
|
||||
use frame_system::{self as system, EventRecord, Phase};
|
||||
|
||||
mod contract {
|
||||
mod contracts {
|
||||
// Re-export contents of the root. This basically
|
||||
// needs to give a name for the current crate.
|
||||
// This hack is required for `impl_outer_event!`.
|
||||
@@ -53,7 +53,9 @@ use pallet_balances as balances;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum MetaEvent for Test {
|
||||
balances<T>, contract<T>,
|
||||
system<T>,
|
||||
balances<T>,
|
||||
contracts<T>,
|
||||
}
|
||||
}
|
||||
impl_outer_origin! {
|
||||
@@ -62,7 +64,7 @@ impl_outer_origin! {
|
||||
impl_outer_dispatch! {
|
||||
pub enum Call for Test where origin: Origin {
|
||||
balances::Balances,
|
||||
contract::Contract,
|
||||
contracts::Contracts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +85,6 @@ impl Get<u64> for TransferFee {
|
||||
fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct CreationFee;
|
||||
impl Get<u64> for CreationFee {
|
||||
fn get() -> u64 { INSTANTIATION_FEE.with(|v| *v.borrow()) }
|
||||
}
|
||||
|
||||
pub struct BlockGasLimit;
|
||||
impl Get<u64> for BlockGasLimit {
|
||||
fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) }
|
||||
@@ -118,16 +115,16 @@ impl frame_system::Trait for Test {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = (Balances, Contracts);
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = (System, Contract);
|
||||
type OnNewAccount = ();
|
||||
type Event = MetaEvent;
|
||||
type DustRemoval = ();
|
||||
type TransferPayment = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const MinimumPeriod: u64 = 1;
|
||||
@@ -169,7 +166,6 @@ impl Trait for Test {
|
||||
type RentByteFee = RentByteFee;
|
||||
type RentDepositOffset = RentDepositOffset;
|
||||
type SurchargeReward = SurchargeReward;
|
||||
type CreationFee = CreationFee;
|
||||
type TransactionBaseFee = TransactionBaseFee;
|
||||
type TransactionByteFee = TransactionByteFee;
|
||||
type ContractFee = ContractFee;
|
||||
@@ -182,7 +178,7 @@ impl Trait for Test {
|
||||
|
||||
type Balances = pallet_balances::Module<Test>;
|
||||
type Timestamp = pallet_timestamp::Module<Test>;
|
||||
type Contract = Module<Test>;
|
||||
type Contracts = Module<Test>;
|
||||
type System = frame_system::Module<Test>;
|
||||
type Randomness = pallet_randomness_collective_flip::Module<Test>;
|
||||
|
||||
@@ -304,7 +300,7 @@ fn refunds_unused_gas() {
|
||||
ExtBuilder::default().gas_price(2).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 100_000_000);
|
||||
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new()));
|
||||
|
||||
// 2 * 135 - gas price multiplied by the call base fee.
|
||||
assert_eq!(Balances::free_balance(ALICE), 100_000_000 - (2 * 135));
|
||||
@@ -413,10 +409,10 @@ fn instantiate_and_call_and_deposit_event() {
|
||||
ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Check at the end to get hash on error easily
|
||||
let creation = Contract::instantiate(
|
||||
let creation = Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000,
|
||||
@@ -427,34 +423,44 @@ fn instantiate_and_call_and_deposit_event() {
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::NewAccount(BOB, 100)
|
||||
pallet_balances::RawEvent::Endowed(BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])),
|
||||
event: MetaEvent::contracts(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)),
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)),
|
||||
topics: vec![],
|
||||
}
|
||||
]);
|
||||
@@ -493,24 +499,29 @@ fn dispatch_call() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Let's keep this assert even though it's redundant. If you ever need to update the
|
||||
// wasm source this test will fail and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000,
|
||||
@@ -518,7 +529,7 @@ fn dispatch_call() {
|
||||
vec![],
|
||||
));
|
||||
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, // newly created account
|
||||
0,
|
||||
@@ -529,44 +540,59 @@ fn dispatch_call() {
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::NewAccount(BOB, 100)
|
||||
pallet_balances::RawEvent::Endowed(BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)),
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
|
||||
// Dispatching the call.
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::NewAccount(CHARLIE, 50)
|
||||
pallet_balances::RawEvent::Endowed(CHARLIE, 50)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0)
|
||||
pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
@@ -574,7 +600,7 @@ fn dispatch_call() {
|
||||
// Event emited as a result of dispatch.
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)),
|
||||
event: MetaEvent::contracts(RawEvent::Dispatched(BOB, true)),
|
||||
topics: vec![],
|
||||
}
|
||||
]);
|
||||
@@ -611,24 +637,29 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Let's keep this assert even though it's redundant. If you ever need to update the
|
||||
// wasm source this test will fail and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000,
|
||||
@@ -639,7 +670,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
|
||||
// Call the newly instantiated contract. The contract is expected to dispatch a call
|
||||
// and then trap.
|
||||
assert_err!(
|
||||
Contract::call(
|
||||
Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, // newly created account
|
||||
0,
|
||||
@@ -651,29 +682,39 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(
|
||||
pallet_balances::RawEvent::NewAccount(BOB, 100)
|
||||
pallet_balances::RawEvent::Endowed(BOB, 100)
|
||||
),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)),
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)),
|
||||
topics: vec![],
|
||||
},
|
||||
// ABSENCE of events which would be caused by dispatched Balances::transfer call
|
||||
@@ -810,19 +851,24 @@ fn test_set_rent_code_and_hash() {
|
||||
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// If you ever need to update the wasm source this test will fail
|
||||
// and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
@@ -837,8 +883,8 @@ fn storage_size() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
30_000,
|
||||
100_000, code_hash.into(),
|
||||
@@ -847,11 +893,11 @@ fn storage_size() {
|
||||
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
|
||||
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4);
|
||||
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte()));
|
||||
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
|
||||
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4 + 4);
|
||||
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte()));
|
||||
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
|
||||
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4);
|
||||
});
|
||||
@@ -874,8 +920,8 @@ fn deduct_blocks() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
30_000,
|
||||
100_000, code_hash.into(),
|
||||
@@ -890,7 +936,7 @@ fn deduct_blocks() {
|
||||
initialize_block(5);
|
||||
|
||||
// Trigger rent through call
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
|
||||
// Check result
|
||||
let rent = (8 + 4 - 3) // storage size = size_offset + deploy_set_storage - deposit_offset
|
||||
@@ -905,7 +951,7 @@ fn deduct_blocks() {
|
||||
initialize_block(12);
|
||||
|
||||
// Trigger rent through call
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
|
||||
// Check result
|
||||
let rent_2 = (8 + 4 - 2) // storage size = size_offset + deploy_set_storage - deposit_offset
|
||||
@@ -917,7 +963,7 @@ fn deduct_blocks() {
|
||||
assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2);
|
||||
|
||||
// Second call on same block should have no effect on rent
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
|
||||
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
|
||||
assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2);
|
||||
@@ -930,34 +976,34 @@ fn deduct_blocks() {
|
||||
fn call_contract_removals() {
|
||||
removals(|| {
|
||||
// Call on already-removed account might fail, and this is fine.
|
||||
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null());
|
||||
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null());
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inherent_claim_surcharge_contract_removals() {
|
||||
removals(|| Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok());
|
||||
removals(|| Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_claim_surcharge_contract_removals() {
|
||||
removals(|| Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok());
|
||||
removals(|| Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claim_surcharge_malus() {
|
||||
// Test surcharge malus for inherent
|
||||
claim_surcharge(4, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(3, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(2, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(1, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false);
|
||||
claim_surcharge(4, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(3, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(2, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true);
|
||||
claim_surcharge(1, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false);
|
||||
|
||||
// Test surcharge malus for signed
|
||||
claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true);
|
||||
claim_surcharge(3, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
claim_surcharge(2, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
claim_surcharge(1, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
claim_surcharge(4, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true);
|
||||
claim_surcharge(3, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
claim_surcharge(2, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
claim_surcharge(1, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false);
|
||||
}
|
||||
|
||||
/// Claim surcharge with the given trigger_call at the given blocks.
|
||||
@@ -968,8 +1014,8 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool)
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000, code_hash.into(),
|
||||
@@ -1001,8 +1047,8 @@ fn removals(trigger_call: impl Fn() -> bool) {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000, code_hash.into(),
|
||||
@@ -1037,8 +1083,8 @@ fn removals(trigger_call: impl Fn() -> bool) {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
1_000,
|
||||
100_000, code_hash.into(),
|
||||
@@ -1072,8 +1118,8 @@ fn removals(trigger_call: impl Fn() -> bool) {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
50+Balances::minimum_balance(),
|
||||
100_000, code_hash.into(),
|
||||
@@ -1086,7 +1132,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
|
||||
assert_eq!(Balances::free_balance(BOB), 50 + Balances::minimum_balance());
|
||||
|
||||
// Transfer funds
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer()));
|
||||
assert_eq!(ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000);
|
||||
assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance());
|
||||
|
||||
@@ -1116,8 +1162,8 @@ fn call_removed_contract() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone()));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000, code_hash.into(),
|
||||
@@ -1125,28 +1171,28 @@ fn call_removed_contract() {
|
||||
));
|
||||
|
||||
// Calling contract should succeed.
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
|
||||
// Advance blocks
|
||||
initialize_block(10);
|
||||
|
||||
// Calling contract should remove contract and fail.
|
||||
assert_err!(
|
||||
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
"contract has been evicted"
|
||||
);
|
||||
// Calling a contract that is about to evict shall emit an event.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Evicted(BOB, true)),
|
||||
event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
// Subsequent contract calls should also fail.
|
||||
assert_err!(
|
||||
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
"contract has been evicted"
|
||||
);
|
||||
})
|
||||
@@ -1209,8 +1255,8 @@ fn default_rent_allowance_on_instantiate() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
30_000,
|
||||
100_000,
|
||||
@@ -1226,7 +1272,7 @@ fn default_rent_allowance_on_instantiate() {
|
||||
initialize_block(5);
|
||||
|
||||
// Trigger rent through call
|
||||
assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
|
||||
|
||||
// Check contract is still alive
|
||||
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive();
|
||||
@@ -1322,32 +1368,37 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, restoration_wasm));
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, restoration_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm));
|
||||
|
||||
// If you ever need to update the wasm source this test will fail
|
||||
// and will show you the actual hash.
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::CodeStored(set_rent_code_hash.into())),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(restoration_code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::CodeStored(set_rent_code_hash.into())),
|
||||
topics: vec![],
|
||||
},
|
||||
]);
|
||||
|
||||
// Create an account with address `BOB` with code `CODE_SET_RENT`.
|
||||
// The input parameter sets the rent allowance to 0.
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
30_000,
|
||||
100_000,
|
||||
@@ -1361,7 +1412,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
assert_eq!(bob_contract.rent_allowance, 0);
|
||||
|
||||
if test_different_storage {
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB, 0, 100_000,
|
||||
call::set_storage_4_byte())
|
||||
@@ -1377,14 +1428,14 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
// Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0
|
||||
// we expect that it will get removed leaving tombstone.
|
||||
assert_err!(
|
||||
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
|
||||
"contract has been evicted"
|
||||
);
|
||||
assert!(ContractInfoOf::<Test>::get(BOB).unwrap().get_tombstone().is_some());
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(
|
||||
event: MetaEvent::contracts(
|
||||
RawEvent::Evicted(BOB.clone(), true)
|
||||
),
|
||||
topics: vec![],
|
||||
@@ -1396,7 +1447,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
/// Note that we can't use `ALICE` for creating `DJANGO` so we create yet another
|
||||
/// account `CHARLIE` and create `DJANGO` with it.
|
||||
Balances::deposit_creating(&CHARLIE, 1_000_000);
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(CHARLIE),
|
||||
30_000,
|
||||
100_000,
|
||||
@@ -1415,7 +1466,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
|
||||
// Perform a call to `DJANGO`. This should either perform restoration successfully or
|
||||
// fail depending on the test parameters.
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
DJANGO,
|
||||
0,
|
||||
@@ -1437,7 +1488,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(
|
||||
event: MetaEvent::contracts(
|
||||
RawEvent::Restored(DJANGO, BOB, bob_code_hash, 50, false)
|
||||
),
|
||||
topics: vec![],
|
||||
@@ -1448,32 +1499,42 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Evicted(BOB, true)),
|
||||
event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(CHARLIE, 1_000_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(DJANGO, 30_000)),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(CHARLIE, 1_000_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)),
|
||||
event: MetaEvent::system(frame_system::RawEvent::NewAccount(DJANGO)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Instantiated(CHARLIE, DJANGO)),
|
||||
event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(DJANGO, 30_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(RawEvent::Restored(
|
||||
event: MetaEvent::contracts(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::Instantiated(CHARLIE, DJANGO)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contracts(RawEvent::Restored(
|
||||
DJANGO,
|
||||
BOB,
|
||||
bob_code_hash,
|
||||
@@ -1500,12 +1561,12 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
|
||||
assert_eq!(System::events(), vec![
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::balances(balances::RawEvent::ReapedAccount(DJANGO, 0)),
|
||||
event: MetaEvent::system(system::RawEvent::ReapedAccount(DJANGO)),
|
||||
topics: vec![],
|
||||
},
|
||||
EventRecord {
|
||||
phase: Phase::ApplyExtrinsic(0),
|
||||
event: MetaEvent::contract(
|
||||
event: MetaEvent::contracts(
|
||||
RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50, true)
|
||||
),
|
||||
topics: vec![],
|
||||
@@ -1586,8 +1647,8 @@ fn storage_max_value_limit() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
30_000,
|
||||
100_000,
|
||||
@@ -1600,7 +1661,7 @@ fn storage_max_value_limit() {
|
||||
assert_eq!(bob_contract.rent_allowance, <BalanceOf<Test>>::max_value());
|
||||
|
||||
// Call contract with allowed storage value.
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -1610,7 +1671,7 @@ fn storage_max_value_limit() {
|
||||
|
||||
// Call contract with too large a storage value.
|
||||
assert_err!(
|
||||
Contract::call(
|
||||
Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -1950,10 +2011,10 @@ fn deploy_and_call_other_contract() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm));
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm));
|
||||
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
100_000,
|
||||
@@ -1963,7 +2024,7 @@ fn deploy_and_call_other_contract() {
|
||||
|
||||
// Call BOB contract, which attempts to instantiate and call the callee contract and
|
||||
// makes various assertions on the results from those calls.
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -2077,10 +2138,10 @@ fn self_destruct_by_draining_balance() {
|
||||
let (wasm, code_hash) = compile_module::<Test>(CODE_SELF_DESTRUCT).unwrap();
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Instantiate the BOB contract.
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
100_000,
|
||||
@@ -2095,7 +2156,7 @@ fn self_destruct_by_draining_balance() {
|
||||
);
|
||||
|
||||
// Call BOB with no input data, forcing it to self-destruct.
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -2113,10 +2174,10 @@ fn cannot_self_destruct_while_live() {
|
||||
let (wasm, code_hash) = compile_module::<Test>(CODE_SELF_DESTRUCT).unwrap();
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Instantiate the BOB contract.
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
100_000,
|
||||
@@ -2133,7 +2194,7 @@ fn cannot_self_destruct_while_live() {
|
||||
// Call BOB with input data, forcing it make a recursive call to itself to
|
||||
// self-destruct, resulting in a trap.
|
||||
assert_err!(
|
||||
Contract::call(
|
||||
Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -2313,12 +2374,12 @@ fn destroy_contract_and_transfer_funds() {
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
// Create
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm));
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm));
|
||||
|
||||
// This deploys the BOB contract, which in turn deploys the CHARLIE contract during
|
||||
// construction.
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
200_000,
|
||||
100_000,
|
||||
@@ -2333,7 +2394,7 @@ fn destroy_contract_and_transfer_funds() {
|
||||
);
|
||||
|
||||
// Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct.
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
@@ -2408,12 +2469,12 @@ fn cannot_self_destruct_in_constructor() {
|
||||
let (wasm, code_hash) = compile_module::<Test>(CODE_SELF_DESTRUCTING_CONSTRUCTOR).unwrap();
|
||||
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
|
||||
Balances::deposit_creating(&ALICE, 1_000_000);
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
|
||||
// Fail to instantiate the BOB contract since its final balance is below existential
|
||||
// deposit.
|
||||
assert_err!(
|
||||
Contract::instantiate(
|
||||
Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100_000,
|
||||
100_000,
|
||||
@@ -2529,15 +2590,15 @@ fn get_runtime_storage() {
|
||||
0x14144020u32.to_le_bytes().to_vec().as_ref()
|
||||
);
|
||||
|
||||
assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contract::instantiate(
|
||||
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
|
||||
assert_ok!(Contracts::instantiate(
|
||||
Origin::signed(ALICE),
|
||||
100,
|
||||
100_000,
|
||||
code_hash.into(),
|
||||
vec![],
|
||||
));
|
||||
assert_ok!(Contract::call(
|
||||
assert_ok!(Contracts::call(
|
||||
Origin::signed(ALICE),
|
||||
BOB,
|
||||
0,
|
||||
|
||||
@@ -30,7 +30,7 @@ use frame_support::{
|
||||
weights::SimpleDispatchInfo,
|
||||
traits::{
|
||||
Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get,
|
||||
OnReapAccount, OnUnbalanced
|
||||
OnReapAccount, OnUnbalanced, BalanceStatus
|
||||
}
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
@@ -802,7 +802,7 @@ decl_module! {
|
||||
let queue = <DispatchQueue<T>>::get();
|
||||
ensure!(!queue.iter().any(|item| &item.1 == &proposal_hash), Error::<T>::Imminent);
|
||||
|
||||
let _ = T::Currency::repatriate_reserved(&old, &who, deposit);
|
||||
let _ = T::Currency::repatriate_reserved(&old, &who, deposit, BalanceStatus::Free);
|
||||
<Preimages<T>>::remove(&proposal_hash);
|
||||
Self::deposit_event(RawEvent::PreimageReaped(proposal_hash, old, deposit, who));
|
||||
}
|
||||
@@ -1227,20 +1227,19 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const LaunchPeriod: u64 = 2;
|
||||
|
||||
@@ -91,7 +91,7 @@ use frame_support::{
|
||||
decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo,
|
||||
traits::{
|
||||
Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons,
|
||||
ChangeMembers, OnUnbalanced, WithdrawReason, Contains
|
||||
ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus
|
||||
}
|
||||
};
|
||||
use sp_phragmen::ExtendedBalance;
|
||||
@@ -314,7 +314,7 @@ decl_module! {
|
||||
let valid = Self::is_defunct_voter(&target);
|
||||
if valid {
|
||||
// reporter will get the voting bond of the target
|
||||
T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get())?;
|
||||
T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get(), BalanceStatus::Free)?;
|
||||
// remove the target. They are defunct.
|
||||
Self::do_remove_voter(&target, false);
|
||||
} else {
|
||||
@@ -814,23 +814,22 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = System;
|
||||
type Event = Event;
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
}
|
||||
type AccountStore = frame_system::Module<Test>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const CandidacyBond: u64 = 3;
|
||||
@@ -937,7 +936,7 @@ mod tests {
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Balances: pallet_balances::{Module, Call, Event<T>, Config<T>},
|
||||
Elections: elections::{Module, Call, Event<T>},
|
||||
}
|
||||
@@ -1427,7 +1426,7 @@ mod tests {
|
||||
|
||||
assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 3));
|
||||
assert_eq!(
|
||||
System::events()[1].event,
|
||||
System::events()[7].event,
|
||||
Event::elections(RawEvent::VoterReported(3, 5, true))
|
||||
);
|
||||
|
||||
@@ -1456,7 +1455,7 @@ mod tests {
|
||||
|
||||
assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 4));
|
||||
assert_eq!(
|
||||
System::events()[1].event,
|
||||
System::events()[7].event,
|
||||
Event::elections(RawEvent::VoterReported(4, 5, false))
|
||||
);
|
||||
|
||||
@@ -1867,7 +1866,7 @@ mod tests {
|
||||
assert_eq!(balances(&5), (45, 2));
|
||||
|
||||
assert_eq!(
|
||||
System::events()[0].event,
|
||||
System::events()[6].event,
|
||||
Event::elections(RawEvent::NewTerm(vec![(4, 40), (5, 50)])),
|
||||
);
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ use frame_support::{
|
||||
decl_storage, decl_event, ensure, decl_module, decl_error,
|
||||
weights::SimpleDispatchInfo,
|
||||
traits::{
|
||||
Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier,
|
||||
Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, BalanceStatus,
|
||||
OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, ChangeMembers
|
||||
}
|
||||
};
|
||||
@@ -501,7 +501,7 @@ 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.
|
||||
T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get())?;
|
||||
T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get(), BalanceStatus::Free)?;
|
||||
Self::deposit_event(RawEvent::VoterReaped(who, reporter));
|
||||
} else {
|
||||
let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0;
|
||||
|
||||
@@ -39,9 +39,9 @@ parameter_types! {
|
||||
}
|
||||
impl frame_system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Call = ();
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Call = ();
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
@@ -54,21 +54,20 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = System;
|
||||
type Event = Event;
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type Event = Event;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
@@ -151,7 +150,7 @@ frame_support::construct_runtime!(
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Balances: pallet_balances::{Module, Call, Event<T>, Config<T>},
|
||||
Elections: elections::{Module, Call, Event<T>, Config<T>},
|
||||
}
|
||||
@@ -266,8 +265,7 @@ pub(crate) fn create_candidate(i: u64, index: u32) {
|
||||
}
|
||||
|
||||
pub(crate) fn balances(who: &u64) -> (u64, u64) {
|
||||
let a = Balances::account(who);
|
||||
(a.free, a.reserved)
|
||||
(Balances::free_balance(who), Balances::reserved_balance(who))
|
||||
}
|
||||
|
||||
pub(crate) fn locks(who: &u64) -> Vec<u64> {
|
||||
|
||||
@@ -688,20 +688,19 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = pallet_balances::Module<Test>;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type Event = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Event = ();
|
||||
|
||||
@@ -416,6 +416,7 @@ mod tests {
|
||||
|
||||
impl_outer_event!{
|
||||
pub enum MetaEvent for Runtime {
|
||||
system<T>,
|
||||
balances<T>,
|
||||
}
|
||||
}
|
||||
@@ -451,20 +452,19 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Runtime {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = MetaEvent;
|
||||
type DustRemoval = ();
|
||||
type TransferPayment = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
@@ -559,7 +559,7 @@ mod tests {
|
||||
header: Header {
|
||||
parent_hash: [69u8; 32].into(),
|
||||
number: 1,
|
||||
state_root: hex!("a0b84fec49718caf59350dab6ec2993f12db399a7cccdb80f3cf79618ed93bd8").into(),
|
||||
state_root: hex!("96797237079b6d6ffab7a47f90ee257a439a0e8268bdab3fe2f1e52572b101de").into(),
|
||||
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
|
||||
digest: Digest { logs: vec![], },
|
||||
},
|
||||
|
||||
@@ -261,6 +261,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const WindowSize: u64 = 11;
|
||||
|
||||
@@ -166,7 +166,7 @@ use frame_support::{
|
||||
decl_event, decl_module, decl_storage, ensure, decl_error,
|
||||
traits::{
|
||||
Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency,
|
||||
SignedImbalance, UpdateBalanceOutcome, WithdrawReason, WithdrawReasons, TryDrop,
|
||||
SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, BalanceStatus,
|
||||
},
|
||||
Parameter, StorageMap,
|
||||
};
|
||||
@@ -714,8 +714,8 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Move up to `amount` from reserved balance of account `who` to free balance of account
|
||||
/// `beneficiary`.
|
||||
/// Move up to `amount` from reserved balance of account `who` to balance of account
|
||||
/// `beneficiary`, either free or reserved depending on `status`.
|
||||
///
|
||||
/// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then
|
||||
/// the `remaining` would be returned, else `Zero::zero()`.
|
||||
@@ -726,13 +726,23 @@ impl<T: Trait> Module<T> {
|
||||
who: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
amount: T::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> T::Balance {
|
||||
let b = Self::reserved_balance(asset_id, who);
|
||||
let slash = sp_std::cmp::min(b, amount);
|
||||
|
||||
let original_free_balance = Self::free_balance(asset_id, beneficiary);
|
||||
let new_free_balance = original_free_balance + slash;
|
||||
Self::set_free_balance(asset_id, beneficiary, new_free_balance);
|
||||
match status {
|
||||
BalanceStatus::Free => {
|
||||
let original_free_balance = Self::free_balance(asset_id, beneficiary);
|
||||
let new_free_balance = original_free_balance + slash;
|
||||
Self::set_free_balance(asset_id, beneficiary, new_free_balance);
|
||||
}
|
||||
BalanceStatus::Reserved => {
|
||||
let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary);
|
||||
let new_reserved_balance = original_reserved_balance + slash;
|
||||
Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance);
|
||||
}
|
||||
}
|
||||
|
||||
let new_reserve_balance = b - slash;
|
||||
Self::set_reserved_balance(asset_id, who, new_reserve_balance);
|
||||
@@ -1080,8 +1090,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 (TransactionPayment, TransferPayment, DustRemoval)
|
||||
// This should eventually be refactored so that the two type items that do
|
||||
// depend on the Imbalance type (TransactionPayment, DustRemoval)
|
||||
// are placed in their own SRML module.
|
||||
struct ElevatedTrait<T: Subtrait>(T);
|
||||
impl<T: Subtrait> Clone for ElevatedTrait<T> {
|
||||
@@ -1106,12 +1116,15 @@ impl<T: Subtrait> frame_system::Trait for ElevatedTrait<T> {
|
||||
type Lookup = T::Lookup;
|
||||
type Header = T::Header;
|
||||
type Event = ();
|
||||
type BlockHashCount = T::BlockHashCount;
|
||||
type MaximumBlockWeight = T::MaximumBlockWeight;
|
||||
type MaximumBlockLength = T::MaximumBlockLength;
|
||||
type AvailableBlockRatio = T::AvailableBlockRatio;
|
||||
type BlockHashCount = T::BlockHashCount;
|
||||
type Version = T::Version;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
impl<T: Subtrait> Trait for ElevatedTrait<T> {
|
||||
type Balance = T::Balance;
|
||||
@@ -1189,7 +1202,7 @@ where
|
||||
}
|
||||
|
||||
fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
|
||||
let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value);
|
||||
let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value);
|
||||
if let SignedImbalance::Positive(p) = imbalance {
|
||||
p
|
||||
} else {
|
||||
@@ -1201,10 +1214,7 @@ where
|
||||
fn make_free_balance_be(
|
||||
who: &T::AccountId,
|
||||
balance: Self::Balance,
|
||||
) -> (
|
||||
SignedImbalance<Self::Balance, Self::PositiveImbalance>,
|
||||
UpdateBalanceOutcome,
|
||||
) {
|
||||
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
|
||||
let original = <Module<T>>::free_balance(&U::asset_id(), who);
|
||||
let imbalance = if original <= balance {
|
||||
SignedImbalance::Positive(PositiveImbalance::new(balance - original))
|
||||
@@ -1212,7 +1222,7 @@ where
|
||||
SignedImbalance::Negative(NegativeImbalance::new(original - balance))
|
||||
};
|
||||
<Module<T>>::set_free_balance(&U::asset_id(), who, balance);
|
||||
(imbalance, UpdateBalanceOutcome::Updated)
|
||||
imbalance
|
||||
}
|
||||
|
||||
fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
|
||||
@@ -1288,8 +1298,9 @@ where
|
||||
slashed: &T::AccountId,
|
||||
beneficiary: &T::AccountId,
|
||||
value: Self::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> result::Result<Self::Balance, DispatchError> {
|
||||
Ok(<Module<T>>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value))
|
||||
Ok(<Module<T>>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ use frame_support::{parameter_types, impl_outer_event, impl_outer_origin, weight
|
||||
use super::*;
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test where system = frame_system {}
|
||||
pub enum Origin for Test where system = frame_system {}
|
||||
}
|
||||
|
||||
// For testing the module, we construct most of a mock runtime. This means
|
||||
@@ -62,6 +62,9 @@ impl frame_system::Trait for Test {
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
@@ -77,6 +80,7 @@ mod generic_asset {
|
||||
use frame_system as system;
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for Test {
|
||||
system<T>,
|
||||
generic_asset<T>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ fn slash_reserved_should_return_none() {
|
||||
fn repatriate_reserved_return_amount_substracted_by_slash_amount() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
GenericAsset::set_reserved_balance(&1, &0, 100);
|
||||
assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130), 30);
|
||||
assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130, BalanceStatus::Free), 30);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -571,7 +571,7 @@ fn repatriate_reserved_return_amount_substracted_by_slash_amount() {
|
||||
fn repatriate_reserved_return_none() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
GenericAsset::set_reserved_balance(&1, &0, 100);
|
||||
assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90), 0);
|
||||
assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90, BalanceStatus::Free), 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
mod grandpa {
|
||||
@@ -73,6 +76,7 @@ mod grandpa {
|
||||
|
||||
impl_outer_event!{
|
||||
pub enum TestEvent for Test {
|
||||
system<T>,
|
||||
grandpa,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
Judgement::Reasonable
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
// Create identity info with x additional fields
|
||||
let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1;
|
||||
// 32 byte data that we reuse below
|
||||
@@ -171,7 +171,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// Generic data to be used.
|
||||
let data = Data::Raw(vec![0; 32]);
|
||||
|
||||
@@ -212,7 +212,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
|
||||
@@ -262,7 +262,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
|
||||
@@ -296,7 +296,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
|
||||
@@ -330,7 +330,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
@@ -359,7 +359,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
@@ -388,7 +388,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||
@@ -410,7 +410,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark `provide_judgement` extrinsic.
|
||||
// Benchmark `provide_judgement` extrinsic.g
|
||||
struct ProvideJudgement;
|
||||
impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for ProvideJudgement {
|
||||
|
||||
@@ -425,7 +425,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// Add r registrars
|
||||
let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1;
|
||||
benchmarking::add_registrars::<T>(r)?;
|
||||
@@ -477,7 +477,7 @@ impl<T: Trait> BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>> for
|
||||
|
||||
fn instance(&self, components: &[(BenchmarkParameter, u32)])
|
||||
-> Result<(crate::Call<T>, RawOrigin<T::AccountId>), &'static str>
|
||||
{
|
||||
{
|
||||
// The target user
|
||||
let caller = account::<T>("caller", 0);
|
||||
let caller_origin: <T as frame_system::Trait>::Origin = RawOrigin::Signed(caller.clone()).into();
|
||||
@@ -550,8 +550,8 @@ impl<T: Trait> Benchmarking<BenchmarkResults> for Module<T> {
|
||||
sp_io::benchmarking::commit_db();
|
||||
sp_io::benchmarking::wipe_db();
|
||||
|
||||
// first one is set_identity.
|
||||
let components = <SelectedBenchmark as BenchmarkingSetup<T, crate::Call<T>, RawOrigin<T::AccountId>>>::components(&selected_benchmark);
|
||||
// first one is set_identity.
|
||||
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.
|
||||
|
||||
@@ -73,7 +73,7 @@ use sp_runtime::{DispatchResult, RuntimeDebug};
|
||||
use sp_runtime::traits::{StaticLookup, EnsureOrigin, Zero, AppendZerosInput};
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, ensure, decl_error,
|
||||
traits::{Currency, ReservableCurrency, OnUnbalanced, Get},
|
||||
traits::{Currency, ReservableCurrency, OnUnbalanced, Get, BalanceStatus},
|
||||
weights::SimpleDispatchInfo,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
@@ -823,7 +823,7 @@ decl_module! {
|
||||
match id.judgements.binary_search_by_key(®_index, |x| x.0) {
|
||||
Ok(position) => {
|
||||
if let Judgement::FeePaid(fee) = id.judgements[position].1 {
|
||||
let _ = T::Currency::repatriate_reserved(&target, &sender, fee);
|
||||
let _ = T::Currency::repatriate_reserved(&target, &sender, fee, BalanceStatus::Free);
|
||||
}
|
||||
id.judgements[position] = item
|
||||
}
|
||||
@@ -924,20 +924,19 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const BasicDeposit: u64 = 10;
|
||||
|
||||
@@ -115,6 +115,9 @@ impl frame_system::Trait for Runtime {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
|
||||
@@ -16,6 +16,9 @@ sp-core = { version = "2.0.0", default-features = false, path = "../../primitive
|
||||
frame-support = { version = "2.0.0", default-features = false, path = "../support" }
|
||||
frame-system = { version = "2.0.0", default-features = false, path = "../system" }
|
||||
|
||||
[dev-dependencies]
|
||||
pallet-balances = { version = "2.0.0", path = "../balances" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
|
||||
+181
-142
@@ -19,41 +19,24 @@
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use sp_std::{prelude::*, marker::PhantomData, convert::TryInto};
|
||||
use codec::{Encode, Codec};
|
||||
use frame_support::{Parameter, decl_module, decl_event, decl_storage};
|
||||
use sp_runtime::traits::{One, AtLeast32Bit, StaticLookup, Member, LookupError};
|
||||
use frame_system::{IsDeadAccount, OnNewAccount};
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use codec::Codec;
|
||||
use sp_runtime::traits::{
|
||||
StaticLookup, Member, LookupError, Zero, One, BlakeTwo256, Hash, Saturating, AtLeast32Bit
|
||||
};
|
||||
use frame_support::{Parameter, decl_module, decl_error, decl_event, decl_storage, ensure};
|
||||
use frame_support::dispatch::DispatchResult;
|
||||
use frame_support::traits::{Currency, ReservableCurrency, Get, BalanceStatus::Reserved};
|
||||
use frame_support::storage::migration::take_storage_value;
|
||||
use frame_system::{ensure_signed, ensure_root};
|
||||
use self::address::Address as RawAddress;
|
||||
|
||||
mod mock;
|
||||
|
||||
pub mod address;
|
||||
mod tests;
|
||||
|
||||
/// Number of account IDs stored per enum set.
|
||||
const ENUM_SET_SIZE: u32 = 64;
|
||||
|
||||
pub type Address<T> = RawAddress<<T as frame_system::Trait>::AccountId, <T as Trait>::AccountIndex>;
|
||||
|
||||
/// Turn an Id into an Index, or None for the purpose of getting
|
||||
/// a hint at a possibly desired index.
|
||||
pub trait ResolveHint<AccountId, AccountIndex> {
|
||||
/// Turn an Id into an Index, or None for the purpose of getting
|
||||
/// a hint at a possibly desired index.
|
||||
fn resolve_hint(who: &AccountId) -> Option<AccountIndex>;
|
||||
}
|
||||
|
||||
/// Simple encode-based resolve hint implementation.
|
||||
pub struct SimpleResolveHint<AccountId, AccountIndex>(PhantomData<(AccountId, AccountIndex)>);
|
||||
impl<AccountId: Encode, AccountIndex: From<u32>>
|
||||
ResolveHint<AccountId, AccountIndex> for SimpleResolveHint<AccountId, AccountIndex>
|
||||
{
|
||||
fn resolve_hint(who: &AccountId) -> Option<AccountIndex> {
|
||||
Some(AccountIndex::from(who.using_encoded(|e| e[0] as u32 + e[1] as u32 * 256)))
|
||||
}
|
||||
}
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
||||
|
||||
/// The module's config trait.
|
||||
pub trait Trait: frame_system::Trait {
|
||||
@@ -61,19 +44,28 @@ pub trait Trait: frame_system::Trait {
|
||||
/// can hold.
|
||||
type AccountIndex: Parameter + Member + Codec + Default + AtLeast32Bit + Copy;
|
||||
|
||||
/// Whether an account is dead or not.
|
||||
type IsDeadAccount: IsDeadAccount<Self::AccountId>;
|
||||
/// The currency trait.
|
||||
type Currency: ReservableCurrency<Self::AccountId>;
|
||||
|
||||
/// How to turn an id into an index.
|
||||
type ResolveHint: ResolveHint<Self::AccountId, Self::AccountIndex>;
|
||||
/// The deposit needed for reserving an index.
|
||||
type Deposit: Get<BalanceOf<Self>>;
|
||||
|
||||
/// The overarching event type.
|
||||
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin, system = frame_system {
|
||||
fn deposit_event() = default;
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Indices {
|
||||
/// The lookup from index to account.
|
||||
pub Accounts build(|config: &GenesisConfig<T>|
|
||||
config.indices.iter()
|
||||
.cloned()
|
||||
.map(|(a, b)| (a, (b, Zero::zero())))
|
||||
.collect::<Vec<_>>()
|
||||
): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf<T>)>;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(indices): Vec<(T::AccountIndex, T::AccountId)>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,36 +74,146 @@ decl_event!(
|
||||
<T as frame_system::Trait>::AccountId,
|
||||
<T as Trait>::AccountIndex
|
||||
{
|
||||
/// A new account index was assigned.
|
||||
///
|
||||
/// This event is not triggered when an existing index is reassigned
|
||||
/// to another `AccountId`.
|
||||
NewAccountIndex(AccountId, AccountIndex),
|
||||
/// A account index was assigned.
|
||||
IndexAssigned(AccountId, AccountIndex),
|
||||
/// A account index has been freed up (unassigned).
|
||||
IndexFreed(AccountIndex),
|
||||
}
|
||||
);
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Indices {
|
||||
/// The next free enumeration set.
|
||||
pub NextEnumSet get(fn next_enum_set) build(|config: &GenesisConfig<T>| {
|
||||
(config.ids.len() as u32 / ENUM_SET_SIZE).into()
|
||||
}): T::AccountIndex;
|
||||
|
||||
/// The enumeration sets.
|
||||
pub EnumSet get(fn enum_set) build(|config: &GenesisConfig<T>| {
|
||||
(0..((config.ids.len() as u32) + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE)
|
||||
.map(|i| (
|
||||
i.into(),
|
||||
config.ids[
|
||||
(i * ENUM_SET_SIZE) as usize..
|
||||
config.ids.len().min(((i + 1) * ENUM_SET_SIZE) as usize)
|
||||
].to_owned(),
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
}): map hasher(blake2_256) T::AccountIndex => Vec<T::AccountId>;
|
||||
decl_error! {
|
||||
pub enum Error for Module<T: Trait> {
|
||||
/// The index was not already assigned.
|
||||
NotAssigned,
|
||||
/// The index is assigned to another account.
|
||||
NotOwner,
|
||||
/// The index was not available.
|
||||
InUse,
|
||||
/// The source and destination accounts are identical.
|
||||
NotTransfer,
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(ids): Vec<T::AccountId>;
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin, system = frame_system {
|
||||
fn deposit_event() = default;
|
||||
|
||||
fn on_initialize() {
|
||||
Self::migrations();
|
||||
}
|
||||
|
||||
/// Assign an previously unassigned index.
|
||||
///
|
||||
/// Payment: `Deposit` is reserved from the sender account.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Signed_.
|
||||
///
|
||||
/// - `index`: the index to be claimed. This must not be in use.
|
||||
///
|
||||
/// Emits `IndexAssigned` if successful.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - `O(1)`.
|
||||
/// - One storage mutation (codec `O(1)`).
|
||||
/// - One reserve operation.
|
||||
/// - One event.
|
||||
/// # </weight>
|
||||
fn claim(origin, index: T::AccountIndex) {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
Accounts::<T>::try_mutate(index, |maybe_value| {
|
||||
ensure!(maybe_value.is_none(), Error::<T>::InUse);
|
||||
*maybe_value = Some((who.clone(), T::Deposit::get()));
|
||||
T::Currency::reserve(&who, T::Deposit::get())
|
||||
})?;
|
||||
Self::deposit_event(RawEvent::IndexAssigned(who, index));
|
||||
}
|
||||
|
||||
/// Assign an index already owned by the sender to another account. The balance reservation
|
||||
/// is effectively transfered to the new account.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Signed_.
|
||||
///
|
||||
/// - `index`: the index to be re-assigned. This must be owned by the sender.
|
||||
/// - `new`: the new owner of the index. This function is a no-op if it is equal to sender.
|
||||
///
|
||||
/// Emits `IndexAssigned` if successful.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - `O(1)`.
|
||||
/// - One storage mutation (codec `O(1)`).
|
||||
/// - One transfer operation.
|
||||
/// - One event.
|
||||
/// # </weight>
|
||||
fn transfer(origin, new: T::AccountId, index: T::AccountIndex) {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(who != new, Error::<T>::NotTransfer);
|
||||
|
||||
Accounts::<T>::try_mutate(index, |maybe_value| -> DispatchResult {
|
||||
let (account, amount) = maybe_value.take().ok_or(Error::<T>::NotAssigned)?;
|
||||
ensure!(&account == &who, Error::<T>::NotOwner);
|
||||
let lost = T::Currency::repatriate_reserved(&who, &new, amount, Reserved)?;
|
||||
*maybe_value = Some((new.clone(), amount.saturating_sub(lost)));
|
||||
Ok(())
|
||||
})?;
|
||||
Self::deposit_event(RawEvent::IndexAssigned(new, index));
|
||||
}
|
||||
|
||||
/// Free up an index owned by the sender.
|
||||
///
|
||||
/// Payment: Any previous deposit placed for the index is unreserved in the sender account.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Signed_ and the sender must own the index.
|
||||
///
|
||||
/// - `index`: the index to be freed. This must be owned by the sender.
|
||||
///
|
||||
/// Emits `IndexFreed` if successful.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - `O(1)`.
|
||||
/// - One storage mutation (codec `O(1)`).
|
||||
/// - One reserve operation.
|
||||
/// - One event.
|
||||
/// # </weight>
|
||||
fn free(origin, index: T::AccountIndex) {
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
Accounts::<T>::try_mutate(index, |maybe_value| -> DispatchResult {
|
||||
let (account, amount) = maybe_value.take().ok_or(Error::<T>::NotAssigned)?;
|
||||
ensure!(&account == &who, Error::<T>::NotOwner);
|
||||
T::Currency::unreserve(&who, amount);
|
||||
Ok(())
|
||||
})?;
|
||||
Self::deposit_event(RawEvent::IndexFreed(index));
|
||||
}
|
||||
|
||||
/// Force an index to an account. This doesn't require a deposit. If the index is already
|
||||
/// held, then any deposit is reimbursed to its current owner.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Root_.
|
||||
///
|
||||
/// - `index`: the index to be (re-)assigned.
|
||||
/// - `new`: the new owner of the index. This function is a no-op if it is equal to sender.
|
||||
///
|
||||
/// Emits `IndexAssigned` if successful.
|
||||
///
|
||||
/// # <weight>
|
||||
/// - `O(1)`.
|
||||
/// - One storage mutation (codec `O(1)`).
|
||||
/// - Up to one reserve operation.
|
||||
/// - One event.
|
||||
/// # </weight>
|
||||
fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex) {
|
||||
ensure_root(origin)?;
|
||||
|
||||
Accounts::<T>::mutate(index, |maybe_value| {
|
||||
if let Some((account, amount)) = maybe_value.take() {
|
||||
T::Currency::unreserve(&account, amount);
|
||||
}
|
||||
*maybe_value = Some((new.clone(), Zero::zero()));
|
||||
});
|
||||
Self::deposit_event(RawEvent::IndexAssigned(new, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,22 +222,7 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Lookup an T::AccountIndex to get an Id, if there's one there.
|
||||
pub fn lookup_index(index: T::AccountIndex) -> Option<T::AccountId> {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let set = Self::enum_set(index / enum_set_size);
|
||||
let i: usize = (index % enum_set_size).try_into().ok()?;
|
||||
set.get(i).cloned()
|
||||
}
|
||||
|
||||
/// `true` if the account `index` is ready for reclaim.
|
||||
pub fn can_reclaim(try_index: T::AccountIndex) -> bool {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let try_set = Self::enum_set(try_index / enum_set_size);
|
||||
let maybe_usize: Result<usize, _> = (try_index % enum_set_size).try_into();
|
||||
if let Ok(i) = maybe_usize {
|
||||
i < try_set.len() && T::IsDeadAccount::is_dead_account(&try_set[i])
|
||||
} else {
|
||||
false
|
||||
}
|
||||
Accounts::<T>::get(index).map(|x| x.0)
|
||||
}
|
||||
|
||||
/// Lookup an address to get an Id, if there's one there.
|
||||
@@ -148,76 +235,28 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC MUTABLES (DANGEROUS)
|
||||
/// Do any migrations.
|
||||
fn migrations() {
|
||||
if let Some(set_count) = take_storage_value::<T::AccountIndex>(b"Indices", b"NextEnumSet", b"") {
|
||||
// migrations need doing.
|
||||
let set_size: T::AccountIndex = 64.into();
|
||||
|
||||
fn enum_set_size() -> T::AccountIndex {
|
||||
ENUM_SET_SIZE.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> OnNewAccount<T::AccountId> for Module<T> {
|
||||
// Implementation of the config type managing the creation of new accounts.
|
||||
// See Balances module for a concrete example.
|
||||
//
|
||||
// # <weight>
|
||||
// - Independent of the arguments.
|
||||
// - Given the correct value of `Self::next_enum_set`, it always has a limited
|
||||
// number of reads and writes and no complex computation.
|
||||
//
|
||||
// As for storage, calling this function with _non-dead-indices_ will linearly grow the length of
|
||||
// of `Self::enum_set`. Appropriate economic incentives should exist to make callers of this
|
||||
// function provide a `who` argument that reclaims a dead account.
|
||||
//
|
||||
// At the time of this writing, only the Balances module calls this function upon creation
|
||||
// of new accounts.
|
||||
// # </weight>
|
||||
fn on_new_account(who: &T::AccountId) {
|
||||
let enum_set_size = Self::enum_set_size();
|
||||
let next_set_index = Self::next_enum_set();
|
||||
|
||||
if let Some(try_index) = T::ResolveHint::resolve_hint(who) {
|
||||
// then check to see if this account id identifies a dead account index.
|
||||
let set_index = try_index / enum_set_size;
|
||||
let mut try_set = Self::enum_set(set_index);
|
||||
if let Ok(item_index) = (try_index % enum_set_size).try_into() {
|
||||
if item_index < try_set.len() {
|
||||
if T::IsDeadAccount::is_dead_account(&try_set[item_index]) {
|
||||
// yup - this index refers to a dead account. can be reused.
|
||||
try_set[item_index] = who.clone();
|
||||
<EnumSet<T>>::insert(set_index, try_set);
|
||||
|
||||
return
|
||||
let mut set_index: T::AccountIndex = Zero::zero();
|
||||
while set_index < set_count {
|
||||
let maybe_accounts = take_storage_value::<Vec<T::AccountId>>(b"Indices", b"EnumSet", BlakeTwo256::hash_of(&set_index).as_ref());
|
||||
if let Some(accounts) = maybe_accounts {
|
||||
for (item_index, target) in accounts.into_iter().enumerate() {
|
||||
if target != T::AccountId::default() && !T::Currency::total_balance(&target).is_zero() {
|
||||
let index = set_index * set_size + T::AccountIndex::from(item_index as u32);
|
||||
Accounts::<T>::insert(index, (target, BalanceOf::<T>::zero()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
set_index += One::one();
|
||||
}
|
||||
}
|
||||
|
||||
// insert normally as a back up
|
||||
let mut set_index = next_set_index;
|
||||
// defensive only: this loop should never iterate since we keep NextEnumSet up to date
|
||||
// later.
|
||||
let mut set = loop {
|
||||
let set = Self::enum_set(set_index);
|
||||
if set.len() < ENUM_SET_SIZE as usize {
|
||||
break set;
|
||||
}
|
||||
set_index += One::one();
|
||||
};
|
||||
|
||||
let index = set_index * enum_set_size + T::AccountIndex::from(set.len() as u32);
|
||||
|
||||
// update set.
|
||||
set.push(who.clone());
|
||||
|
||||
// keep NextEnumSet up to date
|
||||
if set.len() == ENUM_SET_SIZE as usize {
|
||||
<NextEnumSet<T>>::put(set_index + One::one());
|
||||
}
|
||||
|
||||
// write set.
|
||||
<EnumSet<T>>::insert(set_index, set);
|
||||
|
||||
Self::deposit_event(RawEvent::NewAccountIndex(who.clone(), index));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,51 +18,29 @@
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use std::{cell::RefCell, collections::HashSet};
|
||||
use sp_runtime::testing::Header;
|
||||
use sp_runtime::Perbill;
|
||||
use sp_core::H256;
|
||||
use frame_support::{impl_outer_origin, parameter_types, weights::Weight};
|
||||
use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint};
|
||||
use frame_support::{impl_outer_origin, impl_outer_event, parameter_types, weights::Weight};
|
||||
use crate::{self as indices, Module, Trait};
|
||||
use frame_system as system;
|
||||
use pallet_balances as balances;
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Runtime where system = frame_system {}
|
||||
pub enum Origin for Test where system = frame_system {}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static ALIVE: RefCell<HashSet<u64>> = Default::default();
|
||||
}
|
||||
|
||||
pub fn make_account(who: u64) {
|
||||
ALIVE.with(|a| a.borrow_mut().insert(who));
|
||||
Indices::on_new_account(&who);
|
||||
}
|
||||
|
||||
pub fn kill_account(who: u64) {
|
||||
ALIVE.with(|a| a.borrow_mut().remove(&who));
|
||||
}
|
||||
|
||||
pub struct TestIsDeadAccount {}
|
||||
impl IsDeadAccount<u64> for TestIsDeadAccount {
|
||||
fn is_dead_account(who: &u64) -> bool {
|
||||
!ALIVE.with(|a| a.borrow_mut().contains(who))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestResolveHint;
|
||||
impl ResolveHint<u64, u64> for TestResolveHint {
|
||||
fn resolve_hint(who: &u64) -> Option<u64> {
|
||||
if *who < 256 {
|
||||
None
|
||||
} else {
|
||||
Some(*who - 256)
|
||||
}
|
||||
impl_outer_event!{
|
||||
pub enum MetaEvent for Test {
|
||||
system<T>,
|
||||
balances<T>,
|
||||
indices<T>,
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Runtime;
|
||||
pub struct Test;
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const MaximumBlockWeight: Weight = 1024;
|
||||
@@ -70,46 +48,59 @@ parameter_types! {
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
}
|
||||
|
||||
impl frame_system::Trait for Runtime {
|
||||
impl frame_system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Call = ();
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Call = ();
|
||||
type Hash = H256;
|
||||
type Hashing = ::sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = Indices;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
type Event = MetaEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type MaximumBlockWeight = MaximumBlockWeight;
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
|
||||
impl Trait for Runtime {
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type DustRemoval = ();
|
||||
type Event = MetaEvent;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const Deposit: u64 = 1;
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
type AccountIndex = u64;
|
||||
type IsDeadAccount = TestIsDeadAccount;
|
||||
type ResolveHint = TestResolveHint;
|
||||
type Event = ();
|
||||
type Currency = Balances;
|
||||
type Deposit = Deposit;
|
||||
type Event = MetaEvent;
|
||||
}
|
||||
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
{
|
||||
ALIVE.with(|a| {
|
||||
let mut h = a.borrow_mut();
|
||||
h.clear();
|
||||
for i in 1..5 { h.insert(i); }
|
||||
});
|
||||
}
|
||||
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
|
||||
GenesisConfig::<Runtime> {
|
||||
ids: vec![1, 2, 3, 4]
|
||||
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||
pallet_balances::GenesisConfig::<Test>{
|
||||
balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
|
||||
}.assimilate_storage(&mut t).unwrap();
|
||||
t.into()
|
||||
}
|
||||
|
||||
pub type Indices = Module<Runtime>;
|
||||
pub type System = frame_system::Module<Test>;
|
||||
pub type Balances = pallet_balances::Module<Test>;
|
||||
pub type Indices = Module<Test>;
|
||||
|
||||
@@ -19,49 +19,85 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use super::*;
|
||||
use crate::mock::{Indices, new_test_ext, make_account, kill_account, TestIsDeadAccount};
|
||||
use super::mock::*;
|
||||
use frame_support::{assert_ok, assert_noop};
|
||||
use pallet_balances::Error as BalancesError;
|
||||
|
||||
#[test]
|
||||
fn claiming_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_noop!(Indices::claim(Some(0).into(), 0), BalancesError::<Test, _>::InsufficientBalance);
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_noop!(Indices::claim(Some(2).into(), 0), Error::<Test>::InUse);
|
||||
assert_eq!(Balances::reserved_balance(1), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freeing_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_ok!(Indices::claim(Some(2).into(), 1));
|
||||
assert_noop!(Indices::free(Some(0).into(), 0), Error::<Test>::NotOwner);
|
||||
assert_noop!(Indices::free(Some(1).into(), 1), Error::<Test>::NotOwner);
|
||||
assert_noop!(Indices::free(Some(1).into(), 2), Error::<Test>::NotAssigned);
|
||||
assert_ok!(Indices::free(Some(1).into(), 0));
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
assert_noop!(Indices::free(Some(1).into(), 0), Error::<Test>::NotAssigned);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexing_lookup_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_ok!(Indices::claim(Some(2).into(), 1));
|
||||
assert_eq!(Indices::lookup_index(0), Some(1));
|
||||
assert_eq!(Indices::lookup_index(1), Some(2));
|
||||
assert_eq!(Indices::lookup_index(2), Some(3));
|
||||
assert_eq!(Indices::lookup_index(3), Some(4));
|
||||
assert_eq!(Indices::lookup_index(4), None);
|
||||
assert_eq!(Indices::lookup_index(2), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_indexing_on_new_accounts_should_work() {
|
||||
fn reclaim_index_on_accounts_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Indices::lookup_index(4), None);
|
||||
make_account(5);
|
||||
assert_eq!(Indices::lookup_index(4), Some(5));
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_ok!(Indices::free(Some(1).into(), 0));
|
||||
assert_ok!(Indices::claim(Some(2).into(), 0));
|
||||
assert_eq!(Indices::lookup_index(0), Some(2));
|
||||
assert_eq!(Balances::reserved_balance(2), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reclaim_indexing_on_new_accounts_should_work() {
|
||||
fn transfer_index_on_accounts_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Indices::lookup_index(1), Some(2));
|
||||
assert_eq!(Indices::lookup_index(4), None);
|
||||
|
||||
kill_account(2); // index 1 no longer locked to id 2
|
||||
|
||||
make_account(1 + 256); // id 257 takes index 1.
|
||||
assert_eq!(Indices::lookup_index(1), Some(257));
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_noop!(Indices::transfer(Some(1).into(), 2, 1), Error::<Test>::NotAssigned);
|
||||
assert_noop!(Indices::transfer(Some(2).into(), 3, 0), Error::<Test>::NotOwner);
|
||||
assert_ok!(Indices::transfer(Some(1).into(), 3, 0));
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
assert_eq!(Balances::reserved_balance(3), 1);
|
||||
assert_eq!(Indices::lookup_index(0), Some(3));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alive_account_should_prevent_reclaim() {
|
||||
fn force_transfer_index_on_preowned_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert!(!TestIsDeadAccount::is_dead_account(&2));
|
||||
assert_eq!(Indices::lookup_index(1), Some(2));
|
||||
assert_eq!(Indices::lookup_index(4), None);
|
||||
|
||||
make_account(1 + 256); // id 257 takes index 1.
|
||||
assert_eq!(Indices::lookup_index(4), Some(257));
|
||||
assert_ok!(Indices::claim(Some(1).into(), 0));
|
||||
assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0));
|
||||
assert_eq!(Balances::reserved_balance(1), 0);
|
||||
assert_eq!(Balances::reserved_balance(3), 0);
|
||||
assert_eq!(Indices::lookup_index(0), Some(3));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn force_transfer_index_on_free_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0));
|
||||
assert_eq!(Balances::reserved_balance(3), 0);
|
||||
assert_eq!(Indices::lookup_index(0), Some(3));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -269,6 +269,9 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
ord_parameter_types! {
|
||||
pub const One: u64 = 1;
|
||||
|
||||
@@ -285,20 +285,19 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ReservationFee: u64 = 2;
|
||||
|
||||
@@ -89,6 +89,9 @@ impl frame_system::Trait for Runtime {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl Trait for Runtime {
|
||||
@@ -103,6 +106,7 @@ mod offences {
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for Runtime {
|
||||
system<T>,
|
||||
offences,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
type System = frame_system::Module<Test>;
|
||||
|
||||
@@ -164,7 +164,7 @@ use frame_support::{
|
||||
GetDispatchInfo, PaysFee, DispatchClass, ClassifyDispatch, Weight, WeighData,
|
||||
SimpleDispatchInfo,
|
||||
},
|
||||
traits::{Currency, ReservableCurrency, Get, OnReapAccount},
|
||||
traits::{Currency, ReservableCurrency, Get, OnReapAccount, BalanceStatus},
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
@@ -587,7 +587,7 @@ decl_module! {
|
||||
let active_recovery = <ActiveRecoveries<T>>::take(&who, &rescuer).ok_or(Error::<T>::NotStarted)?;
|
||||
// Move the reserved funds from the rescuer to the rescued account.
|
||||
// Acts like a slashing mechanism for those who try to maliciously recover accounts.
|
||||
let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit);
|
||||
let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit, BalanceStatus::Free);
|
||||
Self::deposit_event(RawEvent::RecoveryClosed(who, rescuer));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ impl_outer_origin! {
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for Test {
|
||||
system<T>,
|
||||
pallet_balances<T>,
|
||||
recovery<T>,
|
||||
}
|
||||
@@ -62,10 +63,10 @@ parameter_types! {
|
||||
|
||||
impl frame_system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Call = Call;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Call = Call;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
@@ -77,22 +78,21 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = (Balances, Recovery);
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u128;
|
||||
type OnReapAccount = (System, Recovery);
|
||||
type OnNewAccount = ();
|
||||
type Event = TestEvent;
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type Event = TestEvent;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
|
||||
@@ -47,7 +47,6 @@ parameter_types! {
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
ord_parameter_types! {
|
||||
pub const KickOrigin: u64 = 2;
|
||||
@@ -71,17 +70,17 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
|
||||
@@ -176,6 +176,9 @@ impl frame_system::Trait for Test {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Session;
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Trait for Test {
|
||||
|
||||
@@ -262,7 +262,7 @@ use sp_runtime::{Percent, ModuleId, RuntimeDebug,
|
||||
use frame_support::{decl_error, decl_module, decl_storage, decl_event, ensure, dispatch::DispatchResult};
|
||||
use frame_support::weights::SimpleDispatchInfo;
|
||||
use frame_support::traits::{
|
||||
Currency, ReservableCurrency, Randomness, Get, ChangeMembers,
|
||||
Currency, ReservableCurrency, Randomness, Get, ChangeMembers, BalanceStatus,
|
||||
ExistenceRequirement::AllowDeath,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
@@ -984,7 +984,7 @@ decl_module! {
|
||||
match kind {
|
||||
BidKind::Deposit(deposit) => {
|
||||
// Slash deposit and move it to the society account
|
||||
let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit);
|
||||
let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit, BalanceStatus::Free);
|
||||
}
|
||||
BidKind::Vouch(voucher, _) => {
|
||||
// Ban the voucher from vouching again
|
||||
|
||||
@@ -53,7 +53,6 @@ parameter_types! {
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
|
||||
ord_parameter_types! {
|
||||
@@ -78,17 +77,17 @@ impl frame_system::Trait for Test {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type OnReapAccount = System;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
|
||||
@@ -250,7 +250,6 @@
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod migration;
|
||||
mod slashing;
|
||||
|
||||
pub mod inflation;
|
||||
@@ -761,9 +760,6 @@ decl_storage! {
|
||||
|
||||
/// The earliest era for which we have a pending, unapplied slash.
|
||||
EarliestUnappliedSlash: Option<EraIndex>;
|
||||
|
||||
/// The version of storage for upgrade.
|
||||
StorageVersion: u32;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(stakers):
|
||||
@@ -795,8 +791,6 @@ decl_storage! {
|
||||
}, _ => Ok(())
|
||||
};
|
||||
}
|
||||
|
||||
StorageVersion::put(migration::CURRENT_VERSION);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1298,9 +1292,10 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Ensures storage is upgraded to most recent necessary state.
|
||||
fn ensure_storage_upgraded() {
|
||||
migration::perform_migrations::<T>();
|
||||
}
|
||||
///
|
||||
/// Right now it's a no-op as all networks that are supported by Substrate Frame Core are
|
||||
/// running with the latest staking storage scheme.
|
||||
fn ensure_storage_upgraded() {}
|
||||
|
||||
/// Actually make a payment to a staker. This uses the currency's reward function
|
||||
/// to pay the right payee for the given staker account.
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
// Copyright 2019-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/>.
|
||||
|
||||
//! Storage migrations for srml-staking.
|
||||
|
||||
/// Indicator of a version of a storage layout.
|
||||
pub type VersionNumber = u32;
|
||||
|
||||
// the current expected version of the storage
|
||||
pub const CURRENT_VERSION: VersionNumber = 2;
|
||||
|
||||
/// The inner logic of migrations.
|
||||
#[cfg(any(test, feature = "migrate"))]
|
||||
pub mod inner {
|
||||
use crate::{Store, Module, Trait};
|
||||
use frame_support::{StorageLinkedMap, StoragePrefixedMap, StorageValue};
|
||||
use codec::{Encode, Decode};
|
||||
use sp_std::vec::Vec;
|
||||
use super::{CURRENT_VERSION, VersionNumber};
|
||||
|
||||
// the minimum supported version of the migration logic.
|
||||
const MIN_SUPPORTED_VERSION: VersionNumber = 0;
|
||||
|
||||
// migrate storage from v0 to v1.
|
||||
//
|
||||
// this upgrades the `Nominators` linked_map value type from `Vec<T::AccountId>` to
|
||||
// `Option<Nominations<T::AccountId>>`
|
||||
pub fn to_v1<T: Trait>(version: &mut VersionNumber) {
|
||||
if *version != 0 { return }
|
||||
*version += 1;
|
||||
|
||||
let now = <Module<T>>::current_era();
|
||||
let res = <Module<T> as Store>::Nominators::translate::<T::AccountId, Vec<T::AccountId>, _, _>(
|
||||
|key| key,
|
||||
|targets| crate::Nominations {
|
||||
targets,
|
||||
submitted_in: now,
|
||||
suppressed: false,
|
||||
},
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
frame_support::print("Encountered error in migration of Staking::Nominators map.");
|
||||
if e.is_none() {
|
||||
frame_support::print("Staking::Nominators map reinitialized");
|
||||
}
|
||||
}
|
||||
|
||||
frame_support::print("Finished migrating Staking storage to v1.");
|
||||
}
|
||||
|
||||
// migrate storage from v1 to v2: adds another field to the `SlashingSpans`
|
||||
// struct.
|
||||
pub fn to_v2<T: Trait>(version: &mut VersionNumber) {
|
||||
use crate::{EraIndex, slashing::SpanIndex};
|
||||
#[derive(Decode)]
|
||||
struct V1SlashingSpans {
|
||||
span_index: SpanIndex,
|
||||
last_start: EraIndex,
|
||||
prior: Vec<EraIndex>,
|
||||
}
|
||||
|
||||
#[derive(Encode)]
|
||||
struct V2SlashingSpans {
|
||||
span_index: SpanIndex,
|
||||
last_start: EraIndex,
|
||||
last_nonzero_slash: EraIndex,
|
||||
prior: Vec<EraIndex>,
|
||||
}
|
||||
|
||||
if *version != 1 { return }
|
||||
*version += 1;
|
||||
|
||||
let prefix = <Module<T> as Store>::SlashingSpans::final_prefix();
|
||||
let mut current_key = prefix.to_vec();
|
||||
loop {
|
||||
let maybe_next_key = sp_io::storage::next_key(¤t_key[..])
|
||||
.filter(|v| v.starts_with(&prefix[..]));
|
||||
|
||||
match maybe_next_key {
|
||||
Some(next_key) => {
|
||||
let maybe_spans = sp_io::storage::get(&next_key[..])
|
||||
.and_then(|v| V1SlashingSpans::decode(&mut &v[..]).ok());
|
||||
if let Some(spans) = maybe_spans {
|
||||
let new_val = V2SlashingSpans {
|
||||
span_index: spans.span_index,
|
||||
last_start: spans.last_start,
|
||||
last_nonzero_slash: spans.last_start,
|
||||
prior: spans.prior,
|
||||
}.encode();
|
||||
|
||||
sp_io::storage::set(&next_key[..], &new_val[..]);
|
||||
}
|
||||
current_key = next_key;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn perform_migrations<T: Trait>() {
|
||||
<Module<T> as Store>::StorageVersion::mutate(|version| {
|
||||
if *version < MIN_SUPPORTED_VERSION {
|
||||
frame_support::print("Cannot migrate staking storage because version is less than\
|
||||
minimum.");
|
||||
frame_support::print(*version);
|
||||
return
|
||||
}
|
||||
|
||||
if *version == CURRENT_VERSION { return }
|
||||
|
||||
to_v1::<T>(version);
|
||||
to_v2::<T>(version);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(test, feature = "migrate")))]
|
||||
mod inner {
|
||||
pub(super) fn perform_migrations<T>() { }
|
||||
}
|
||||
|
||||
/// Perform all necessary storage migrations to get storage into the expected stsate for current
|
||||
/// logic. No-op if fully upgraded.
|
||||
pub(crate) fn perform_migrations<T: crate::Trait>() {
|
||||
inner::perform_migrations::<T>();
|
||||
}
|
||||
@@ -138,19 +138,16 @@ impl frame_system::Trait for Test {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const CreationFee: Balance = 0;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = (Balances, Staking, Session);
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = Balance;
|
||||
type OnReapAccount = (System, Staking);
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const Period: BlockNumber = 1;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
use super::*;
|
||||
use mock::*;
|
||||
use codec::Encode;
|
||||
use sp_runtime::{assert_eq_error_rate, traits::{OnInitialize, BadOrigin}};
|
||||
use sp_staking::offence::OffenceDetails;
|
||||
use frame_support::{
|
||||
@@ -2671,13 +2670,6 @@ fn remove_multi_deferred() {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_initialized() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
assert_eq!(<Staking as Store>::StorageVersion::get(), crate::migration::CURRENT_VERSION);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slash_kicks_validators_not_nominators() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
@@ -2717,56 +2709,6 @@ fn slash_kicks_validators_not_nominators() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migration_v2() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
use crate::{EraIndex, slashing::SpanIndex};
|
||||
|
||||
#[derive(Encode)]
|
||||
struct V1SlashingSpans {
|
||||
span_index: SpanIndex,
|
||||
last_start: EraIndex,
|
||||
prior: Vec<EraIndex>,
|
||||
}
|
||||
|
||||
// inject old-style values directly into storage.
|
||||
let set = |stash, spans: V1SlashingSpans| {
|
||||
let key = <Staking as Store>::SlashingSpans::hashed_key_for(stash);
|
||||
sp_io::storage::set(&key, &spans.encode());
|
||||
};
|
||||
|
||||
let spans_11 = V1SlashingSpans {
|
||||
span_index: 10,
|
||||
last_start: 1,
|
||||
prior: vec![0],
|
||||
};
|
||||
|
||||
let spans_21 = V1SlashingSpans {
|
||||
span_index: 1,
|
||||
last_start: 5,
|
||||
prior: vec![],
|
||||
};
|
||||
|
||||
set(11, spans_11);
|
||||
set(21, spans_21);
|
||||
|
||||
<Staking as Store>::StorageVersion::put(1);
|
||||
|
||||
// perform migration.
|
||||
crate::migration::inner::to_v2::<Test>(&mut 1);
|
||||
|
||||
assert_eq!(
|
||||
<Staking as Store>::SlashingSpans::get(&11).unwrap().last_nonzero_slash(),
|
||||
1,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
<Staking as Store>::SlashingSpans::get(&21).unwrap().last_nonzero_slash(),
|
||||
5,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_slash_keeps_nominators() {
|
||||
ExtBuilder::default().build().execute_with(|| {
|
||||
|
||||
@@ -20,7 +20,7 @@ use frame_support_procedural_tools::syn_ext as ext;
|
||||
use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes};
|
||||
use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{Ident, Result, TypePath};
|
||||
|
||||
@@ -58,7 +58,7 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream
|
||||
return Err(syn::Error::new(
|
||||
modules_token.span,
|
||||
"`System` module declaration is missing. \
|
||||
Please add this line: `System: system::{Module, Call, Storage, Config, Event},`",
|
||||
Please add this line: `System: system::{Module, Call, Storage, Config, Event<T>},`",
|
||||
))
|
||||
}
|
||||
};
|
||||
@@ -68,19 +68,17 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream
|
||||
|
||||
let all_but_system_modules = modules.iter().filter(|module| module.name != SYSTEM_MODULE_NAME);
|
||||
|
||||
let outer_event = decl_outer_event_or_origin(
|
||||
let outer_event = decl_outer_event(
|
||||
&name,
|
||||
all_but_system_modules.clone(),
|
||||
&system_module,
|
||||
modules.iter(),
|
||||
&scrate,
|
||||
DeclOuterKind::Event,
|
||||
)?;
|
||||
let outer_origin = decl_outer_event_or_origin(
|
||||
|
||||
let outer_origin = decl_outer_origin(
|
||||
&name,
|
||||
all_but_system_modules.clone(),
|
||||
&system_module,
|
||||
&scrate,
|
||||
DeclOuterKind::Origin,
|
||||
)?;
|
||||
let all_modules = decl_all_modules(&name, modules.iter());
|
||||
let module_to_index = decl_module_to_index(modules.iter(), modules.len(), &scrate);
|
||||
@@ -264,32 +262,24 @@ fn decl_outer_dispatch<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum DeclOuterKind {
|
||||
Event,
|
||||
Origin,
|
||||
}
|
||||
|
||||
fn decl_outer_event_or_origin<'a>(
|
||||
fn decl_outer_origin<'a>(
|
||||
runtime_name: &'a Ident,
|
||||
module_declarations: impl Iterator<Item = &'a ModuleDeclaration>,
|
||||
system_name: &'a Ident,
|
||||
scrate: &'a TokenStream2,
|
||||
kind: DeclOuterKind,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let mut modules_tokens = TokenStream2::new();
|
||||
let kind_str = format!("{:?}", kind);
|
||||
for module_declaration in module_declarations {
|
||||
match module_declaration.find_part(&kind_str) {
|
||||
match module_declaration.find_part("Origin") {
|
||||
Some(module_entry) => {
|
||||
let module = &module_declaration.module;
|
||||
let instance = module_declaration.instance.as_ref();
|
||||
let generics = &module_entry.generics;
|
||||
if instance.is_some() && generics.params.len() == 0 {
|
||||
let msg = format!(
|
||||
"Instantiable module with no generic `{}` cannot \
|
||||
be constructed: module `{}` must have generic `{}`",
|
||||
kind_str, module_declaration.name, kind_str
|
||||
"Instantiable module with no generic `Origin` cannot \
|
||||
be constructed: module `{}` must have generic `Origin`",
|
||||
module_declaration.name
|
||||
);
|
||||
return Err(syn::Error::new(module_declaration.name.span(), msg));
|
||||
}
|
||||
@@ -299,14 +289,46 @@ fn decl_outer_event_or_origin<'a>(
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
let macro_call = match kind {
|
||||
DeclOuterKind::Event => quote!(#scrate::impl_outer_event!),
|
||||
DeclOuterKind::Origin => quote!(#scrate::impl_outer_origin!),
|
||||
};
|
||||
let enum_name = Ident::new(kind_str.as_str(), Span::call_site());
|
||||
|
||||
Ok(quote!(
|
||||
#macro_call {
|
||||
pub enum #enum_name for #runtime_name where system = #system_name {
|
||||
#scrate::impl_outer_origin! {
|
||||
pub enum Origin for #runtime_name where system = #system_name {
|
||||
#modules_tokens
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn decl_outer_event<'a>(
|
||||
runtime_name: &'a Ident,
|
||||
module_declarations: impl Iterator<Item = &'a ModuleDeclaration>,
|
||||
scrate: &'a TokenStream2,
|
||||
) -> syn::Result<TokenStream2> {
|
||||
let mut modules_tokens = TokenStream2::new();
|
||||
for module_declaration in module_declarations {
|
||||
match module_declaration.find_part("Event") {
|
||||
Some(module_entry) => {
|
||||
let module = &module_declaration.module;
|
||||
let instance = module_declaration.instance.as_ref();
|
||||
let generics = &module_entry.generics;
|
||||
if instance.is_some() && generics.params.len() == 0 {
|
||||
let msg = format!(
|
||||
"Instantiable module with no generic `Event` cannot \
|
||||
be constructed: module `{}` must have generic `Event`",
|
||||
module_declaration.name,
|
||||
);
|
||||
return Err(syn::Error::new(module_declaration.name.span(), msg));
|
||||
}
|
||||
let tokens = quote!(#module #instance #generics ,);
|
||||
modules_tokens.extend(tokens);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(quote!(
|
||||
#scrate::impl_outer_event! {
|
||||
pub enum Event for #runtime_name {
|
||||
#modules_tokens
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,30 +337,14 @@ macro_rules! impl_outer_event {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident {
|
||||
$( $rest_event_without_system:tt )*
|
||||
$( $rest_events:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
system;
|
||||
Modules { $( $rest_event_without_system )* };
|
||||
;
|
||||
);
|
||||
};
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub enum $name:ident for $runtime:ident where system = $system:ident {
|
||||
$( $rest_event_with_system:tt )*
|
||||
}
|
||||
) => {
|
||||
$crate::impl_outer_event!(
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_with_system )* };
|
||||
Modules { $( $rest_events )* };
|
||||
;
|
||||
);
|
||||
};
|
||||
@@ -369,7 +353,6 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident<T>,
|
||||
$( $rest_event_generic_instance:tt )*
|
||||
@@ -380,7 +363,6 @@ macro_rules! impl_outer_event {
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_generic_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },;
|
||||
);
|
||||
@@ -390,7 +372,6 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident $instance:ident,
|
||||
$( $rest_event_instance:tt )*
|
||||
@@ -401,7 +382,6 @@ macro_rules! impl_outer_event {
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },;
|
||||
);
|
||||
@@ -411,7 +391,6 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident<T>,
|
||||
$( $rest_event_generic:tt )*
|
||||
@@ -422,7 +401,6 @@ macro_rules! impl_outer_event {
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_generic )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,;
|
||||
);
|
||||
@@ -432,7 +410,6 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {
|
||||
$module:ident,
|
||||
$( $rest_event_no_generic_no_instance:tt )*
|
||||
@@ -443,7 +420,6 @@ macro_rules! impl_outer_event {
|
||||
$( #[$attr] )*;
|
||||
$name;
|
||||
$runtime;
|
||||
$system;
|
||||
Modules { $( $rest_event_no_generic_no_instance )* };
|
||||
$( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,;
|
||||
);
|
||||
@@ -454,7 +430,6 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr:meta])*;
|
||||
$name:ident;
|
||||
$runtime:ident;
|
||||
$system:ident;
|
||||
Modules {};
|
||||
$( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*;
|
||||
) => {
|
||||
@@ -468,18 +443,12 @@ macro_rules! impl_outer_event {
|
||||
$(#[$attr])*
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum $name {
|
||||
system($system::Event),
|
||||
$(
|
||||
[< $module_name $(_ $generic_instance )? >](
|
||||
$module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? >
|
||||
),
|
||||
)*
|
||||
}
|
||||
impl From<$system::Event> for $name {
|
||||
fn from(x: $system::Event) -> Self {
|
||||
$name::system(x)
|
||||
}
|
||||
}
|
||||
$(
|
||||
impl From<$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >> for $name {
|
||||
fn from(x: $module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >) -> Self {
|
||||
@@ -505,7 +474,6 @@ macro_rules! impl_outer_event {
|
||||
$crate::__impl_outer_event_json_metadata!(
|
||||
$runtime;
|
||||
$name;
|
||||
$system;
|
||||
$(
|
||||
$module_name::Event
|
||||
< $( $generic_param )? $(, $module_name::$generic_instance )? >
|
||||
@@ -521,7 +489,6 @@ macro_rules! __impl_outer_event_json_metadata {
|
||||
(
|
||||
$runtime:ident;
|
||||
$event_name:ident;
|
||||
$system:ident;
|
||||
$( $module_name:ident::Event < $( $generic_params:path ),* > $( $instance:ident )?, )*;
|
||||
) => {
|
||||
impl $runtime {
|
||||
@@ -530,22 +497,20 @@ macro_rules! __impl_outer_event_json_metadata {
|
||||
$crate::event::OuterEventMetadata {
|
||||
name: $crate::event::DecodeDifferent::Encode(stringify!($event_name)),
|
||||
events: $crate::event::DecodeDifferent::Encode(&[
|
||||
("system", $crate::event::FnEncode($system::Event::metadata))
|
||||
$(
|
||||
, (
|
||||
(
|
||||
stringify!($module_name),
|
||||
$crate::event::FnEncode(
|
||||
$module_name::Event ::< $( $generic_params ),* > ::metadata
|
||||
)
|
||||
)
|
||||
)*
|
||||
),*
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
$crate::__impl_outer_event_json_metadata! {
|
||||
@DECL_MODULE_EVENT_FNS
|
||||
$system <> ;
|
||||
$( $module_name < $( $generic_params ),* > $( $instance )? ; )*
|
||||
}
|
||||
}
|
||||
@@ -717,6 +682,7 @@ mod tests {
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
system,
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
event_module3,
|
||||
@@ -727,7 +693,8 @@ mod tests {
|
||||
pub struct TestRuntime2;
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEventSystemRenamed for TestRuntime2 where system = system_renamed {
|
||||
pub enum TestEventSystemRenamed for TestRuntime2 {
|
||||
system_renamed,
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
event_module3,
|
||||
|
||||
@@ -404,6 +404,7 @@ mod tests {
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for TestRuntime {
|
||||
system,
|
||||
event_module<T>,
|
||||
event_module2<T>,
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V>
|
||||
}
|
||||
|
||||
fn insert<KeyArg: EncodeLike<K>, ValArg: EncodeLike<V>>(key: KeyArg, val: ValArg) {
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &val.borrow())
|
||||
unhashed::put(Self::storage_map_final_key(key).as_ref(), &val)
|
||||
}
|
||||
|
||||
fn remove<KeyArg: EncodeLike<K>>(key: KeyArg) {
|
||||
@@ -117,12 +117,58 @@ impl<K: FullEncode, V: FullCodec, G: StorageMap<K, V>> storage::StorageMap<K, V>
|
||||
|
||||
let ret = f(&mut val);
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()),
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn mutate_exists<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Option<V>) -> R>(key: KeyArg, f: F) -> R {
|
||||
let final_key = Self::storage_map_final_key(key);
|
||||
let mut val = unhashed::get(final_key.as_ref());
|
||||
|
||||
let ret = f(&mut val);
|
||||
match val {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn try_mutate<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(
|
||||
key: KeyArg,
|
||||
f: F
|
||||
) -> Result<R, E> {
|
||||
let final_key = Self::storage_map_final_key(key);
|
||||
let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref()));
|
||||
|
||||
let ret = f(&mut val);
|
||||
if ret.is_ok() {
|
||||
match G::from_query_to_optional_value(val) {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn try_mutate_exists<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Option<V>) -> Result<R, E>>(
|
||||
key: KeyArg,
|
||||
f: F
|
||||
) -> Result<R, E> {
|
||||
let final_key = Self::storage_map_final_key(key);
|
||||
let mut val = unhashed::get(final_key.as_ref());
|
||||
|
||||
let ret = f(&mut val);
|
||||
if ret.is_ok() {
|
||||
match val {
|
||||
Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()),
|
||||
None => unhashed::kill(final_key.as_ref()),
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query {
|
||||
let key = Self::storage_map_final_key(key);
|
||||
let value = unhashed::take(key.as_ref());
|
||||
|
||||
@@ -66,6 +66,10 @@ impl<T: FullCodec, G: StorageValue<T>> storage::StorageValue<T> for G {
|
||||
G::from_optional_value_to_query(value)
|
||||
}
|
||||
|
||||
fn try_get() -> Result<T, ()> {
|
||||
unhashed::get(&Self::storage_value_final_key()).ok_or(())
|
||||
}
|
||||
|
||||
fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<T>>(f: F) -> Result<Option<T>, ()> {
|
||||
let key = Self::storage_value_final_key();
|
||||
|
||||
|
||||
+15
-1
@@ -18,8 +18,9 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
use frame_support::{StorageHasher, Twox128};
|
||||
use crate::{StorageHasher, Twox128};
|
||||
|
||||
/// Utility to iterate through raw items in storage.
|
||||
pub struct StorageIterator<T> {
|
||||
prefix: [u8; 32],
|
||||
previous_key: Vec<u8>,
|
||||
@@ -28,12 +29,14 @@ pub struct StorageIterator<T> {
|
||||
}
|
||||
|
||||
impl<T> StorageIterator<T> {
|
||||
/// Construct iterator to iterate over map items in `module` for the map called `item`.
|
||||
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() }
|
||||
}
|
||||
/// Mutate this iterator into a draining iterator; items iterated are removed from storage.
|
||||
pub fn drain(mut self) -> Self {
|
||||
self.drain = true;
|
||||
self
|
||||
@@ -67,6 +70,7 @@ impl<T: Decode + Sized> Iterator for StorageIterator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
|
||||
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));
|
||||
@@ -75,6 +79,16 @@ pub fn get_storage_value<T: Decode + Sized>(module: &[u8], item: &[u8], hash: &[
|
||||
frame_support::storage::unhashed::get::<T>(&key)
|
||||
}
|
||||
|
||||
/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`.
|
||||
pub fn take_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::take::<T>(&key)
|
||||
}
|
||||
|
||||
/// Put a particular value into storage by the `module`, the map's `item` name and the key `hash`.
|
||||
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));
|
||||
@@ -25,6 +25,7 @@ pub mod hashed;
|
||||
pub mod child;
|
||||
#[doc(hidden)]
|
||||
pub mod generator;
|
||||
pub mod migration;
|
||||
|
||||
/// A trait for working with macro-generated storage values under the substrate storage API.
|
||||
///
|
||||
@@ -43,6 +44,10 @@ pub trait StorageValue<T: FullCodec> {
|
||||
/// Load the value from the provided storage instance.
|
||||
fn get() -> Self::Query;
|
||||
|
||||
/// Try to get the underlying value from the provided storage instance; `Ok` if it exists,
|
||||
/// `Err` if not.
|
||||
fn try_get() -> Result<T, ()>;
|
||||
|
||||
/// Translate a value from some previous type (`O`) to the current type.
|
||||
///
|
||||
/// `f: F` is the translation function.
|
||||
@@ -143,14 +148,28 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Mutate the item, only if an `Ok` value is returned.
|
||||
fn try_mutate<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Self::Query) -> Result<R, E>>(
|
||||
key: KeyArg,
|
||||
f: F,
|
||||
) -> Result<R, E>;
|
||||
|
||||
/// Mutate the value under a key. Deletes the item if mutated to a `None`.
|
||||
fn mutate_exists<KeyArg: EncodeLike<K>, R, F: FnOnce(&mut Option<V>) -> R>(key: KeyArg, f: F) -> R;
|
||||
|
||||
/// Mutate the item, only if an `Ok` value is returned. Deletes the item if mutated to a `None`.
|
||||
fn try_mutate_exists<KeyArg: EncodeLike<K>, R, E, F: FnOnce(&mut Option<V>) -> Result<R, E>>(
|
||||
key: KeyArg,
|
||||
f: F,
|
||||
) -> Result<R, E>;
|
||||
|
||||
/// Take the value under a key.
|
||||
fn take<KeyArg: EncodeLike<K>>(key: KeyArg) -> Self::Query;
|
||||
|
||||
/// Append the given items to the value in the storage.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str>
|
||||
where
|
||||
fn append<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) -> Result<(), &'static str> where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
@@ -162,8 +181,7 @@ pub trait StorageMap<K: FullEncode, V: FullCodec> {
|
||||
/// old (presumably corrupt) value is replaced with the given `items`.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend`.
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items)
|
||||
where
|
||||
fn append_or_insert<Items, Item, EncodeLikeItem, KeyArg>(key: KeyArg, items: Items) where
|
||||
KeyArg: EncodeLike<K>,
|
||||
Item: Encode,
|
||||
EncodeLikeItem: EncodeLike<Item>,
|
||||
|
||||
@@ -28,6 +28,115 @@ use sp_runtime::{
|
||||
};
|
||||
|
||||
use crate::dispatch::Parameter;
|
||||
use crate::storage::StorageMap;
|
||||
|
||||
/// An abstraction of a value stored within storage, but possibly as part of a larger composite
|
||||
/// item.
|
||||
pub trait StoredMap<K, T> {
|
||||
/// Get the item, or its default if it doesn't yet exist; we make no distinction between the
|
||||
/// two.
|
||||
fn get(k: &K) -> T;
|
||||
/// Get whether the item takes up any storage. If this is `false`, then `get` will certainly
|
||||
/// return the `T::default()`. If `true`, then there is no implication for `get` (i.e. it
|
||||
/// may return any value, including the default).
|
||||
///
|
||||
/// NOTE: This may still be `true`, even after `remove` is called. This is the case where
|
||||
/// a single storage entry is shared between multiple `StoredMap` items single, without
|
||||
/// additional logic to enforce it, deletion of any one them doesn't automatically imply
|
||||
/// deletion of them all.
|
||||
fn is_explicit(k: &K) -> bool;
|
||||
/// Mutate the item.
|
||||
fn mutate<R>(k: &K, f: impl FnOnce(&mut T) -> R) -> R;
|
||||
/// Mutate the item, removing or resetting to default value if it has been mutated to `None`.
|
||||
fn mutate_exists<R>(k: &K, f: impl FnOnce(&mut Option<T>) -> R) -> R;
|
||||
/// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is
|
||||
/// returned. It is removed or reset to default value if it has been mutated to `None`
|
||||
fn try_mutate_exists<R, E>(k: &K, f: impl FnOnce(&mut Option<T>) -> Result<R, E>) -> Result<R, E>;
|
||||
/// Set the item to something new.
|
||||
fn insert(k: &K, t: T) { Self::mutate(k, |i| *i = t); }
|
||||
/// Remove the item or otherwise replace it with its default value; we don't care which.
|
||||
fn remove(k: &K);
|
||||
}
|
||||
|
||||
/// A simple, generic one-parameter event notifier/handler.
|
||||
pub trait Happened<T> {
|
||||
/// The thing happened.
|
||||
fn happened(t: &T);
|
||||
}
|
||||
|
||||
/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this
|
||||
/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this
|
||||
/// would break the ability to have custom impls of `StoredValue`. The other workaround is to
|
||||
/// implement it directly in the macro.
|
||||
///
|
||||
/// This form has the advantage that two additional types are provides, `Created` and `Removed`,
|
||||
/// which are both generic events that can be tied to handlers to do something in the case of being
|
||||
/// about to create an account where one didn't previously exist (at all; not just where it used to
|
||||
/// be the default value), or where the account is being removed or reset back to the default value
|
||||
/// where previously it did exist (though may have been in a default state). This works well with
|
||||
/// system module's `CallOnCreatedAccount` and `CallKillAccount`.
|
||||
pub struct StorageMapShim<
|
||||
S,
|
||||
Created,
|
||||
Removed,
|
||||
K,
|
||||
T
|
||||
>(sp_std::marker::PhantomData<(S, Created, Removed, K, T)>);
|
||||
impl<
|
||||
S: StorageMap<K, T, Query=T>,
|
||||
Created: Happened<K>,
|
||||
Removed: Happened<K>,
|
||||
K: FullCodec,
|
||||
T: FullCodec
|
||||
> StoredMap<K, T> for StorageMapShim<S, Created, Removed, K, T> {
|
||||
fn get(k: &K) -> T { S::get(k) }
|
||||
fn is_explicit(k: &K) -> bool { S::contains_key(k) }
|
||||
fn insert(k: &K, t: T) {
|
||||
S::insert(k, t);
|
||||
if !S::contains_key(&k) {
|
||||
Created::happened(k);
|
||||
}
|
||||
}
|
||||
fn remove(k: &K) {
|
||||
if S::contains_key(&k) {
|
||||
Removed::happened(&k);
|
||||
}
|
||||
S::remove(k);
|
||||
}
|
||||
fn mutate<R>(k: &K, f: impl FnOnce(&mut T) -> R) -> R {
|
||||
let r = S::mutate(k, f);
|
||||
if !S::contains_key(&k) {
|
||||
Created::happened(k);
|
||||
}
|
||||
r
|
||||
}
|
||||
fn mutate_exists<R>(k: &K, f: impl FnOnce(&mut Option<T>) -> R) -> R {
|
||||
let (existed, exists, r) = S::mutate_exists(k, |maybe_value| {
|
||||
let existed = maybe_value.is_some();
|
||||
let r = f(maybe_value);
|
||||
(existed, maybe_value.is_some(), r)
|
||||
});
|
||||
if !existed && exists {
|
||||
Created::happened(k);
|
||||
} else if existed && !exists {
|
||||
Removed::happened(k);
|
||||
}
|
||||
r
|
||||
}
|
||||
fn try_mutate_exists<R, E>(k: &K, f: impl FnOnce(&mut Option<T>) -> Result<R, E>) -> Result<R, E> {
|
||||
S::try_mutate_exists(k, |maybe_value| {
|
||||
let existed = maybe_value.is_some();
|
||||
f(maybe_value).map(|v| (existed, maybe_value.is_some(), v))
|
||||
}).map(|(existed, exists, v)| {
|
||||
if !existed && exists {
|
||||
Created::happened(k);
|
||||
} else if existed && !exists {
|
||||
Removed::happened(k);
|
||||
}
|
||||
v
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Anything that can have a `::len()` method.
|
||||
pub trait Len {
|
||||
@@ -65,6 +174,25 @@ pub trait Contains<T: Ord> {
|
||||
fn count() -> usize { Self::sorted_members().len() }
|
||||
}
|
||||
|
||||
/// Determiner to say whether a given account is unused.
|
||||
pub trait IsDeadAccount<AccountId> {
|
||||
/// Is the given account dead?
|
||||
fn is_dead_account(who: &AccountId) -> bool;
|
||||
}
|
||||
|
||||
impl<AccountId> IsDeadAccount<AccountId> for () {
|
||||
fn is_dead_account(_who: &AccountId) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for when a new account has been created.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait OnNewAccount<AccountId> {
|
||||
/// A new account `who` has been registered.
|
||||
fn on_new_account(who: &AccountId);
|
||||
}
|
||||
|
||||
/// The account with the given id was reaped.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait OnReapAccount<AccountId> {
|
||||
@@ -72,20 +200,6 @@ pub trait OnReapAccount<AccountId> {
|
||||
fn on_reap_account(who: &AccountId);
|
||||
}
|
||||
|
||||
/// Outcome of a balance update.
|
||||
pub enum UpdateBalanceOutcome {
|
||||
/// Account balance was simply updated.
|
||||
Updated,
|
||||
/// The update led to killing the account.
|
||||
AccountKilled,
|
||||
/// Free balance became zero as a result of this update.
|
||||
FreeBalanceZero,
|
||||
/// Reserved balance became zero as a result of this update.
|
||||
ReservedBalanceZero,
|
||||
/// The account started and ended non-existent.
|
||||
StillDead,
|
||||
}
|
||||
|
||||
/// A trait for finding the author of a block header based on the `PreRuntime` digests contained
|
||||
/// within it.
|
||||
pub trait FindAuthor<Author> {
|
||||
@@ -494,10 +608,15 @@ pub trait Currency<AccountId> {
|
||||
fn make_free_balance_be(
|
||||
who: &AccountId,
|
||||
balance: Self::Balance,
|
||||
) -> (
|
||||
SignedImbalance<Self::Balance, Self::PositiveImbalance>,
|
||||
UpdateBalanceOutcome,
|
||||
);
|
||||
) -> SignedImbalance<Self::Balance, Self::PositiveImbalance>;
|
||||
}
|
||||
|
||||
/// Status of funds.
|
||||
pub enum BalanceStatus {
|
||||
/// Funds are free, as corresponding to `free` item in Balances.
|
||||
Free,
|
||||
/// Funds are reserved, as corresponding to `reserved` item in Balances.
|
||||
Reserved,
|
||||
}
|
||||
|
||||
/// A currency where funds can be reserved from the user.
|
||||
@@ -528,7 +647,6 @@ pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
|
||||
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
|
||||
fn reserved_balance(who: &AccountId) -> Self::Balance;
|
||||
|
||||
|
||||
/// 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
|
||||
@@ -547,16 +665,18 @@ pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
|
||||
/// invoke `on_reserved_too_low` and could reap the account.
|
||||
fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance;
|
||||
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to free balance of account
|
||||
/// Moves up to `value` from reserved balance of account `slashed` to balance of account
|
||||
/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
|
||||
/// returned.
|
||||
/// returned. Funds will be placed in either the `free` balance or the `reserved` balance,
|
||||
/// depending on the `status`.
|
||||
///
|
||||
/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
|
||||
/// then `Ok(non_zero)` will be returned.
|
||||
fn repatriate_reserved(
|
||||
slashed: &AccountId,
|
||||
beneficiary: &AccountId,
|
||||
value: Self::Balance
|
||||
value: Self::Balance,
|
||||
status: BalanceStatus,
|
||||
) -> result::Result<Self::Balance, DispatchError>;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},`
|
||||
error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event<T>},`
|
||||
--> $DIR/missing_system_module.rs:8:2
|
||||
|
|
||||
8 | {
|
||||
|
||||
@@ -99,7 +99,7 @@ frame_support::construct_runtime!(
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Module1_1: module1::<Instance1>::{Module, Call, Storage},
|
||||
Module2: module2::{Module, Call, Storage},
|
||||
Module1_2: module1::<Instance2>::{Module, Call, Storage},
|
||||
|
||||
@@ -247,7 +247,7 @@ frame_support::construct_runtime!(
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Module1_1: module1::<Instance1>::{
|
||||
Module, Call, Storage, Event<T>, Config<T>, Origin<T>, Inherent
|
||||
},
|
||||
|
||||
@@ -173,7 +173,7 @@ frame_support::construct_runtime!(
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: system::{Module, Call, Event},
|
||||
System: system::{Module, Call, Event<T>},
|
||||
Module: module::{Module, Call, Storage, Config},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ pub trait Trait: 'static + Eq + Clone {
|
||||
type BlockNumber: Decode + Encode + EncodeLike + Clone + Default;
|
||||
type Hash;
|
||||
type AccountId: Encode + EncodeLike + Decode;
|
||||
type Event: From<Event>;
|
||||
type Event: From<Event<Self>>;
|
||||
type ModuleToIndex: frame_support::traits::ModuleToIndex;
|
||||
}
|
||||
|
||||
@@ -36,9 +36,10 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
frame_support::decl_event!(
|
||||
pub enum Event {
|
||||
pub enum Event<T> where BlockNumber = <T as Trait>::BlockNumber {
|
||||
ExtrinsicSuccess,
|
||||
ExtrinsicFailed,
|
||||
Ignore(BlockNumber),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ impl_outer_origin!{
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum Event for Runtime {
|
||||
system<T>,
|
||||
module,
|
||||
}
|
||||
}
|
||||
@@ -75,6 +76,9 @@ impl system::Trait for Runtime {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl module::Trait for Runtime {
|
||||
|
||||
+320
-183
@@ -113,35 +113,19 @@ use sp_runtime::{
|
||||
use sp_core::{ChangesTrieConfiguration, storage::well_known_keys};
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, decl_error, storage, Parameter,
|
||||
traits::{Contains, Get, ModuleToIndex, OnReapAccount},
|
||||
traits::{
|
||||
Contains, Get, ModuleToIndex, OnNewAccount, OnReapAccount, IsDeadAccount, Happened,
|
||||
StoredMap
|
||||
},
|
||||
weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo},
|
||||
};
|
||||
use codec::{Encode, Decode};
|
||||
use codec::{Encode, Decode, FullCodec, EncodeLike};
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use sp_io::TestExternalities;
|
||||
|
||||
pub mod offchain;
|
||||
|
||||
/// Handler for when a new account has been created.
|
||||
#[impl_trait_for_tuples::impl_for_tuples(30)]
|
||||
pub trait OnNewAccount<AccountId> {
|
||||
/// A new account `who` has been registered.
|
||||
fn on_new_account(who: &AccountId);
|
||||
}
|
||||
|
||||
/// Determiner to say whether a given account is unused.
|
||||
pub trait IsDeadAccount<AccountId> {
|
||||
/// Is the given account dead?
|
||||
fn is_dead_account(who: &AccountId) -> bool;
|
||||
}
|
||||
|
||||
impl<AccountId> IsDeadAccount<AccountId> for () {
|
||||
fn is_dead_account(_who: &AccountId) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the trie root of a list of extrinsics.
|
||||
pub fn extrinsics_root<H: Hash, E: codec::Encode>(extrinsics: &[E]) -> H::Output {
|
||||
extrinsics_data_root::<H>(extrinsics.iter().map(codec::Encode::encode).collect())
|
||||
@@ -200,7 +184,7 @@ pub trait Trait: 'static + Eq + Clone {
|
||||
>;
|
||||
|
||||
/// The aggregated event type of the runtime.
|
||||
type Event: Parameter + Member + From<Event> + Debug;
|
||||
type Event: Parameter + Member + From<Event<Self>> + Debug;
|
||||
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
type BlockHashCount: Get<Self::BlockNumber>;
|
||||
@@ -224,6 +208,18 @@ pub trait Trait: 'static + Eq + Clone {
|
||||
/// Expects the `ModuleToIndex` type that is being generated by `construct_runtime!` in the
|
||||
/// runtime. For tests it is okay to use `()` as type (returns `0` for each input).
|
||||
type ModuleToIndex: ModuleToIndex;
|
||||
|
||||
/// Data to be associated with an account (other than nonce/transaction counter, which this
|
||||
/// module does regardless).
|
||||
type AccountData: Member + FullCodec + Clone + Default;
|
||||
|
||||
/// Handler for when a new account has just been created.
|
||||
type OnNewAccount: OnNewAccount<Self::AccountId>;
|
||||
|
||||
/// A function that is invoked when an account has been determined to be dead.
|
||||
///
|
||||
/// All resources should be cleaned up associated with the given account.
|
||||
type OnReapAccount: OnReapAccount<Self::AccountId>;
|
||||
}
|
||||
|
||||
pub type DigestOf<T> = generic::Digest<<T as Trait>::Hash>;
|
||||
@@ -232,111 +228,6 @@ pub type DigestItemOf<T> = generic::DigestItem<<T as Trait>::Hash>;
|
||||
pub type Key = Vec<u8>;
|
||||
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
/// A big dispatch that will disallow any other transaction to be included.
|
||||
// TODO: this must be preferable available for testing really (not possible at the moment).
|
||||
#[weight = SimpleDispatchInfo::MaxOperational]
|
||||
fn fill_block(origin) {
|
||||
ensure_root(origin)?;
|
||||
}
|
||||
|
||||
/// Make some on-chain remark.
|
||||
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
|
||||
fn remark(origin, _remark: Vec<u8>) {
|
||||
ensure_signed(origin)?;
|
||||
}
|
||||
|
||||
/// Set the number of pages in the WebAssembly environment's heap.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn set_heap_pages(origin, pages: u64) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode());
|
||||
}
|
||||
|
||||
/// Set the new runtime code.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(200_000)]
|
||||
pub fn set_code(origin, code: Vec<u8>) {
|
||||
ensure_root(origin)?;
|
||||
|
||||
let current_version = T::Version::get();
|
||||
let new_version = sp_io::misc::runtime_version(&code)
|
||||
.and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok())
|
||||
.ok_or_else(|| Error::<T>::FailedToExtractRuntimeVersion)?;
|
||||
|
||||
if new_version.spec_name != current_version.spec_name {
|
||||
Err(Error::<T>::InvalidSpecName)?
|
||||
}
|
||||
|
||||
if new_version.spec_version < current_version.spec_version {
|
||||
Err(Error::<T>::SpecVersionNotAllowedToDecrease)?
|
||||
} else if new_version.spec_version == current_version.spec_version {
|
||||
if new_version.impl_version < current_version.impl_version {
|
||||
Err(Error::<T>::ImplVersionNotAllowedToDecrease)?
|
||||
} else if new_version.impl_version == current_version.impl_version {
|
||||
Err(Error::<T>::SpecOrImplVersionNeedToIncrease)?
|
||||
}
|
||||
}
|
||||
|
||||
storage::unhashed::put_raw(well_known_keys::CODE, &code);
|
||||
Self::deposit_event(Event::CodeUpdated);
|
||||
}
|
||||
|
||||
/// Set the new runtime code without doing any checks of the given `code`.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(200_000)]
|
||||
pub fn set_code_without_checks(origin, code: Vec<u8>) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::put_raw(well_known_keys::CODE, &code);
|
||||
Self::deposit_event(Event::CodeUpdated);
|
||||
}
|
||||
|
||||
/// Set the new changes trie configuration.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(20_000)]
|
||||
pub fn set_changes_trie_config(origin, changes_trie_config: Option<ChangesTrieConfiguration>) {
|
||||
ensure_root(origin)?;
|
||||
match changes_trie_config.clone() {
|
||||
Some(changes_trie_config) => storage::unhashed::put_raw(
|
||||
well_known_keys::CHANGES_TRIE_CONFIG,
|
||||
&changes_trie_config.encode(),
|
||||
),
|
||||
None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG),
|
||||
}
|
||||
|
||||
let log = generic::DigestItem::ChangesTrieSignal(
|
||||
generic::ChangesTrieSignal::NewConfiguration(changes_trie_config),
|
||||
);
|
||||
Self::deposit_log(log.into());
|
||||
}
|
||||
|
||||
/// Set some items of storage.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn set_storage(origin, items: Vec<KeyValue>) {
|
||||
ensure_root(origin)?;
|
||||
for i in &items {
|
||||
storage::unhashed::put_raw(&i.0, &i.1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill some items from storage.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn kill_storage(origin, keys: Vec<Key>) {
|
||||
ensure_root(origin)?;
|
||||
for key in &keys {
|
||||
storage::unhashed::kill(&key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill all storage items with a key that starts with the given prefix.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn kill_prefix(origin, prefix: Key) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::kill_prefix(&prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A phase of a block's execution.
|
||||
#[derive(Encode, Decode, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone))]
|
||||
@@ -359,40 +250,6 @@ pub struct EventRecord<E: Parameter + Member, T> {
|
||||
pub topics: Vec<T>,
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event for the System module.
|
||||
pub enum Event {
|
||||
/// An extrinsic completed successfully.
|
||||
ExtrinsicSuccess(DispatchInfo),
|
||||
/// An extrinsic failed.
|
||||
ExtrinsicFailed(DispatchError, DispatchInfo),
|
||||
/// `:code` was updated.
|
||||
CodeUpdated,
|
||||
}
|
||||
);
|
||||
|
||||
decl_error! {
|
||||
/// Error for the System module
|
||||
pub enum Error for Module<T: Trait> {
|
||||
/// The name of specification does not match between the current runtime
|
||||
/// and the new runtime.
|
||||
InvalidSpecName,
|
||||
/// The specification version is not allowed to decrease between the current runtime
|
||||
/// and the new runtime.
|
||||
SpecVersionNotAllowedToDecrease,
|
||||
/// The implementation version is not allowed to decrease between the current runtime
|
||||
/// and the new runtime.
|
||||
ImplVersionNotAllowedToDecrease,
|
||||
/// The specification or the implementation version need to increase between the
|
||||
/// current runtime and the new runtime.
|
||||
SpecOrImplVersionNeedToIncrease,
|
||||
/// Failed to extract the runtime version from the new runtime.
|
||||
///
|
||||
/// Either calling `Core_version` or decoding `RuntimeVersion` failed.
|
||||
FailedToExtractRuntimeVersion,
|
||||
}
|
||||
}
|
||||
|
||||
/// Origin for the System module.
|
||||
#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
|
||||
pub enum RawOrigin<AccountId> {
|
||||
@@ -435,29 +292,45 @@ type EventIndex = u32;
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as System {
|
||||
/// Extrinsics nonce for accounts.
|
||||
pub AccountNonce get(fn account_nonce): map hasher(blake2_256) T::AccountId => T::Index;
|
||||
/// The full account information for a particular account ID.
|
||||
// TODO: should be hasher(twox64_concat) - will need staged migration
|
||||
// TODO: should not including T::Index (the nonce)
|
||||
// https://github.com/paritytech/substrate/issues/4917
|
||||
pub Account get(fn account): map hasher(blake2_256) T::AccountId => (T::Index, T::AccountData);
|
||||
|
||||
/// Total extrinsics count for the current block.
|
||||
ExtrinsicCount: Option<u32>;
|
||||
|
||||
/// Total weight for all extrinsics put together, for the current block.
|
||||
AllExtrinsicsWeight: Option<Weight>;
|
||||
|
||||
/// Total length (in bytes) for all extrinsics put together, for the current block.
|
||||
AllExtrinsicsLen: Option<u32>;
|
||||
|
||||
/// Map of block numbers to block hashes.
|
||||
// TODO: should be hasher(twox64_concat) - will need one-off migration
|
||||
// https://github.com/paritytech/substrate/issues/4917
|
||||
pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]):
|
||||
map hasher(blake2_256) T::BlockNumber => T::Hash;
|
||||
|
||||
/// Extrinsics data for the current block (maps an extrinsic's index to its data).
|
||||
ExtrinsicData get(fn extrinsic_data): map hasher(blake2_256) u32 => Vec<u8>;
|
||||
ExtrinsicData get(fn extrinsic_data): map hasher(twox_64_concat) u32 => Vec<u8>;
|
||||
|
||||
/// The current block number being processed. Set by `execute_block`.
|
||||
Number get(fn block_number) build(|_| 1.into()): T::BlockNumber;
|
||||
|
||||
/// Hash of the previous block.
|
||||
ParentHash get(fn parent_hash) build(|_| hash69()): T::Hash;
|
||||
|
||||
/// Extrinsics root of the current block, also part of the block header.
|
||||
ExtrinsicsRoot get(fn extrinsics_root): T::Hash;
|
||||
|
||||
/// Digest of the current block, also part of the block header.
|
||||
Digest get(fn digest): DigestOf<T>;
|
||||
|
||||
/// Events deposited for the current block.
|
||||
Events get(fn events): Vec<EventRecord<T::Event, T::Hash>>;
|
||||
|
||||
/// The number of events in the `Events<T>` list.
|
||||
EventCount get(fn event_count): EventIndex;
|
||||
|
||||
@@ -499,6 +372,150 @@ decl_storage! {
|
||||
}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
/// Event for the System module.
|
||||
pub enum Event<T> where AccountId = <T as Trait>::AccountId {
|
||||
/// An extrinsic completed successfully.
|
||||
ExtrinsicSuccess(DispatchInfo),
|
||||
/// An extrinsic failed.
|
||||
ExtrinsicFailed(DispatchError, DispatchInfo),
|
||||
/// `:code` was updated.
|
||||
CodeUpdated,
|
||||
/// A new account was created.
|
||||
NewAccount(AccountId),
|
||||
/// An account was reaped.
|
||||
ReapedAccount(AccountId),
|
||||
}
|
||||
);
|
||||
|
||||
decl_error! {
|
||||
/// Error for the System module
|
||||
pub enum Error for Module<T: Trait> {
|
||||
/// The name of specification does not match between the current runtime
|
||||
/// and the new runtime.
|
||||
InvalidSpecName,
|
||||
/// The specification version is not allowed to decrease between the current runtime
|
||||
/// and the new runtime.
|
||||
SpecVersionNotAllowedToDecrease,
|
||||
/// The implementation version is not allowed to decrease between the current runtime
|
||||
/// and the new runtime.
|
||||
ImplVersionNotAllowedToDecrease,
|
||||
/// The specification or the implementation version need to increase between the
|
||||
/// current runtime and the new runtime.
|
||||
SpecOrImplVersionNeedToIncrease,
|
||||
/// Failed to extract the runtime version from the new runtime.
|
||||
///
|
||||
/// Either calling `Core_version` or decoding `RuntimeVersion` failed.
|
||||
FailedToExtractRuntimeVersion,
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
type Error = Error<T>;
|
||||
|
||||
/// A big dispatch that will disallow any other transaction to be included.
|
||||
// TODO: This should only be available for testing, rather than in general usage, but
|
||||
// that's not possible at present (since it's within the decl_module macro).
|
||||
#[weight = SimpleDispatchInfo::MaxOperational]
|
||||
fn fill_block(origin) {
|
||||
ensure_root(origin)?;
|
||||
}
|
||||
|
||||
/// Make some on-chain remark.
|
||||
#[weight = SimpleDispatchInfo::FixedNormal(10_000)]
|
||||
fn remark(origin, _remark: Vec<u8>) {
|
||||
ensure_signed(origin)?;
|
||||
}
|
||||
|
||||
/// Set the number of pages in the WebAssembly environment's heap.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn set_heap_pages(origin, pages: u64) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode());
|
||||
}
|
||||
|
||||
/// Set the new runtime code.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(200_000)]
|
||||
pub fn set_code(origin, code: Vec<u8>) {
|
||||
ensure_root(origin)?;
|
||||
|
||||
let current_version = T::Version::get();
|
||||
let new_version = sp_io::misc::runtime_version(&code)
|
||||
.and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok())
|
||||
.ok_or_else(|| Error::<T>::FailedToExtractRuntimeVersion)?;
|
||||
|
||||
if new_version.spec_name != current_version.spec_name {
|
||||
Err(Error::<T>::InvalidSpecName)?
|
||||
}
|
||||
|
||||
if new_version.spec_version < current_version.spec_version {
|
||||
Err(Error::<T>::SpecVersionNotAllowedToDecrease)?
|
||||
} else if new_version.spec_version == current_version.spec_version {
|
||||
if new_version.impl_version < current_version.impl_version {
|
||||
Err(Error::<T>::ImplVersionNotAllowedToDecrease)?
|
||||
} else if new_version.impl_version == current_version.impl_version {
|
||||
Err(Error::<T>::SpecOrImplVersionNeedToIncrease)?
|
||||
}
|
||||
}
|
||||
|
||||
storage::unhashed::put_raw(well_known_keys::CODE, &code);
|
||||
Self::deposit_event(RawEvent::CodeUpdated);
|
||||
}
|
||||
|
||||
/// Set the new runtime code without doing any checks of the given `code`.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(200_000)]
|
||||
pub fn set_code_without_checks(origin, code: Vec<u8>) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::put_raw(well_known_keys::CODE, &code);
|
||||
Self::deposit_event(RawEvent::CodeUpdated);
|
||||
}
|
||||
|
||||
/// Set the new changes trie configuration.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(20_000)]
|
||||
pub fn set_changes_trie_config(origin, changes_trie_config: Option<ChangesTrieConfiguration>) {
|
||||
ensure_root(origin)?;
|
||||
match changes_trie_config.clone() {
|
||||
Some(changes_trie_config) => storage::unhashed::put_raw(
|
||||
well_known_keys::CHANGES_TRIE_CONFIG,
|
||||
&changes_trie_config.encode(),
|
||||
),
|
||||
None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG),
|
||||
}
|
||||
|
||||
let log = generic::DigestItem::ChangesTrieSignal(
|
||||
generic::ChangesTrieSignal::NewConfiguration(changes_trie_config),
|
||||
);
|
||||
Self::deposit_log(log.into());
|
||||
}
|
||||
|
||||
/// Set some items of storage.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn set_storage(origin, items: Vec<KeyValue>) {
|
||||
ensure_root(origin)?;
|
||||
for i in &items {
|
||||
storage::unhashed::put_raw(&i.0, &i.1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill some items from storage.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn kill_storage(origin, keys: Vec<Key>) {
|
||||
ensure_root(origin)?;
|
||||
for key in &keys {
|
||||
storage::unhashed::kill(&key);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill all storage items with a key that starts with the given prefix.
|
||||
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
|
||||
fn kill_prefix(origin, prefix: Key) {
|
||||
ensure_root(origin)?;
|
||||
storage::unhashed::kill_prefix(&prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnsureRoot<AccountId>(sp_std::marker::PhantomData<AccountId>);
|
||||
impl<
|
||||
O: Into<Result<RawOrigin<AccountId>, O>> + From<RawOrigin<AccountId>>,
|
||||
@@ -622,7 +639,7 @@ impl<T: Trait> Module<T> {
|
||||
Self::deposit_event_indexed(&[], event.into());
|
||||
}
|
||||
|
||||
/// Deposits an event into this block's event record adding this event
|
||||
/// Deposits an event into this block's event record adding this event
|
||||
/// to the corresponding topic indexes.
|
||||
///
|
||||
/// This will update storage entries that correspond to the specified topics.
|
||||
@@ -832,9 +849,14 @@ impl<T: Trait> Module<T> {
|
||||
/// Return the chain's current runtime version.
|
||||
pub fn runtime_version() -> RuntimeVersion { T::Version::get() }
|
||||
|
||||
/// Retrieve the account transaction counter from storage.
|
||||
pub fn account_nonce(who: impl EncodeLike<T::AccountId>) -> T::Index {
|
||||
Account::<T>::get(who).0
|
||||
}
|
||||
|
||||
/// Increment a particular account's nonce by 1.
|
||||
pub fn inc_account_nonce(who: &T::AccountId) {
|
||||
<AccountNonce<T>>::insert(who, Self::account_nonce(who) + T::Index::one());
|
||||
pub fn inc_account_nonce(who: impl EncodeLike<T::AccountId>) {
|
||||
Account::<T>::mutate(who, |a| a.0 += T::Index::one());
|
||||
}
|
||||
|
||||
/// Note what the extrinsic data of the current extrinsic index is. If this
|
||||
@@ -851,10 +873,10 @@ impl<T: Trait> Module<T> {
|
||||
pub fn note_applied_extrinsic(r: &DispatchOutcome, _encoded_len: u32, info: DispatchInfo) {
|
||||
Self::deposit_event(
|
||||
match r {
|
||||
Ok(()) => Event::ExtrinsicSuccess(info),
|
||||
Ok(()) => RawEvent::ExtrinsicSuccess(info),
|
||||
Err(err) => {
|
||||
sp_runtime::print(err);
|
||||
Event::ExtrinsicFailed(err.clone(), info)
|
||||
RawEvent::ExtrinsicFailed(err.clone(), info)
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -879,12 +901,118 @@ impl<T: Trait> Module<T> {
|
||||
let xts_root = extrinsics_data_root::<T::Hashing>(extrinsics);
|
||||
<ExtrinsicsRoot<T>>::put(xts_root);
|
||||
}
|
||||
|
||||
/// An account is being created.
|
||||
pub fn on_created_account(who: T::AccountId) {
|
||||
T::OnNewAccount::on_new_account(&who);
|
||||
Self::deposit_event(RawEvent::NewAccount(who));
|
||||
}
|
||||
|
||||
/// Kill the account and reap any related information.
|
||||
pub fn kill_account(who: T::AccountId) {
|
||||
if Account::<T>::contains_key(&who) {
|
||||
Account::<T>::remove(&who);
|
||||
Self::on_killed_account(who);
|
||||
}
|
||||
}
|
||||
|
||||
/// Do anything that needs to be done after an account has been killed.
|
||||
fn on_killed_account(who: T::AccountId) {
|
||||
T::OnReapAccount::on_reap_account(&who);
|
||||
Self::deposit_event(RawEvent::ReapedAccount(who));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> OnReapAccount<T::AccountId> for Module<T> {
|
||||
/// Remove the nonce for the account. Account is considered fully removed from the system.
|
||||
fn on_reap_account(who: &T::AccountId) {
|
||||
<AccountNonce<T>>::remove(who);
|
||||
/// Event handler which calls on_created_account when it happens.
|
||||
pub struct CallOnCreatedAccount<T>(PhantomData<T>);
|
||||
impl<T: Trait> Happened<T::AccountId> for CallOnCreatedAccount<T> {
|
||||
fn happened(who: &T::AccountId) {
|
||||
Module::<T>::on_created_account(who.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Event handler which calls kill_account when it happens.
|
||||
pub struct CallKillAccount<T>(PhantomData<T>);
|
||||
impl<T: Trait> Happened<T::AccountId> for CallKillAccount<T> {
|
||||
fn happened(who: &T::AccountId) {
|
||||
Module::<T>::kill_account(who.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Implement StoredMap for a simple single-item, kill-account-on-remove system. This works fine for
|
||||
// storing a single item which is required to not be empty/default for the account to exist.
|
||||
// Anything more complex will need more sophisticated logic.
|
||||
impl<T: Trait> StoredMap<T::AccountId, T::AccountData> for Module<T> {
|
||||
fn get(k: &T::AccountId) -> T::AccountData {
|
||||
Account::<T>::get(k).1
|
||||
}
|
||||
fn is_explicit(k: &T::AccountId) -> bool {
|
||||
Account::<T>::contains_key(k)
|
||||
}
|
||||
fn insert(k: &T::AccountId, t: T::AccountData) {
|
||||
let existed = Account::<T>::contains_key(k);
|
||||
Account::<T>::insert(k, (T::Index::default(), t));
|
||||
if !existed {
|
||||
Self::on_created_account(k.clone());
|
||||
}
|
||||
}
|
||||
fn remove(k: &T::AccountId) {
|
||||
if Account::<T>::contains_key(&k) {
|
||||
Self::kill_account(k.clone());
|
||||
}
|
||||
}
|
||||
fn mutate<R>(k: &T::AccountId, f: impl FnOnce(&mut T::AccountData) -> R) -> R {
|
||||
let existed = Account::<T>::contains_key(k);
|
||||
let r = Account::<T>::mutate(k, |a| f(&mut a.1));
|
||||
if !existed {
|
||||
Self::on_created_account(k.clone());
|
||||
}
|
||||
r
|
||||
}
|
||||
fn mutate_exists<R>(k: &T::AccountId, f: impl FnOnce(&mut Option<T::AccountData>) -> R) -> R {
|
||||
let (existed, exists, r) = Account::<T>::mutate_exists(k, |maybe_value| {
|
||||
let existed = maybe_value.is_some();
|
||||
let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v);
|
||||
let r = f(&mut maybe_extra);
|
||||
*maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra));
|
||||
(existed, maybe_value.is_some(), r)
|
||||
});
|
||||
if !existed && exists {
|
||||
Self::on_created_account(k.clone());
|
||||
} else if existed && !exists {
|
||||
Self::on_killed_account(k.clone());
|
||||
}
|
||||
r
|
||||
}
|
||||
fn try_mutate_exists<R, E>(k: &T::AccountId, f: impl FnOnce(&mut Option<T::AccountData>) -> Result<R, E>) -> Result<R, E> {
|
||||
Account::<T>::try_mutate_exists(k, |maybe_value| {
|
||||
let existed = maybe_value.is_some();
|
||||
let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v);
|
||||
f(&mut maybe_extra).map(|v| {
|
||||
*maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra));
|
||||
(existed, maybe_value.is_some(), v)
|
||||
})
|
||||
}).map(|(existed, exists, v)| {
|
||||
if !existed && exists {
|
||||
Self::on_created_account(k.clone());
|
||||
} else if existed && !exists {
|
||||
Self::on_killed_account(k.clone());
|
||||
}
|
||||
v
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Split an `option` into two constituent options, as defined by a `splitter` function.
|
||||
pub fn split_inner<T, R, S>(option: Option<T>, splitter: impl FnOnce(T) -> (R, S))
|
||||
-> (Option<R>, Option<S>)
|
||||
{
|
||||
match option {
|
||||
Some(inner) => {
|
||||
let (r, s) = splitter(inner);
|
||||
(Some(r), Some(s))
|
||||
}
|
||||
None => (None, None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,7 +1180,7 @@ impl<T: Trait> SignedExtension for CheckNonce<T> {
|
||||
_info: Self::DispatchInfo,
|
||||
_len: usize,
|
||||
) -> Result<(), TransactionValidityError> {
|
||||
let expected = <AccountNonce<T>>::get(who);
|
||||
let (expected, extra) = Account::<T>::get(who);
|
||||
if self.0 != expected {
|
||||
return Err(
|
||||
if self.0 < expected {
|
||||
@@ -1062,8 +1190,7 @@ impl<T: Trait> SignedExtension for CheckNonce<T> {
|
||||
}.into()
|
||||
)
|
||||
}
|
||||
|
||||
<AccountNonce<T>>::insert(who, expected + T::Index::one());
|
||||
Account::<T>::insert(who, (expected + T::Index::one(), extra));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1075,7 +1202,7 @@ impl<T: Trait> SignedExtension for CheckNonce<T> {
|
||||
_len: usize,
|
||||
) -> TransactionValidity {
|
||||
// check index
|
||||
let expected = <AccountNonce<T>>::get(who);
|
||||
let (expected, _extra) = Account::<T>::get(who);
|
||||
if self.0 < expected {
|
||||
return InvalidTransaction::Stale.into()
|
||||
}
|
||||
@@ -1097,6 +1224,12 @@ impl<T: Trait> SignedExtension for CheckNonce<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T> {
|
||||
fn is_dead_account(who: &T::AccountId) -> bool {
|
||||
!Account::<T>::contains_key(who)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for transaction mortality.
|
||||
#[derive(Encode, Decode, Clone, Eq, PartialEq)]
|
||||
pub struct CheckEra<T: Trait + Send + Sync>((Era, sp_std::marker::PhantomData<T>));
|
||||
@@ -1288,14 +1421,18 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = Version;
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
|
||||
impl From<Event> for u16 {
|
||||
fn from(e: Event) -> u16 {
|
||||
impl From<Event<Test>> for u16 {
|
||||
fn from(e: Event<Test>) -> u16 {
|
||||
match e {
|
||||
Event::ExtrinsicSuccess(..) => 100,
|
||||
Event::ExtrinsicFailed(..) => 101,
|
||||
Event::CodeUpdated => 102,
|
||||
Event::<Test>::ExtrinsicSuccess(..) => 100,
|
||||
Event::<Test>::ExtrinsicFailed(..) => 101,
|
||||
Event::<Test>::CodeUpdated => 102,
|
||||
_ => 103,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1475,7 +1612,7 @@ mod tests {
|
||||
#[test]
|
||||
fn signed_ext_check_nonce_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
<AccountNonce<Test>>::insert(1, 1);
|
||||
Account::<Test>::insert(1, (1, ()));
|
||||
let info = DispatchInfo::default();
|
||||
let len = 0_usize;
|
||||
// stale
|
||||
|
||||
@@ -20,8 +20,8 @@ use codec::Encode;
|
||||
use sp_std::convert::TryInto;
|
||||
use sp_std::prelude::Vec;
|
||||
use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature};
|
||||
use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount};
|
||||
use frame_support::debug;
|
||||
use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One};
|
||||
use frame_support::{debug, storage::StorageMap};
|
||||
|
||||
/// Creates runtime-specific signed transaction.
|
||||
///
|
||||
@@ -128,19 +128,19 @@ pub trait SignAndSubmitTransaction<T: crate::Trait, Call> {
|
||||
fn sign_and_submit(call: impl Into<Call>, public: PublicOf<T, Call, Self>) -> Result<(), ()> {
|
||||
let call = call.into();
|
||||
let id = public.clone().into_account();
|
||||
let expected = <crate::Module<T>>::account_nonce(&id);
|
||||
let (expected_nonce, extra) = super::Account::<T>::get(&id);
|
||||
debug::native::debug!(
|
||||
target: "offchain",
|
||||
"Creating signed transaction from account: {:?} (nonce: {:?})",
|
||||
id,
|
||||
expected,
|
||||
expected_nonce,
|
||||
);
|
||||
let (call, signature_data) = Self::CreateTransaction
|
||||
::create_transaction::<Self::Signer>(call, public, id.clone(), expected)
|
||||
::create_transaction::<Self::Signer>(call, public, id.clone(), expected_nonce)
|
||||
.ok_or(())?;
|
||||
// increment the nonce. This is fine, since the code should always
|
||||
// be running in off-chain context, so we NEVER persists data.
|
||||
<crate::Module<T>>::inc_account_nonce(&id);
|
||||
super::Account::<T>::insert(&id, (expected_nonce + One::one(), extra));
|
||||
|
||||
let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?;
|
||||
sp_io::offchain::submit_transaction(xt.encode())
|
||||
|
||||
@@ -276,6 +276,9 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const MinimumPeriod: u64 = 5;
|
||||
|
||||
@@ -302,25 +302,23 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const CreationFee: u64 = 0;
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_balances::Trait for Runtime {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
thread_local! {
|
||||
static TRANSACTION_BASE_FEE: RefCell<u64> = RefCell::new(0);
|
||||
static TRANSACTION_BYTE_FEE: RefCell<u64> = RefCell::new(1);
|
||||
static WEIGHT_TO_FEE: RefCell<u64> = RefCell::new(1);
|
||||
|
||||
@@ -758,20 +758,19 @@ mod tests {
|
||||
type MaximumBlockLength = MaximumBlockLength;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = System;
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
pub struct TenToFourteen;
|
||||
impl Contains<u64> for TenToFourteen {
|
||||
|
||||
@@ -661,6 +661,7 @@ mod tests {
|
||||
|
||||
impl_outer_event! {
|
||||
pub enum TestEvent for Test {
|
||||
system<T>,
|
||||
pallet_balances<T>,
|
||||
utility<T>,
|
||||
}
|
||||
@@ -700,20 +701,19 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const ExistentialDeposit: u64 = 1;
|
||||
pub const CreationFee: u64 = 0;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = TestEvent;
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const MultisigDepositBase: u64 = 1;
|
||||
|
||||
@@ -50,7 +50,7 @@ use sp_std::prelude::*;
|
||||
use sp_std::fmt::Debug;
|
||||
use codec::{Encode, Decode};
|
||||
use sp_runtime::{DispatchResult, RuntimeDebug, traits::{
|
||||
StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Saturating, Convert
|
||||
StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert
|
||||
}};
|
||||
use frame_support::{decl_module, decl_event, decl_storage, decl_error};
|
||||
use frame_support::traits::{
|
||||
@@ -115,6 +115,7 @@ decl_storage! {
|
||||
add_extra_genesis {
|
||||
config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf<T>)>;
|
||||
build(|config: &GenesisConfig<T>| {
|
||||
use sp_runtime::traits::Saturating;
|
||||
// Generate initial vesting configuration
|
||||
// * who - Account which we are generating vesting configuration for
|
||||
// * begin - Block when the account will start to vest
|
||||
@@ -336,19 +337,16 @@ mod tests {
|
||||
type AvailableBlockRatio = AvailableBlockRatio;
|
||||
type Version = ();
|
||||
type ModuleToIndex = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const CreationFee: u64 = 0;
|
||||
type AccountData = pallet_balances::AccountData<u64>;
|
||||
type OnNewAccount = ();
|
||||
type OnReapAccount = Balances;
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
type OnReapAccount = System;
|
||||
type OnNewAccount = ();
|
||||
type Event = ();
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
type Event = ();
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type CreationFee = CreationFee;
|
||||
type AccountStore = System;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Event = ();
|
||||
|
||||
Reference in New Issue
Block a user