Split Indices module from Balances (#1404)

* Indices module

* Remove indices stuff from balances

* Rejob node, move Lookup into system.

* Fix up some modules.

* Fix democracy tests

* Fix staking tests

* Fix more tests

* Final test fixes

* Bump runtime versions

* Assets uses compact dispatchers

* Contracts module uses indexed addressing

* Democracy has more compact encoding

* Example now demonstrates compact eencoding

* Sudo uses indexed address

* Upgrade key also uses indexed lookups

* Assets more compact types.

* Fix test

* Rebuild runtime, whitespace

* Remove TOODs

* Remove TODOs

* Add a couple of tests back to balances.

* Update lib.rs

* Update lib.rs
This commit is contained in:
Gav Wood
2019-01-16 15:57:19 +01:00
committed by GitHub
parent 04175ddc83
commit c9f047fe84
44 changed files with 907 additions and 619 deletions
-109
View File
@@ -1,109 +0,0 @@
// Copyright 2017-2018 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/>.
//! Address type that is union of index and id for an account.
#[cfg(feature = "std")]
use std::fmt;
use super::{Member, Decode, Encode, As, Input, Output};
/// A vetted and verified extrinsic from the external world.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Debug, Hash))]
pub enum Address<AccountId, AccountIndex> where
AccountId: Member,
AccountIndex: Member,
{
/// It's an account ID (pubkey).
Id(AccountId),
/// It's an account index.
Index(AccountIndex),
}
#[cfg(feature = "std")]
impl<AccountId, AccountIndex> fmt::Display for Address<AccountId, AccountIndex> where
AccountId: Member,
AccountIndex: Member,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<AccountId, AccountIndex> From<AccountId> for Address<AccountId, AccountIndex> where
AccountId: Member,
AccountIndex: Member,
{
fn from(a: AccountId) -> Self {
Address::Id(a)
}
}
fn need_more_than<T: PartialOrd>(a: T, b: T) -> Option<T> {
if a < b { Some(a) } else { None }
}
impl<AccountId, AccountIndex> Decode for Address<AccountId, AccountIndex> where
AccountId: Member + Decode,
AccountIndex: Member + Decode + PartialOrd<AccountIndex> + Ord + As<u32> + As<u16> + As<u8> + Copy,
{
fn decode<I: Input>(input: &mut I) -> Option<Self> {
Some(match input.read_byte()? {
x @ 0x00...0xef => Address::Index(As::sa(x)),
0xfc => Address::Index(As::sa(need_more_than(0xef, u16::decode(input)?)?)),
0xfd => Address::Index(As::sa(need_more_than(0xffff, u32::decode(input)?)?)),
0xfe => Address::Index(need_more_than(As::sa(0xffffffffu32), Decode::decode(input)?)?),
0xff => Address::Id(Decode::decode(input)?),
_ => return None,
})
}
}
impl<AccountId, AccountIndex> Encode for Address<AccountId, AccountIndex> where
AccountId: Member + Encode,
AccountIndex: Member + Encode + PartialOrd<AccountIndex> + Ord + As<u32> + As<u16> + As<u8> + Copy,
{
fn encode_to<T: Output>(&self, dest: &mut T) {
match *self {
Address::Id(ref i) => {
dest.push_byte(255);
dest.push(i);
}
Address::Index(i) if i > As::sa(0xffffffffu32) => {
dest.push_byte(254);
dest.push(&i);
}
Address::Index(i) if i > As::sa(0xffffu32) => {
dest.push_byte(253);
dest.push(&As::<u32>::as_(i));
}
Address::Index(i) if i >= As::sa(0xf0u32) => {
dest.push_byte(252);
dest.push(&As::<u16>::as_(i));
}
Address::Index(i) => dest.push_byte(As::<u8>::as_(i)),
}
}
}
impl<AccountId, AccountIndex> Default for Address<AccountId, AccountIndex> where
AccountId: Member + Default,
AccountIndex: Member,
{
fn default() -> Self {
Address::Id(Default::default())
}
}
+25 -196
View File
@@ -43,27 +43,16 @@ extern crate substrate_primitives;
use rstd::prelude::*;
use rstd::{cmp, result};
use codec::{Encode, Decode, Codec, Input, Output, HasCompact};
use codec::{Codec, HasCompact};
use runtime_support::{StorageValue, StorageMap, Parameter};
use runtime_support::dispatch::Result;
use primitives::traits::{Zero, One, SimpleArithmetic, MakePayment,
As, Lookup, Member, CheckedAdd, CheckedSub, CurrentHeight, BlockNumberToHash};
use address::Address as RawAddress;
use system::ensure_signed;
use primitives::traits::{Zero, SimpleArithmetic, MakePayment,
As, StaticLookup, Member, CheckedAdd, CheckedSub};
use system::{IsDeadAccount, OnNewAccount, ensure_signed};
mod mock;
pub mod address;
mod tests;
/// Number of account IDs stored per enum set.
const ENUM_SET_SIZE: usize = 64;
/// The byte to identify intention to reclaim an existing account index.
const RECLAIM_INDEX_MAGIC: usize = 0x69;
pub type Address<T> = RawAddress<<T as system::Trait>::AccountId, <T as Trait>::AccountIndex>;
/// The account with the given id was killed.
pub trait OnFreeBalanceZero<AccountId> {
/// The account was the given id was killed.
@@ -117,16 +106,17 @@ impl<AccountId> EnsureAccountLiquid<AccountId> for () {
pub trait Trait: system::Trait {
/// The balance of an account.
type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As<Self::AccountIndex> + As<usize> + As<u64>;
/// Type used for storing an account's index; implies the maximum number of accounts the system
/// can hold.
type AccountIndex: Parameter + Member + Codec + Default + SimpleArithmetic + As<u8> + As<u16> + As<u32> + As<u64> + As<usize> + Copy;
type Balance: Parameter + Member + SimpleArithmetic + Codec + Default + Copy + As<usize> + As<u64>;
/// A function which is invoked when the free-balance has fallen below the existential deposit and
/// has been reduced to zero.
///
/// Gives a chance to clean up resources associated with the given account.
type OnFreeBalanceZero: OnFreeBalanceZero<Self::AccountId>;
/// Handler for when a new account is created.
type OnNewAccount: OnNewAccount<Self::AccountId>;
/// A function that returns true iff a given account can transfer its funds to another account.
type EnsureAccountLiquid: EnsureAccountLiquid<Self::AccountId>;
@@ -141,12 +131,12 @@ decl_module! {
/// Transfer some liquid free balance to another staker.
pub fn transfer(
origin,
dest: RawAddress<T::AccountId, T::AccountIndex>,
dest: <T::Lookup as StaticLookup>::Source,
value: <T::Balance as HasCompact>::Type
) {
let transactor = ensure_signed(origin)?;
let dest = Self::lookup(dest)?;
let dest = T::Lookup::lookup(dest)?;
let value = value.into();
let from_balance = Self::free_balance(&transactor);
let to_balance = Self::free_balance(&dest);
@@ -183,11 +173,11 @@ decl_module! {
/// Set the balances of a given account.
fn set_balance(
who: RawAddress<T::AccountId, T::AccountIndex>,
who: <T::Lookup as StaticLookup>::Source,
free: <T::Balance as HasCompact>::Type,
reserved: <T::Balance as HasCompact>::Type
) {
let who = Self::lookup(who)?;
let who = T::Lookup::lookup(who)?;
Self::set_free_balance(&who, free.into());
Self::set_reserved_balance(&who, reserved.into());
}
@@ -197,11 +187,10 @@ decl_module! {
decl_event!(
pub enum Event<T> where
<T as system::Trait>::AccountId,
<T as Trait>::AccountIndex,
<T as Trait>::Balance
{
/// A new account was created.
NewAccount(AccountId, AccountIndex, NewAccountOutcome),
NewAccount(AccountId, Balance),
/// An account was reaped.
ReapedAccount(AccountId),
/// Transfer succeeded (from, to, value, fees).
@@ -217,20 +206,11 @@ decl_storage! {
}): T::Balance;
/// The minimum amount allowed to keep an account open.
pub ExistentialDeposit get(existential_deposit) config(): T::Balance;
/// The amount credited to a destination's account whose index was reclaimed.
pub ReclaimRebate get(reclaim_rebate) config(): T::Balance;
/// The fee required to make a transfer.
pub TransferFee get(transfer_fee) config(): T::Balance;
/// The fee required to create an account. At least as big as ReclaimRebate.
pub CreationFee get(creation_fee) config(): T::Balance;
/// The next free enumeration set.
pub NextEnumSet get(next_enum_set) build(|config: &GenesisConfig<T>| {
T::AccountIndex::sa(config.balances.len() / ENUM_SET_SIZE)
}): T::AccountIndex;
/// The enumeration sets.
pub EnumSet get(enum_set): map T::AccountIndex => Vec<T::AccountId>;
/// The 'free' balance of a given account.
///
/// This is the only balance that matters in terms of most operations on tokens. It is
@@ -268,25 +248,9 @@ decl_storage! {
}
add_extra_genesis {
config(balances): Vec<(T::AccountId, T::Balance)>;
build(|storage: &mut primitives::StorageMap, _: &mut primitives::ChildrenStorageMap, config: &GenesisConfig<T>| {
let ids: Vec<_> = config.balances.iter().map(|x| x.0.clone()).collect();
for i in 0..(ids.len() + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE {
storage.insert(GenesisConfig::<T>::hash(&<EnumSet<T>>::key_for(T::AccountIndex::sa(i))).to_vec(),
ids[i * ENUM_SET_SIZE..ids.len().min((i + 1) * ENUM_SET_SIZE)].to_owned().encode());
}
});
}
}
/// Whatever happened about the hint given when creating the new account.
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Encode, Decode, PartialEq, Eq, Clone, Copy)]
pub enum NewAccountOutcome {
NoHint,
GoodHint,
BadHint,
}
/// Outcome of a balance update.
pub enum UpdateBalanceOutcome {
/// Account balance was simply updated.
@@ -319,30 +283,6 @@ 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).as_();
set.get(i).map(|x| x.clone())
}
/// `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 i = (try_index % enum_set_size).as_();
i < try_set.len() && Self::total_balance(&try_set[i]).is_zero()
}
/// Lookup an address to get an Id, if there's one there.
pub fn lookup_address(a: address::Address<T::AccountId, T::AccountIndex>) -> Option<T::AccountId> {
match a {
address::Address::Id(i) => Some(i),
address::Address::Index(i) => Self::lookup_index(i),
}
}
//PUBLIC MUTABLES (DANGEROUS)
/// Set the free balance of an account to some new value.
@@ -399,22 +339,14 @@ impl<T: Trait> Module<T> {
// account is reaped).
// NOTE: This is orthogonal to the `Bondage` value that an account has, a high
// value of which makes even the `free_balance` unspendable.
// TODO: enforce this for the other balance-altering functions.
if balance < ed {
Self::set_free_balance(who, balance);
UpdateBalanceOutcome::AccountKilled
} else {
if !<FreeBalance<T>>::exists(who) {
let outcome = Self::new_account(&who, balance);
let credit = match outcome {
NewAccountOutcome::GoodHint => balance + <Module<T>>::reclaim_rebate(),
_ => balance,
};
Self::set_free_balance(who, credit);
Self::increase_total_stake_by(credit - balance);
} else {
Self::set_free_balance(who, balance);
Self::new_account(&who, balance);
}
Self::set_free_balance(who, balance);
UpdateBalanceOutcome::Updated
}
@@ -550,82 +482,10 @@ impl<T: Trait> Module<T> {
}
}
fn enum_set_size() -> T::AccountIndex {
T::AccountIndex::sa(ENUM_SET_SIZE)
}
/// Register a new account (with existential balance).
fn new_account(who: &T::AccountId, balance: T::Balance) -> NewAccountOutcome {
let enum_set_size = Self::enum_set_size();
let next_set_index = Self::next_enum_set();
let reclaim_index_magic = T::AccountIndex::sa(RECLAIM_INDEX_MAGIC);
let reclaim_index_modulus = T::AccountIndex::sa(256usize);
let quantization = T::AccountIndex::sa(256usize);
// A little easter-egg for reclaiming dead indexes..
let ret = {
// we quantise the number of accounts so it stays constant over a reasonable
// period of time.
let quantized_account_count: T::AccountIndex = (next_set_index * enum_set_size / quantization + One::one()) * quantization;
// then modify the starting balance to be modulo this to allow it to potentially
// identify an account index for reuse.
let maybe_try_index = balance % <T::Balance as As<T::AccountIndex>>::sa(quantized_account_count * reclaim_index_modulus);
let maybe_try_index = As::<T::AccountIndex>::as_(maybe_try_index);
// this identifier must end with magic byte 0x69 to trigger this check (a minor
// optimisation to ensure we don't check most unintended account creations).
if maybe_try_index % reclaim_index_modulus == reclaim_index_magic {
// reuse is probably intended. first, remove magic byte.
let try_index = maybe_try_index / reclaim_index_modulus;
// then check to see if this balance identifies a dead account index.
let set_index = try_index / enum_set_size;
let mut try_set = Self::enum_set(set_index);
let item_index = (try_index % enum_set_size).as_();
if item_index < try_set.len() {
if Self::total_balance(&try_set[item_index]).is_zero() {
// yup - this index refers to a dead account. can be reused.
try_set[item_index] = who.clone();
<EnumSet<T>>::insert(set_index, try_set);
Self::deposit_event(RawEvent::NewAccount(who.clone(), try_index, NewAccountOutcome::GoodHint));
return NewAccountOutcome::GoodHint
}
}
NewAccountOutcome::BadHint
} else {
NewAccountOutcome::NoHint
}
};
// 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 {
break set;
}
set_index += One::one();
};
let index = T::AccountIndex::sa(set_index.as_() * ENUM_SET_SIZE + set.len());
// update set.
set.push(who.clone());
// keep NextEnumSet up to date
if set.len() == ENUM_SET_SIZE {
<NextEnumSet<T>>::put(set_index + One::one());
}
// write set.
<EnumSet<T>>::insert(set_index, set);
Self::deposit_event(RawEvent::NewAccount(who.clone(), index, ret));
ret
fn new_account(who: &T::AccountId, balance: T::Balance) {
T::OnNewAccount::on_new_account(&who);
Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone()));
}
fn reap_account(who: &T::AccountId) {
@@ -667,43 +527,6 @@ impl<T: Trait> Module<T> {
<TotalIssuance<T>>::put(v);
}
}
pub fn lookup(a: address::Address<T::AccountId, T::AccountIndex>) -> result::Result<T::AccountId, &'static str> {
match a {
address::Address::Id(i) => Ok(i),
address::Address::Index(i) => <Module<T>>::lookup_index(i).ok_or("invalid account index"),
}
}
}
pub struct ChainContext<T>(::rstd::marker::PhantomData<T>);
impl<T> Default for ChainContext<T> {
fn default() -> Self {
ChainContext(::rstd::marker::PhantomData)
}
}
impl<T: Trait> Lookup for ChainContext<T> {
type Source = address::Address<T::AccountId, T::AccountIndex>;
type Target = T::AccountId;
fn lookup(&self, a: Self::Source) -> result::Result<Self::Target, &'static str> {
<Module<T>>::lookup(a)
}
}
impl<T: Trait> CurrentHeight for ChainContext<T> {
type BlockNumber = T::BlockNumber;
fn current_height(&self) -> Self::BlockNumber {
<system::Module<T>>::block_number()
}
}
impl<T: Trait> BlockNumberToHash for ChainContext<T> {
type BlockNumber = T::BlockNumber;
type Hash = T::Hash;
fn block_number_to_hash(&self, n: Self::BlockNumber) -> Option<Self::Hash> {
Some(<system::Module<T>>::block_hash(n))
}
}
impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
@@ -718,3 +541,9 @@ impl<T: Trait> MakePayment<T::AccountId> for Module<T> {
Ok(())
}
}
impl<T: Trait> IsDeadAccount<T::AccountId> for Module<T> {
fn is_dead_account(who: &T::AccountId) -> bool {
Self::total_balance(who).is_zero()
}
}
+3 -3
View File
@@ -19,7 +19,7 @@
#![cfg(test)]
use primitives::BuildStorage;
use primitives::testing::{Digest, DigestItem, Header};
use primitives::{traits::{IdentityLookup}, testing::{Digest, DigestItem, Header}};
use substrate_primitives::{H256, Blake2Hasher};
use runtime_io;
use {GenesisConfig, Module, Trait, system};
@@ -39,14 +39,15 @@ impl system::Trait for Runtime {
type Hashing = ::primitives::traits::BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type Event = ();
type Log = DigestItem;
}
impl Trait for Runtime {
type Balance = u64;
type AccountIndex = u64;
type OnFreeBalanceZero = ();
type OnNewAccount = ();
type EnsureAccountLiquid = ();
type Event = ();
}
@@ -103,7 +104,6 @@ impl ExtBuilder {
existential_deposit: self.existential_deposit,
transfer_fee: self.transfer_fee,
creation_fee: self.creation_fee,
reclaim_rebate: 0,
}.build_storage().unwrap().0);
t.into()
}
+56 -183
View File
@@ -22,67 +22,6 @@ use super::*;
use mock::{Balances, ExtBuilder, Runtime, System};
use runtime_io::with_externalities;
#[test]
fn reward_should_work() {
with_externalities(&mut ExtBuilder::default().monied(true).build(), || {
assert_eq!(Balances::total_balance(&1), 10);
assert_ok!(Balances::reward(&1, 10));
assert_eq!(Balances::total_balance(&1), 20);
assert_eq!(<TotalIssuance<Runtime>>::get(), 110);
});
}
#[test]
fn indexing_lookup_should_work() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(10)
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(0), Some(1));
assert_eq!(Balances::lookup_index(1), Some(2));
assert_eq!(Balances::lookup_index(2), Some(3));
assert_eq!(Balances::lookup_index(3), Some(4));
assert_eq!(Balances::lookup_index(4), None);
},
);
}
#[test]
fn default_indexing_on_new_accounts_should_work() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(10)
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(4), None);
assert_ok!(Balances::transfer(Some(1).into(), 5.into(), 10.into()));
assert_eq!(Balances::lookup_index(4), Some(5));
},
);
}
#[test]
fn default_indexing_on_new_accounts_should_work2() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(10)
.creation_fee(50)
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(4), None);
// account 1 has 256 * 10 = 2560, account 5 is not exist, ext_deposit is 10, value is 10
assert_ok!(Balances::transfer(Some(1).into(), 5.into(), 10.into()));
assert_eq!(Balances::lookup_index(4), Some(5));
assert_eq!(Balances::free_balance(&1), 256 * 10 - 10 - 50); // 10 is value, 50 is creation_free
},
);
}
#[test]
fn default_indexing_on_new_accounts_should_not_work2() {
with_externalities(
@@ -92,18 +31,64 @@ fn default_indexing_on_new_accounts_should_not_work2() {
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(4), None);
assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist
// account 1 has 256 * 10 = 2560, account 5 is not exist, ext_deposit is 10, value is 9, not satisfies for ext_deposit
assert_noop!(
Balances::transfer(Some(1).into(), 5.into(), 9.into()),
Balances::transfer(Some(1).into(), 5, 9.into()),
"value too low to create account"
);
assert_eq!(Balances::lookup_index(4), None); // account 5 should not exist
assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist
assert_eq!(Balances::free_balance(&1), 256 * 10);
},
);
}
#[test]
fn reserved_balance_should_prevent_reclaim_count() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(256 * 1)
.monied(true)
.build(),
|| {
System::inc_account_nonce(&2);
assert_eq!(Balances::is_dead_account(&2), false);
assert_eq!(Balances::is_dead_account(&5), true);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved
assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted."
assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists.
assert_eq!(Balances::is_dead_account(&2), false);
assert_eq!(System::account_nonce(&2), 1);
assert_ok!(Balances::transfer(Some(4).into(), 5, (256 * 1 + 0x69).into())); // account 4 tries to take index 1 for account 5.
assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69);
assert_eq!(Balances::is_dead_account(&5), false);
assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed
assert_eq!(Balances::total_balance(&2), 0); // "reserve" account reduced to 255 (below ED) so account deleted
assert_eq!(System::account_nonce(&2), 0); // nonce zero
assert_eq!(Balances::is_dead_account(&2), true);
assert_ok!(Balances::transfer(Some(4).into(), 6, (256 * 1 + 0x69).into())); // account 4 tries to take index 1 again for account 6.
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
assert_eq!(Balances::is_dead_account(&6), false);
},
);
}
#[test]
fn reward_should_work() {
with_externalities(&mut ExtBuilder::default().monied(true).build(), || {
assert_eq!(Balances::total_balance(&1), 10);
assert_ok!(Balances::reward(&1, 10));
assert_eq!(Balances::total_balance(&1), 20);
assert_eq!(<TotalIssuance<Runtime>>::get(), 110);
});
}
#[test]
fn dust_account_removal_should_work() {
with_externalities(
@@ -116,7 +101,7 @@ fn dust_account_removal_should_work() {
assert_eq!(System::account_nonce(&2), 1);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), (256 * 10 + 1).into())); // index 1 (account 2) becomes zombie
assert_ok!(Balances::transfer(Some(2).into(), 5, (256 * 10 + 1).into())); // index 1 (account 2) becomes zombie
assert_eq!(Balances::total_balance(&2), 0);
assert_eq!(Balances::total_balance(&5), 256 * 10 + 1);
assert_eq!(System::account_nonce(&2), 0);
@@ -136,7 +121,7 @@ fn dust_account_removal_should_work2() {
System::inc_account_nonce(&2);
assert_eq!(System::account_nonce(&2), 1);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), (256 * 10).into())); // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit)
assert_ok!(Balances::transfer(Some(2).into(), 5, (256 * 10).into())); // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit)
assert_eq!(Balances::total_balance(&2), 0);
assert_eq!(Balances::total_balance(&5), 256 * 10);
assert_eq!(System::account_nonce(&2), 0);
@@ -144,118 +129,6 @@ fn dust_account_removal_should_work2() {
);
}
#[test]
fn reclaim_indexing_on_new_accounts_should_work() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(256 * 1)
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(1), Some(2));
assert_eq!(Balances::lookup_index(4), None);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), (256 * 20).into())); // account 2 becomes zombie freeing index 1 for reclaim)
assert_eq!(Balances::total_balance(&2), 0);
assert_ok!(Balances::transfer(Some(5).into(), 6.into(), (256 * 1 + 0x69).into())); // account 6 takes index 1.
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(6));
},
);
}
#[test]
fn reclaim_indexing_on_new_accounts_should_work2() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(256 * 1)
.monied(true)
.build(),
|| {
assert_eq!(Balances::lookup_index(1), Some(2));
assert_eq!(Balances::lookup_index(4), None);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::transfer(Some(2).into(), 5.into(), (256 * 20 - 50).into())); // account 2 becomes zombie freeing index 1 for reclaim) 50 is creation fee
assert_eq!(Balances::total_balance(&2), 0);
assert_ok!(Balances::transfer(Some(5).into(), 6.into(), (256 * 1 + 0x69).into())); // account 6 takes index 1.
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(6));
},
);
}
#[test]
fn reserved_balance_should_prevent_reclaim_count() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(256 * 1)
.monied(true)
.build(),
|| {
System::inc_account_nonce(&2);
assert_eq!(Balances::lookup_index(1), Some(2));
assert_eq!(Balances::lookup_index(4), None);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved
assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted."
assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists.
assert_eq!(System::account_nonce(&2), 1);
assert_ok!(Balances::transfer(Some(4).into(), 5.into(), (256 * 1 + 0x69).into())); // account 4 tries to take index 1 for account 5.
assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(2)); // but fails.
assert_eq!(System::account_nonce(&2), 1);
assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed
assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted."
assert_eq!(System::account_nonce(&2), 0);
assert_ok!(Balances::transfer(Some(4).into(), 6.into(), (256 * 1 + 0x69).into())); // account 4 tries to take index 1 again for account 6.
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds.
},
);
}
#[test]
fn reserved_balance_should_prevent_reclaim_count2() {
with_externalities(
&mut ExtBuilder::default()
.existential_deposit(256 * 1)
.monied(true)
.build(),
|| {
System::inc_account_nonce(&2);
assert_eq!(Balances::lookup_index(1), Some(2));
assert_eq!(Balances::lookup_index(4), None);
assert_eq!(Balances::total_balance(&2), 256 * 20);
assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved
assert_eq!(Balances::free_balance(&2), 0); // "free" account deleted."
assert_eq!(Balances::total_balance(&2), 256 * 19 + 1); // reserve still exists.
assert_eq!(System::account_nonce(&2), 1);
assert_ok!(Balances::transfer(Some(4).into(), 5.into(), (256 * 1 + 0x69).into())); // account 4 tries to take index 1 for account 5.
assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(2)); // but fails.
assert_eq!(System::account_nonce(&2), 1);
assert_eq!(Balances::slash(&2, 256 * 18 + 2), None); // account 2 gets slashed
assert_eq!(Balances::total_balance(&2), 0); // "free" account deleted."
assert_eq!(System::account_nonce(&2), 0);
assert_ok!(Balances::transfer(Some(4).into(), 6.into(), (256 * 1 + 0x69).into())); // account 4 tries to take index 1 again for account 6.
assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69);
assert_eq!(Balances::lookup_index(1), Some(6)); // and succeeds.
},
);
}
#[test]
fn balance_works() {
with_externalities(&mut ExtBuilder::default().build(), || {
@@ -274,7 +147,7 @@ fn balance_transfer_works() {
with_externalities(&mut ExtBuilder::default().build(), || {
Balances::set_free_balance(&1, 111);
Balances::increase_total_stake_by(111);
assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 69.into()));
assert_ok!(Balances::transfer(Some(1).into(), 2, 69.into()));
assert_eq!(Balances::total_balance(&1), 42);
assert_eq!(Balances::total_balance(&2), 69);
});
@@ -313,7 +186,7 @@ fn balance_transfer_when_reserved_should_not_work() {
with_externalities(&mut ExtBuilder::default().build(), || {
Balances::set_free_balance(&1, 111);
assert_ok!(Balances::reserve(&1, 69));
assert_noop!(Balances::transfer(Some(1).into(), 2.into(), 69.into()), "balance too low to send value");
assert_noop!(Balances::transfer(Some(1).into(), 2, 69.into()), "balance too low to send value");
});
}
@@ -444,7 +317,7 @@ fn transferring_too_high_value_should_not_panic() {
<FreeBalance<Runtime>>::insert(2, 1);
assert_err!(
Balances::transfer(Some(1).into(), 2.into(), u64::max_value().into()),
Balances::transfer(Some(1).into(), 2, u64::max_value().into()),
"destination balance too high to receive value"
);
@@ -472,7 +345,7 @@ fn account_removal_on_free_too_low() {
// Transfer funds from account 1 of such amount that after this transfer
// the balance of account 1 will be below the exsistential threshold.
// This should lead to the removal of all balance of this account.
assert_ok!(Balances::transfer(Some(1).into(), 2.into(), 20.into()));
assert_ok!(Balances::transfer(Some(1).into(), 2, 20.into()));
// Verify free balance removal of account 1.
assert_eq!(Balances::free_balance(&1), 0);
@@ -492,7 +365,7 @@ fn transfer_overflow_isnt_exploitable() {
let evil_value = u64::max_value() - 49;
assert_err!(
Balances::transfer(Some(1).into(), 5.into(), evil_value.into()),
Balances::transfer(Some(1).into(), 5, evil_value.into()),
"got overflow after adding a fee to value"
);
}