[FRAME] Introduce force_adjust_total_issuance (#3001)

Add `Balances::force_adjust_total_issuance` as preparation for fixing
https://github.com/polkadot-fellows/runtimes/issues/147.
Important changes in `substrate/frame/balances/src/lib.rs`.

TODO:
- [x] Update weights

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
Co-authored-by: command-bot <>
Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
Oliver Tale-Yazdi
2024-01-30 23:39:47 +01:00
committed by GitHub
parent 4220503d28
commit 5a6f6d33d3
24 changed files with 930 additions and 581 deletions
@@ -286,6 +286,17 @@ mod benchmarks {
}
}
#[benchmark]
fn force_adjust_total_issuance() {
let ti = Balances::<T, I>::total_issuance();
let delta = 123u32.into();
#[extrinsic_call]
_(RawOrigin::Root, AdjustmentDirection::Increase, delta);
assert_eq!(Balances::<T, I>::total_issuance(), ti + delta);
}
impl_benchmark_test_suite! {
Balances,
crate::tests::ExtBuilder::default().build(),
+39 -1
View File
@@ -190,7 +190,8 @@ use sp_runtime::{
};
use sp_std::{cmp, fmt::Debug, mem, prelude::*, result};
pub use types::{
AccountData, BalanceLock, DustCleaner, ExtraFlags, IdAmount, Reasons, ReserveData,
AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, IdAmount, Reasons,
ReserveData,
};
pub use weights::WeightInfo;
@@ -384,6 +385,8 @@ pub mod pallet {
Frozen { who: T::AccountId, amount: T::Balance },
/// Some balance was thawed.
Thawed { who: T::AccountId, amount: T::Balance },
/// The `TotalIssuance` was forcefully changed.
TotalIssuanceForced { old: T::Balance, new: T::Balance },
}
#[pallet::error]
@@ -408,6 +411,10 @@ pub mod pallet {
TooManyHolds,
/// Number of freezes exceed `MaxFreezes`.
TooManyFreezes,
/// The issuance cannot be modified since it is already deactivated.
IssuanceDeactivated,
/// The delta cannot be zero.
DeltaZero,
}
/// The total units issued in the system.
@@ -743,6 +750,37 @@ pub mod pallet {
Self::deposit_event(Event::BalanceSet { who, free: new_free });
Ok(())
}
/// Adjust the total issuance in a saturating way.
///
/// Can only be called by root and always needs a positive `delta`.
///
/// # Example
#[doc = docify::embed!("./src/tests/dispatchable_tests.rs", force_adjust_total_issuance_example)]
#[pallet::call_index(9)]
#[pallet::weight(T::WeightInfo::force_adjust_total_issuance())]
pub fn force_adjust_total_issuance(
origin: OriginFor<T>,
direction: AdjustmentDirection,
#[pallet::compact] delta: T::Balance,
) -> DispatchResult {
ensure_root(origin)?;
ensure!(delta > Zero::zero(), Error::<T, I>::DeltaZero);
let old = TotalIssuance::<T, I>::get();
let new = match direction {
AdjustmentDirection::Increase => old.saturating_add(delta),
AdjustmentDirection::Decrease => old.saturating_sub(delta),
};
ensure!(InactiveIssuance::<T, I>::get() <= new, Error::<T, I>::IssuanceDeactivated);
TotalIssuance::<T, I>::set(new);
Self::deposit_event(Event::<T, I>::TotalIssuanceForced { old, new });
Ok(())
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
@@ -18,9 +18,16 @@
//! Tests regarding the functionality of the dispatchables/extrinsics.
use super::*;
use frame_support::traits::tokens::Preservation::Expendable;
use crate::{
AdjustmentDirection::{Decrease as Dec, Increase as Inc},
Event,
};
use frame_support::traits::{fungible::Unbalanced, tokens::Preservation::Expendable};
use fungible::{hold::Mutate as HoldMutate, Inspect, Mutate};
/// Alice account ID for more readable tests.
const ALICE: u64 = 1;
#[test]
fn default_indexing_on_new_accounts_should_not_work2() {
ExtBuilder::default()
@@ -222,3 +229,109 @@ fn upgrade_accounts_should_work() {
assert_eq!(System::consumers(&7), 0);
});
}
#[test]
#[docify::export]
fn force_adjust_total_issuance_example() {
ExtBuilder::default().build_and_execute_with(|| {
// First we set the TotalIssuance to 64 by giving Alice a balance of 64.
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), ALICE, 64));
let old_ti = Balances::total_issuance();
assert_eq!(old_ti, 64, "TI should be 64");
// Now test the increase:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32));
let new_ti = Balances::total_issuance();
assert_eq!(old_ti + 32, new_ti, "Should increase by 32");
// If Alice tries to call it, it errors:
assert_noop!(
Balances::force_adjust_total_issuance(RawOrigin::Signed(ALICE).into(), Inc, 32),
BadOrigin,
);
});
}
#[test]
fn force_adjust_total_issuance_works() {
ExtBuilder::default().build_and_execute_with(|| {
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64));
let ti = Balances::total_issuance();
// Increase works:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 32));
assert_eq!(Balances::total_issuance(), ti + 32);
System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced {
old: 64,
new: 96,
}));
// Decrease works:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 64));
assert_eq!(Balances::total_issuance(), ti - 32);
System::assert_last_event(RuntimeEvent::Balances(Event::TotalIssuanceForced {
old: 96,
new: 32,
}));
});
}
#[test]
fn force_adjust_total_issuance_saturates() {
ExtBuilder::default().build_and_execute_with(|| {
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64));
let ti = Balances::total_issuance();
let max = Balance::max_value();
assert_eq!(ti, 64);
// Increment saturates:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, max));
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 123));
assert_eq!(Balances::total_issuance(), max);
// Decrement saturates:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, max));
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 123));
assert_eq!(Balances::total_issuance(), 0);
});
}
#[test]
fn force_adjust_total_issuance_rejects_zero_delta() {
ExtBuilder::default().build_and_execute_with(|| {
assert_noop!(
Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 0),
Error::<Test>::DeltaZero,
);
assert_noop!(
Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 0),
Error::<Test>::DeltaZero,
);
});
}
#[test]
fn force_adjust_total_issuance_rejects_more_than_inactive() {
ExtBuilder::default().build_and_execute_with(|| {
assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 1337, 64));
Balances::deactivate(16u32.into());
assert_eq!(Balances::total_issuance(), 64);
assert_eq!(Balances::active_issuance(), 48);
// Works with up to 48:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 40),);
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 8),);
assert_eq!(Balances::total_issuance(), 16);
assert_eq!(Balances::active_issuance(), 0);
// Errors with more than 48:
assert_noop!(
Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Dec, 1),
Error::<Test>::IssuanceDeactivated,
);
// Increasing again increases the inactive issuance:
assert_ok!(Balances::force_adjust_total_issuance(RawOrigin::Root.into(), Inc, 10),);
assert_eq!(Balances::total_issuance(), 26);
assert_eq!(Balances::active_issuance(), 10);
});
}
+3 -1
View File
@@ -126,8 +126,10 @@ impl pallet_transaction_payment::Config for Test {
type FeeMultiplierUpdate = ();
}
pub(crate) type Balance = u64;
impl Config for Test {
type Balance = u64;
type Balance = Balance;
type DustRemoval = DustTrap;
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ExistentialDeposit;
+9
View File
@@ -152,3 +152,12 @@ impl<T: Config<I>, I: 'static> Drop for DustCleaner<T, I> {
}
}
}
/// Whether something should be interpreted as an increase or a decrease.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
pub enum AdjustmentDirection {
/// Increase the amount.
Increase,
/// Decrease the amount.
Decrease,
}
+62 -47
View File
@@ -15,16 +15,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Autogenerated weights for pallet_balances
//! Autogenerated weights for `pallet_balances`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-07-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2024-01-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-o7yfgx5n-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
//! HOSTNAME: `runner-j8vvqcjr-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
// Executed Command:
// target/production/substrate
// target/production/substrate-node
// benchmark
// pallet
// --steps=50
@@ -32,12 +32,12 @@
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json
// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
// --pallet=pallet_balances
// --chain=dev
// --header=./HEADER-APACHE2
// --output=./frame/balances/src/weights.rs
// --template=./.maintain/frame-weight-template.hbs
// --header=./substrate/HEADER-APACHE2
// --output=./substrate/frame/balances/src/weights.rs
// --template=./substrate/.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
@@ -47,7 +47,7 @@
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for pallet_balances.
/// Weight functions needed for `pallet_balances`.
pub trait WeightInfo {
fn transfer_allow_death() -> Weight;
fn transfer_keep_alive() -> Weight;
@@ -57,9 +57,10 @@ pub trait WeightInfo {
fn transfer_all() -> Weight;
fn force_unreserve() -> Weight;
fn upgrade_accounts(u: u32, ) -> Weight;
fn force_adjust_total_issuance() -> Weight;
}
/// Weights for pallet_balances using the Substrate node and recommended hardware.
/// Weights for `pallet_balances` using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: `System::Account` (r:1 w:1)
@@ -68,8 +69,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 58_474_000 picoseconds.
Weight::from_parts(59_117_000, 3593)
// Minimum execution time: 46_329_000 picoseconds.
Weight::from_parts(47_297_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -79,8 +80,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 44_629_000 picoseconds.
Weight::from_parts(45_798_000, 3593)
// Minimum execution time: 36_187_000 picoseconds.
Weight::from_parts(36_900_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -90,8 +91,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 16_483_000 picoseconds.
Weight::from_parts(16_939_000, 3593)
// Minimum execution time: 13_498_000 picoseconds.
Weight::from_parts(14_143_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -101,8 +102,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 24_638_000 picoseconds.
Weight::from_parts(25_487_000, 3593)
// Minimum execution time: 18_756_000 picoseconds.
Weight::from_parts(19_553_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -112,8 +113,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `103`
// Estimated: `6196`
// Minimum execution time: 60_041_000 picoseconds.
Weight::from_parts(63_365_000, 6196)
// Minimum execution time: 47_826_000 picoseconds.
Weight::from_parts(48_834_000, 6196)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -123,8 +124,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 54_445_000 picoseconds.
Weight::from_parts(55_623_000, 3593)
// Minimum execution time: 44_621_000 picoseconds.
Weight::from_parts(45_151_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -134,8 +135,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 19_309_000 picoseconds.
Weight::from_parts(19_953_000, 3593)
// Minimum execution time: 16_194_000 picoseconds.
Weight::from_parts(16_945_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -146,14 +147,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `0 + u * (135 ±0)`
// Estimated: `990 + u * (2603 ±0)`
// Minimum execution time: 19_362_000 picoseconds.
Weight::from_parts(19_612_000, 990)
// Standard Error: 13_108
.saturating_add(Weight::from_parts(16_444_591, 0).saturating_mul(u.into()))
// Minimum execution time: 15_782_000 picoseconds.
Weight::from_parts(16_118_000, 990)
// Standard Error: 10_499
.saturating_add(Weight::from_parts(13_327_660, 0).saturating_mul(u.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into())))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into())))
.saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into()))
}
fn force_adjust_total_issuance() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 6_157_000 picoseconds.
Weight::from_parts(6_507_000, 0)
}
}
// For backwards compatibility and tests.
@@ -164,8 +172,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 58_474_000 picoseconds.
Weight::from_parts(59_117_000, 3593)
// Minimum execution time: 46_329_000 picoseconds.
Weight::from_parts(47_297_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -175,8 +183,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 44_629_000 picoseconds.
Weight::from_parts(45_798_000, 3593)
// Minimum execution time: 36_187_000 picoseconds.
Weight::from_parts(36_900_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -186,8 +194,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 16_483_000 picoseconds.
Weight::from_parts(16_939_000, 3593)
// Minimum execution time: 13_498_000 picoseconds.
Weight::from_parts(14_143_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -197,8 +205,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 24_638_000 picoseconds.
Weight::from_parts(25_487_000, 3593)
// Minimum execution time: 18_756_000 picoseconds.
Weight::from_parts(19_553_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -208,8 +216,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `103`
// Estimated: `6196`
// Minimum execution time: 60_041_000 picoseconds.
Weight::from_parts(63_365_000, 6196)
// Minimum execution time: 47_826_000 picoseconds.
Weight::from_parts(48_834_000, 6196)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -219,8 +227,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3593`
// Minimum execution time: 54_445_000 picoseconds.
Weight::from_parts(55_623_000, 3593)
// Minimum execution time: 44_621_000 picoseconds.
Weight::from_parts(45_151_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -230,8 +238,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `174`
// Estimated: `3593`
// Minimum execution time: 19_309_000 picoseconds.
Weight::from_parts(19_953_000, 3593)
// Minimum execution time: 16_194_000 picoseconds.
Weight::from_parts(16_945_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -242,12 +250,19 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0 + u * (135 ±0)`
// Estimated: `990 + u * (2603 ±0)`
// Minimum execution time: 19_362_000 picoseconds.
Weight::from_parts(19_612_000, 990)
// Standard Error: 13_108
.saturating_add(Weight::from_parts(16_444_591, 0).saturating_mul(u.into()))
// Minimum execution time: 15_782_000 picoseconds.
Weight::from_parts(16_118_000, 990)
// Standard Error: 10_499
.saturating_add(Weight::from_parts(13_327_660, 0).saturating_mul(u.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into())))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into())))
.saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into()))
}
fn force_adjust_total_issuance() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 6_157_000 picoseconds.
Weight::from_parts(6_507_000, 0)
}
}