Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Lazy reaping #4895

Merged
merged 24 commits into from
Feb 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/node-template/pallets/template/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl system::Trait for Test {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}
impl Trait for Test {
type Event = ();
Expand Down
2 changes: 1 addition & 1 deletion bin/node-template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl system::Trait for Runtime {
/// What to do if a new account is created.
type OnNewAccount = ();
/// What to do if an account is fully reaped from the system.
type OnReapAccount = Balances;
type OnKilledAccount = ();
/// The data to be stored in an account.
type AccountData = balances::AccountData<Balance>;
}
Expand Down
2 changes: 1 addition & 1 deletion bin/node/cli/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ pub fn testnet_genesis(
}),
pallet_session: Some(SessionConfig {
keys: initial_authorities.iter().map(|x| {
(x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()))
(x.0.clone(), x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()))
}).collect::<Vec<_>>(),
}),
pallet_staking: Some(StakingConfig {
Expand Down
2 changes: 1 addition & 1 deletion bin/node/cli/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ pub fn run_command_for_a_while(base_path: &Path, dev: bool) {

// Stop the process
kill(Pid::from_raw(cmd.id().try_into().unwrap()), SIGINT).unwrap();
assert!(wait_for(&mut cmd, 20).map(|x| x.success()).unwrap_or_default());
assert!(wait_for(&mut cmd, 40).map(|x| x.success()).unwrap_or_default());
}
10 changes: 5 additions & 5 deletions bin/node/executor/tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn panic_execution_with_foreign_code_gives_error() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(69u128, 0u128, 0u128, 0u128).encode()
(69u128, 0u8, 0u128, 0u128, 0u128).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
Expand Down Expand Up @@ -200,7 +200,7 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(0u32, 69u128, 0u128, 0u128, 0u128).encode()
(0u32, 0u8, 69u128, 0u128, 0u128, 0u128).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
69_u128.encode()
Expand Down Expand Up @@ -236,7 +236,7 @@ fn successful_execution_with_native_equivalent_code_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
(0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
Expand Down Expand Up @@ -278,7 +278,7 @@ fn successful_execution_with_foreign_code_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(BLOATY_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
(0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
Expand Down Expand Up @@ -734,7 +734,7 @@ fn successful_execution_gives_ok() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
(0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(111 * DOLLARS).encode()
Expand Down
4 changes: 2 additions & 2 deletions bin/node/executor/tests/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ fn transaction_fee_is_correct_ultimate() {
let mut t = TestExternalities::<Blake2Hasher>::new_with_code(COMPACT_CODE, Storage {
top: map![
<frame_system::Account<Runtime>>::hashed_key_for(alice()) => {
(0u32, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode()
(0u32, 0u8, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode()
},
<frame_system::Account<Runtime>>::hashed_key_for(bob()) => {
(0u32, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode()
(0u32, 0u8, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode()
},
<pallet_balances::TotalIssuance<Runtime>>::hashed_key().to_vec() => {
(110 * DOLLARS).encode()
Expand Down
5 changes: 3 additions & 2 deletions bin/node/executor/tests/submit_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ fn submitted_transaction_should_be_valid() {
// add balance to the account
let author = extrinsic.signature.clone().unwrap().0;
let address = Indices::lookup(author).unwrap();
let account = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() };
<frame_system::Account<Runtime>>::insert(&address, (0u32, account));
let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() };
let account = frame_system::AccountInfo { nonce: 0u32, refcount: 0u8, data };
<frame_system::Account<Runtime>>::insert(&address, account);

// check validity
let res = Executive::validate_transaction(extrinsic);
Expand Down
6 changes: 3 additions & 3 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to 0. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 224,
impl_version: 2,
spec_version: 225,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
};

Expand Down Expand Up @@ -131,7 +131,7 @@ impl frame_system::Trait for Runtime {
type ModuleToIndex = ModuleToIndex;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnReapAccount = (Balances, Staking, Contracts, Session, Recovery);
type OnKilledAccount = ();
}

parameter_types! {
Expand Down
6 changes: 3 additions & 3 deletions bin/node/testing/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ pub fn config_endowed(
}),
pallet_session: Some(SessionConfig {
keys: vec![
(alice(), to_session_keys(
(dave(), alice(), to_session_keys(
&Ed25519Keyring::Alice,
&Sr25519Keyring::Alice,
)),
(bob(), to_session_keys(
(eve(), bob(), to_session_keys(
&Ed25519Keyring::Bob,
&Sr25519Keyring::Bob,
)),
(charlie(), to_session_keys(
(ferdie(), charlie(), to_session_keys(
&Ed25519Keyring::Charlie,
&Sr25519Keyring::Charlie,
)),
Expand Down
2 changes: 1 addition & 1 deletion frame/assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ mod tests {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}
impl Trait for Test {
type Event = ();
Expand Down
2 changes: 1 addition & 1 deletion frame/aura/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl frame_system::Trait for Test {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}

impl pallet_timestamp::Trait for Test {
Expand Down
2 changes: 1 addition & 1 deletion frame/authority-discovery/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ mod tests {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}

impl_outer_origin! {
Expand Down
2 changes: 1 addition & 1 deletion frame/authorship/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ mod tests {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}

parameter_types! {
Expand Down
2 changes: 1 addition & 1 deletion frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl frame_system::Trait for Test {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}

impl_opaque_keys! {
Expand Down
1 change: 0 additions & 1 deletion frame/balances/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ frame-support = { version = "2.0.0-dev", default-features = false, path = "../su
frame-system = { version = "2.0.0-dev", default-features = false, path = "../system" }

[dev-dependencies]
sp-io = { version = "2.0.0-dev", path = "../../primitives/io" }
sp-core = { version = "2.0.0-dev", path = "../../primitives/core" }
pallet-transaction-payment = { version = "2.0.0-dev", path = "../transaction-payment" }

Expand Down
51 changes: 40 additions & 11 deletions frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,12 @@ mod benchmarking;

use sp_std::prelude::*;
use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr, convert::Infallible};
use sp_io::hashing::twox_64;
use codec::{Codec, Encode, Decode};
use frame_support::{
StorageValue, Parameter, decl_event, decl_storage, decl_module, decl_error, ensure,
weights::SimpleDispatchInfo, traits::{
Currency, OnReapAccount, OnUnbalanced, TryDrop, StoredMap,
Currency, OnKilledAccount, OnUnbalanced, TryDrop, StoredMap,
WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement,
Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive,
ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status
Expand All @@ -178,7 +179,7 @@ use sp_runtime::{
};
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
get_storage_value, take_storage_value, put_storage_value, StorageIterator, have_storage_value
};

pub use self::imbalances::{PositiveImbalance, NegativeImbalance};
Expand Down Expand Up @@ -609,7 +610,16 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {

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));
let mut refs: system::RefCount = 0;
// The items in Kusama that would result in a ref count being incremented.
if have_storage_value(b"Democracy", b"Proxy", &hash) { refs += 1 }
// We skip Recovered since it's being replaced anyway.
let mut prefixed_hash = twox_64(&b":session:keys"[..]).to_vec();
prefixed_hash.extend(&b":session:keys"[..]);
prefixed_hash.extend(&hash[..]);
if have_storage_value(b"Session", b"NextKeys", &prefixed_hash) { refs += 1 }
if have_storage_value(b"Staking", b"Bonded", &hash) { refs += 1 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand that we don't have an easy and better choice here, but maybe we should document the rules of these references somewhere in a very strict way. The pattern in staking/session of inc_ref/dec_red reminds me of malloc/free and such, paradigms that we probably want to avoid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these the kind of rules would you be looking for?

If a module wants to "keep alive" an account until it is done with it, it should put a reference counter on it.

When it is done with it, it should remove that reference counter.

It should only happen once each way. (But I guess there is nothing that would stop a module from managing multiple reference counters in both directions.)

put_storage_value(b"System", b"Account", &hash, (nonce, refs, &balances));
}
}

Expand Down Expand Up @@ -721,14 +731,21 @@ impl<T: Trait<I>, I: Instance> Module<T, I> {
}
}
});
Locks::<T, I>::insert(who, locks);
}
}

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);
let existed = Locks::<T, I>::contains_key(who);
if locks.is_empty() {
Locks::<T, I>::remove(who);
if existed {
// TODO: use Locks::<T, I>::hashed_key
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/paritytech/substrate/issues/4969
system::Module::<T>::dec_ref(who);
}
} else {
Locks::<T, I>::insert(who, locks);
if !existed {
system::Module::<T>::inc_ref(who);
}
}
}
}

Expand Down Expand Up @@ -923,7 +940,7 @@ impl<T: Subtrait<I>, I: Instance> frame_system::Trait for ElevatedTrait<T, I> {
type Version = T::Version;
type ModuleToIndex = T::ModuleToIndex;
type OnNewAccount = T::OnNewAccount;
type OnReapAccount = T::OnReapAccount;
type OnKilledAccount = T::OnKilledAccount;
type AccountData = T::AccountData;
}
impl<T: Subtrait<I>, I: Instance> Trait<I> for ElevatedTrait<T, I> {
Expand Down Expand Up @@ -1040,6 +1057,7 @@ impl<T: Trait<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
)?;

let allow_death = existence_requirement == ExistenceRequirement::AllowDeath;
let allow_death = allow_death && system::Module::<T>::allow_death(transactor);
ensure!(allow_death || from_account.free >= ed, Error::<T, I>::KeepAlive);

Ok(())
Expand Down Expand Up @@ -1283,6 +1301,17 @@ impl<T: Trait<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I>
}
}

/// Implement `OnKilledAccount` to remove the local account, if using local account storage.
///
/// NOTE: You probably won't need to use this! This only needs to be "wired in" to System module
/// if you're using the local balance storage. **If you're using the composite system account
/// storage (which is the default in most examples and tests) then there's no need.**
impl<T: Trait<I>, I: Instance> OnKilledAccount<T::AccountId> for Module<T, I> {
fn on_killed_account(who: &T::AccountId) {
Account::<T, I>::remove(who);
}
}

impl<T: Trait<I>, I: Instance> LockableCurrency<T::AccountId> for Module<T, I>
where
T::Balance: MaybeSerializeDeserialize + Debug
Expand Down
15 changes: 13 additions & 2 deletions frame/balances/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ macro_rules! decl_tests {
use sp_runtime::{Fixed64, traits::{SignedExtension, BadOrigin}};
use frame_support::{
assert_noop, assert_ok, assert_err,
traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons,
Currency, ReservableCurrency, ExistenceRequirement::AllowDeath}
traits::{
LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons,
Currency, ReservableCurrency, ExistenceRequirement::AllowDeath, StoredMap
}
};
use pallet_transaction_payment::ChargeTransactionPayment;
use frame_system::RawOrigin;
Expand Down Expand Up @@ -55,6 +57,15 @@ macro_rules! decl_tests {
});
}

#[test]
fn account_should_be_reaped() {
<$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| {
assert_eq!(Balances::free_balance(1), 10);
assert_ok!(<Balances as Currency<_>>::transfer(&1, &2, 10, AllowDeath));
assert!(!<<Test as Trait>::AccountStore as StoredMap<u64, AccountData<u64>>>::is_explicit(&1));
});
}

#[test]
fn partial_locking_should_work() {
<$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| {
Expand Down
2 changes: 1 addition & 1 deletion frame/balances/src/tests_composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl frame_system::Trait for Test {
type ModuleToIndex = ();
type AccountData = super::AccountData<u64>;
type OnNewAccount = ();
type OnReapAccount = Module<Test>;
type OnKilledAccount = ();
}
parameter_types! {
pub const TransactionBaseFee: u64 = 0;
Expand Down
2 changes: 1 addition & 1 deletion frame/balances/src/tests_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl frame_system::Trait for Test {
type ModuleToIndex = ();
type AccountData = super::AccountData<u64>;
type OnNewAccount = ();
type OnReapAccount = Module<Test>;
type OnKilledAccount = Module<Test>;
}
parameter_types! {
pub const TransactionBaseFee: u64 = 0;
Expand Down
2 changes: 1 addition & 1 deletion frame/collective/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ mod tests {
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnReapAccount = ();
type OnKilledAccount = ();
}
impl Trait<Instance1> for Test {
type Origin = Origin;
Expand Down
2 changes: 1 addition & 1 deletion frame/contracts/src/account_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
let exists = !T::Currency::total_balance(&address).is_zero();
total_imbalance = total_imbalance.merge(imbalance);
if existed && !exists {
// Account killed. This will ultimately lead to calling `OnReapAccount` callback
// Account killed. This will ultimately lead to calling `OnKilledAccount` callback
// which will make removal of CodeHashOf and AccountStorage for this account.
// In order to avoid writing over the deleted properties we `continue` here.
continue;
Expand Down
10 changes: 7 additions & 3 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ use frame_support::{
parameter_types, IsSubType,
weights::DispatchInfo,
};
use frame_support::traits::{OnReapAccount, OnUnbalanced, Currency, Get, Time, Randomness};
use frame_support::traits::{OnKilledAccount, OnUnbalanced, Currency, Get, Time, Randomness};
use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root};
use sp_core::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX;
use pallet_contracts_primitives::{RentProjection, ContractAccessError};
Expand Down Expand Up @@ -941,8 +941,12 @@ decl_storage! {
}
}

impl<T: Trait> OnReapAccount<T::AccountId> for Module<T> {
fn on_reap_account(who: &T::AccountId) {
// TODO: this should be removed in favour of a self-destruct contract host function allowing the
// contract to delete all storage and the `ContractInfoOf` key and transfer remaining balance to
// some other account. As it stands, it's an economic insecurity on any smart-contract chain.
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/paritytech/substrate/issues/4952
impl<T: Trait> OnKilledAccount<T::AccountId> for Module<T> {
fn on_killed_account(who: &T::AccountId) {
if let Some(ContractInfo::Alive(info)) = <ContractInfoOf<T>>::take(who) {
child::kill_storage(&info.trie_id, info.child_trie_unique_id());
}
Expand Down
Loading