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

Commit 9c5fc1c

Browse files
shawntabriziParity Benchmarking Bot
andauthored
Better Handle Dead Accounts in Balances (#7843)
* Don't mutate storage when account is dead and should stay dead * cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_balances --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/balances/src/weights.rs --template=./.maintain/frame-weight-template.hbs * more concrete storage noop Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
1 parent c9d9365 commit 9c5fc1c

File tree

4 files changed

+59
-30
lines changed

4 files changed

+59
-30
lines changed

frame/balances/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ impl<T: Config<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
982982
/// Slash a target account `who`, returning the negative imbalance created and any left over
983983
/// amount that could not be slashed.
984984
///
985-
/// Is a no-op if `value` to be slashed is zero.
985+
/// Is a no-op if `value` to be slashed is zero or the account does not exist.
986986
///
987987
/// NOTE: `slash()` prefers free balance, but assumes that reserve balance can be drawn
988988
/// from in extreme circumstances. `can_slash()` should be used prior to `slash()` to avoid having
@@ -993,6 +993,7 @@ impl<T: Config<I>, I: Instance> Currency<T::AccountId> for Module<T, I> where
993993
value: Self::Balance
994994
) -> (Self::NegativeImbalance, Self::Balance) {
995995
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
996+
if Self::is_dead_account(&who) { return (NegativeImbalance::zero(), value) }
996997

997998
Self::mutate_account(who, |account| {
998999
let free_slash = cmp::min(account.free, value);
@@ -1146,9 +1147,10 @@ impl<T: Config<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I
11461147

11471148
/// Unreserve some funds, returning any amount that was unable to be unreserved.
11481149
///
1149-
/// Is a no-op if the value to be unreserved is zero.
1150+
/// Is a no-op if the value to be unreserved is zero or the account does not exist.
11501151
fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
11511152
if value.is_zero() { return Zero::zero() }
1153+
if Self::is_dead_account(&who) { return value }
11521154

11531155
let actual = Self::mutate_account(who, |account| {
11541156
let actual = cmp::min(account.reserved, value);
@@ -1166,12 +1168,13 @@ impl<T: Config<I>, I: Instance> ReservableCurrency<T::AccountId> for Module<T, I
11661168
/// Slash from reserved balance, returning the negative imbalance created,
11671169
/// and any amount that was unable to be slashed.
11681170
///
1169-
/// Is a no-op if the value to be slashed is zero.
1171+
/// Is a no-op if the value to be slashed is zero or the account does not exist.
11701172
fn slash_reserved(
11711173
who: &T::AccountId,
11721174
value: Self::Balance
11731175
) -> (Self::NegativeImbalance, Self::Balance) {
11741176
if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) }
1177+
if Self::is_dead_account(&who) { return (NegativeImbalance::zero(), value) }
11751178

11761179
Self::mutate_account(who, |account| {
11771180
// underflow should never happen, but it if does, there's nothing to be done here.

frame/balances/src/tests.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ macro_rules! decl_tests {
4040
use crate::*;
4141
use sp_runtime::{FixedPointNumber, traits::{SignedExtension, BadOrigin}};
4242
use frame_support::{
43-
assert_noop, assert_ok, assert_err,
43+
assert_noop, assert_storage_noop, assert_ok, assert_err,
4444
traits::{
4545
LockableCurrency, LockIdentifier, WithdrawReasons,
4646
Currency, ReservableCurrency, ExistenceRequirement::AllowDeath, StoredMap
@@ -796,5 +796,28 @@ macro_rules! decl_tests {
796796
);
797797
});
798798
}
799+
800+
#[test]
801+
fn operations_on_dead_account_should_not_change_state() {
802+
// These functions all use `mutate_account` which may introduce a storage change when
803+
// the account never existed to begin with, and shouldn't exist in the end.
804+
<$ext_builder>::default()
805+
.existential_deposit(0)
806+
.build()
807+
.execute_with(|| {
808+
assert!(!<Test as Config>::AccountStore::is_explicit(&1337));
809+
810+
// Unreserve
811+
assert_storage_noop!(assert_eq!(Balances::unreserve(&1337, 42), 42));
812+
// Reserve
813+
assert_noop!(Balances::reserve(&1337, 42), Error::<Test, _>::InsufficientBalance);
814+
// Slash Reserve
815+
assert_storage_noop!(assert_eq!(Balances::slash_reserved(&1337, 42).1, 42));
816+
// Repatriate Reserve
817+
assert_noop!(Balances::repatriate_reserved(&1337, &1338, 42, Status::Free), Error::<Test, _>::DeadAccount);
818+
// Slash
819+
assert_storage_noop!(assert_eq!(Balances::slash(&1337, 42).1, 42));
820+
});
821+
}
799822
}
800823
}

frame/balances/src/weights.rs

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This file is part of Substrate.
22

3-
// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
3+
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
44
// SPDX-License-Identifier: Apache-2.0
55

66
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,9 +15,10 @@
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
1717

18-
//! Weights for pallet_balances
18+
//! Autogenerated weights for pallet_balances
19+
//!
1920
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
20-
//! DATE: 2020-10-27, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
21+
//! DATE: 2021-01-06, STEPS: [50, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
2122
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
2223
2324
// Executed Command:
@@ -48,76 +49,63 @@ pub trait WeightInfo {
4849
fn set_balance_creating() -> Weight;
4950
fn set_balance_killing() -> Weight;
5051
fn force_transfer() -> Weight;
51-
5252
}
5353

5454
/// Weights for pallet_balances using the Substrate node and recommended hardware.
5555
pub struct SubstrateWeight<T>(PhantomData<T>);
5656
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
5757
fn transfer() -> Weight {
58-
(94_088_000 as Weight)
58+
(100_698_000 as Weight)
5959
.saturating_add(T::DbWeight::get().reads(1 as Weight))
6060
.saturating_add(T::DbWeight::get().writes(1 as Weight))
61-
6261
}
6362
fn transfer_keep_alive() -> Weight {
64-
(64_828_000 as Weight)
63+
(69_407_000 as Weight)
6564
.saturating_add(T::DbWeight::get().reads(1 as Weight))
6665
.saturating_add(T::DbWeight::get().writes(1 as Weight))
67-
6866
}
6967
fn set_balance_creating() -> Weight {
70-
(36_151_000 as Weight)
68+
(38_489_000 as Weight)
7169
.saturating_add(T::DbWeight::get().reads(1 as Weight))
7270
.saturating_add(T::DbWeight::get().writes(1 as Weight))
73-
7471
}
7572
fn set_balance_killing() -> Weight {
76-
(45_505_000 as Weight)
73+
(48_458_000 as Weight)
7774
.saturating_add(T::DbWeight::get().reads(1 as Weight))
7875
.saturating_add(T::DbWeight::get().writes(1 as Weight))
79-
8076
}
8177
fn force_transfer() -> Weight {
82-
(92_986_000 as Weight)
78+
(99_320_000 as Weight)
8379
.saturating_add(T::DbWeight::get().reads(2 as Weight))
8480
.saturating_add(T::DbWeight::get().writes(2 as Weight))
85-
8681
}
87-
8882
}
8983

9084
// For backwards compatibility and tests
9185
impl WeightInfo for () {
9286
fn transfer() -> Weight {
93-
(94_088_000 as Weight)
87+
(100_698_000 as Weight)
9488
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
9589
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
96-
9790
}
9891
fn transfer_keep_alive() -> Weight {
99-
(64_828_000 as Weight)
92+
(69_407_000 as Weight)
10093
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
10194
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
102-
10395
}
10496
fn set_balance_creating() -> Weight {
105-
(36_151_000 as Weight)
97+
(38_489_000 as Weight)
10698
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
10799
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
108-
109100
}
110101
fn set_balance_killing() -> Weight {
111-
(45_505_000 as Weight)
102+
(48_458_000 as Weight)
112103
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
113104
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
114-
115105
}
116106
fn force_transfer() -> Weight {
117-
(92_986_000 as Weight)
107+
(99_320_000 as Weight)
118108
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
119109
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
120-
121110
}
122-
123111
}

frame/support/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,21 @@ macro_rules! assert_noop {
499499
}
500500
}
501501

502+
/// Evaluate any expression and assert that runtime storage has not been mutated
503+
/// (i.e. expression is a storage no-operation).
504+
///
505+
/// Used as `assert_storage_noop(expression_to_assert)`.
506+
#[macro_export]
507+
macro_rules! assert_storage_noop {
508+
(
509+
$x:expr
510+
) => {
511+
let h = $crate::storage_root();
512+
$x;
513+
assert_eq!(h, $crate::storage_root());
514+
}
515+
}
516+
502517
/// Assert an expression returns an error specified.
503518
///
504519
/// Used as `assert_err!(expression_to_assert, expected_error_expression)`

0 commit comments

Comments
 (0)