nomination-pools: add permissionless condition to chill (#3453)

Currently, pool member funds cannot be unbonded if the depositor's stake
is less than `MinNominatorBond`. This usually happens after
`T::MinNominatorBond` is increased.

To fix this, the above mentioned condition is added as a case for
permissionless dispatch of `chill`. After pool is chilled, pool members
can unbond their funds since pool won't be nominating anymore.

Consequently, same check is added to `nominate` call, i.e pool can not
start nominating if it's depositor does not have `MinNominatorBond`

cc @Ank4n @kianenigma 

closes #2350

Polkadot address: 16FqwPZ8GRC5U5D4Fu7W33nA55ZXzXGWHwmbnE1eT6pxuqcT

---------

Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com>
Co-authored-by: Gonçalo Pestana <g6pestana@gmail.com>
Co-authored-by: command-bot <>
This commit is contained in:
Dastan
2024-03-07 11:36:30 +01:00
committed by GitHub
parent c16fcc47bc
commit 11831df8e7
5 changed files with 367 additions and 128 deletions
+13
View File
@@ -0,0 +1,13 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
title: "[pallet-nomination-pools]: `chill` is permissionless if depositor's stake is less than `min_nominator_bond`"
doc:
- audience: Runtime Dev
description: |
Nomination pools currently have an issue whereby member funds cannot be unbonded if the depositor bonded amount is less than `MinNominatorBond`.
This PR makes the `chill` function permissionless if this condition is met.
Consequently, `nominate` function also checks for `depositor` to have at least `MinNominatorBond`, and returns `MinimumBondNotMet` error if not.
crates:
- name: pallet-nomination-pools
+37 -2
View File
@@ -2401,6 +2401,11 @@ pub mod pallet {
///
/// This directly forward the call to the staking pallet, on behalf of the pool bonded
/// account.
///
/// # Note
///
/// In addition to a `root` or `nominator` role of `origin`, pool's depositor needs to have
/// at least `depositor_min_bond` in the pool to start nominating.
#[pallet::call_index(8)]
#[pallet::weight(T::WeightInfo::nominate(validators.len() as u32))]
pub fn nominate(
@@ -2411,6 +2416,16 @@ pub mod pallet {
let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
let depositor_points = PoolMembers::<T>::get(&bonded_pool.roles.depositor)
.ok_or(Error::<T>::PoolMemberNotFound)?
.active_points();
ensure!(
bonded_pool.points_to_balance(depositor_points) >= Self::depositor_min_bond(),
Error::<T>::MinimumBondNotMet
);
T::Staking::nominate(&bonded_pool.bonded_account(), validators)
}
@@ -2573,17 +2588,37 @@ pub mod pallet {
/// Chill on behalf of the pool.
///
/// The dispatch origin of this call must be signed by the pool nominator or the pool
/// The dispatch origin of this call can be signed by the pool nominator or the pool
/// root role, same as [`Pallet::nominate`].
///
/// Under certain conditions, this call can be dispatched permissionlessly (i.e. by any
/// account).
///
/// # Conditions for a permissionless dispatch:
/// * When pool depositor has less than `MinNominatorBond` staked, otherwise pool members
/// are unable to unbond.
///
/// # Conditions for permissioned dispatch:
/// * The caller has a nominator or root role of the pool.
/// This directly forward the call to the staking pallet, on behalf of the pool bonded
/// account.
#[pallet::call_index(13)]
#[pallet::weight(T::WeightInfo::chill())]
pub fn chill(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
let who = ensure_signed(origin)?;
let bonded_pool = BondedPool::<T>::get(pool_id).ok_or(Error::<T>::PoolNotFound)?;
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
let depositor_points = PoolMembers::<T>::get(&bonded_pool.roles.depositor)
.ok_or(Error::<T>::PoolMemberNotFound)?
.active_points();
if bonded_pool.points_to_balance(depositor_points) >=
T::Staking::minimum_nominator_bond()
{
ensure!(bonded_pool.can_nominate(&who), Error::<T>::NotNominator);
}
T::Staking::chill(&bonded_pool.bonded_account())
}
@@ -4848,6 +4848,18 @@ mod nominate {
Error::<Runtime>::NotNominator
);
// if `depositor` stake is less than the `MinimumNominatorBond`, they can't nominate
StakingMinBond::set(20);
// Can't nominate if depositor's stake is less than the `MinimumNominatorBond`
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21]),
Error::<Runtime>::MinimumBondNotMet
);
// restore `MinimumNominatorBond`
StakingMinBond::set(10);
// Root can nominate
assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21]));
assert_eq!(Nominations::get().unwrap(), vec![21]);
@@ -7338,3 +7350,33 @@ mod slash {
});
}
}
mod chill {
use super::*;
#[test]
fn chill_works() {
ExtBuilder::default().build_and_execute(|| {
// only nominator or root can chill
assert_noop!(
Pools::chill(RuntimeOrigin::signed(10), 1),
Error::<Runtime>::NotNominator
);
// root can chill and re-nominate
assert_ok!(Pools::chill(RuntimeOrigin::signed(900), 1));
assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![31]));
// nominator can chill and re-nominate
assert_ok!(Pools::chill(RuntimeOrigin::signed(901), 1));
assert_ok!(Pools::nominate(RuntimeOrigin::signed(901), 1, vec![31]));
// if `depositor` stake is less than the `MinimumNominatorBond`, then this call
// becomes permissionless;
StakingMinBond::set(20);
// any account can chill
assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1));
})
}
}
+145 -123
View File
@@ -18,27 +18,25 @@
//! Autogenerated weights for `pallet_nomination_pools`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2024-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2024-03-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-bn-ce5rx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! HOSTNAME: `runner-p5qp1txx-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-node
// target/production/substrate-node
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_nomination_pools
// --no-storage-info
// --no-median-slopes
// --no-min-squares
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./substrate/frame/nomination-pools/src/weights.rs
// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
// --pallet=pallet_nomination_pools
// --chain=dev
// --header=./substrate/HEADER-APACHE2
// --output=./substrate/frame/nomination-pools/src/weights.rs
// --template=./substrate/.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
@@ -114,8 +112,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `3425`
// Estimated: `8877`
// Minimum execution time: 181_861_000 picoseconds.
Weight::from_parts(186_375_000, 8877)
// Minimum execution time: 182_643_000 picoseconds.
Weight::from_parts(186_106_000, 8877)
.saturating_add(T::DbWeight::get().reads(20_u64))
.saturating_add(T::DbWeight::get().writes(13_u64))
}
@@ -147,8 +145,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `3435`
// Estimated: `8877`
// Minimum execution time: 182_273_000 picoseconds.
Weight::from_parts(186_635_000, 8877)
// Minimum execution time: 181_464_000 picoseconds.
Weight::from_parts(184_672_000, 8877)
.saturating_add(T::DbWeight::get().reads(17_u64))
.saturating_add(T::DbWeight::get().writes(13_u64))
}
@@ -182,8 +180,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `3500`
// Estimated: `8877`
// Minimum execution time: 217_878_000 picoseconds.
Weight::from_parts(221_493_000, 8877)
// Minimum execution time: 216_182_000 picoseconds.
Weight::from_parts(218_448_000, 8877)
.saturating_add(T::DbWeight::get().reads(18_u64))
.saturating_add(T::DbWeight::get().writes(14_u64))
}
@@ -203,8 +201,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `1172`
// Estimated: `3719`
// Minimum execution time: 74_509_000 picoseconds.
Weight::from_parts(76_683_000, 3719)
// Minimum execution time: 73_830_000 picoseconds.
Weight::from_parts(75_271_000, 3719)
.saturating_add(T::DbWeight::get().reads(6_u64))
.saturating_add(T::DbWeight::get().writes(4_u64))
}
@@ -244,8 +242,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `3622`
// Estimated: `27847`
// Minimum execution time: 166_424_000 picoseconds.
Weight::from_parts(169_698_000, 27847)
// Minimum execution time: 166_099_000 picoseconds.
Weight::from_parts(170_355_000, 27847)
.saturating_add(T::DbWeight::get().reads(20_u64))
.saturating_add(T::DbWeight::get().writes(13_u64))
}
@@ -270,10 +268,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `1848`
// Estimated: `4764`
// Minimum execution time: 66_110_000 picoseconds.
Weight::from_parts(68_620_141, 4764)
// Standard Error: 1_379
.saturating_add(Weight::from_parts(54_961, 0).saturating_mul(s.into()))
// Minimum execution time: 64_787_000 picoseconds.
Weight::from_parts(67_920_914, 4764)
// Standard Error: 1_482
.saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into()))
.saturating_add(T::DbWeight::get().reads(8_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
@@ -308,10 +306,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `2238`
// Estimated: `27847`
// Minimum execution time: 125_669_000 picoseconds.
Weight::from_parts(130_907_641, 27847)
// Standard Error: 2_490
.saturating_add(Weight::from_parts(75_219, 0).saturating_mul(s.into()))
// Minimum execution time: 124_990_000 picoseconds.
Weight::from_parts(129_041_398, 27847)
// Standard Error: 2_335
.saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into()))
.saturating_add(T::DbWeight::get().reads(12_u64))
.saturating_add(T::DbWeight::get().writes(9_u64))
}
@@ -362,12 +360,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Storage: `NominationPools::ClaimPermissions` (r:0 w:1)
/// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`)
/// The range of component `s` is `[0, 100]`.
fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
fn withdraw_unbonded_kill(s: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `2525`
// Estimated: `27847`
// Minimum execution time: 225_700_000 picoseconds.
Weight::from_parts(234_390_990, 27847)
// Minimum execution time: 221_955_000 picoseconds.
Weight::from_parts(230_244_437, 27847)
// Standard Error: 4_059
.saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into()))
.saturating_add(T::DbWeight::get().reads(24_u64))
.saturating_add(T::DbWeight::get().writes(20_u64))
}
@@ -419,19 +419,25 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `1169`
// Estimated: `8538`
// Minimum execution time: 167_171_000 picoseconds.
Weight::from_parts(170_531_000, 8538)
// Minimum execution time: 165_358_000 picoseconds.
Weight::from_parts(169_683_000, 8538)
.saturating_add(T::DbWeight::get().reads(23_u64))
.saturating_add(T::DbWeight::get().writes(17_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:0)
/// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::PoolMembers` (r:1 w:0)
/// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`)
/// Storage: `Staking::Bonded` (r:1 w:0)
/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::Ledger` (r:1 w:0)
/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
/// Storage: `Staking::MinNominatorBond` (r:1 w:0)
/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::MinCreateBond` (r:1 w:0)
/// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::MinJoinBond` (r:1 w:0)
/// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `Staking::Nominators` (r:1 w:1)
/// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`)
/// Storage: `Staking::MaxNominatorsCount` (r:1 w:0)
@@ -451,13 +457,13 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// The range of component `n` is `[1, 16]`.
fn nominate(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `1808`
// Measured: `1976`
// Estimated: `4556 + n * (2520 ±0)`
// Minimum execution time: 63_785_000 picoseconds.
Weight::from_parts(64_592_302, 4556)
// Standard Error: 9_416
.saturating_add(Weight::from_parts(1_524_398, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(12_u64))
// Minimum execution time: 74_483_000 picoseconds.
Weight::from_parts(76_611_288, 4556)
// Standard Error: 9_013
.saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(15_u64))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(T::DbWeight::get().writes(5_u64))
.saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into()))
@@ -472,8 +478,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `1434`
// Estimated: `4556`
// Minimum execution time: 32_096_000 picoseconds.
Weight::from_parts(33_533_000, 4556)
// Minimum execution time: 32_598_000 picoseconds.
Weight::from_parts(33_350_000, 4556)
.saturating_add(T::DbWeight::get().reads(3_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -488,10 +494,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3735`
// Minimum execution time: 13_914_000 picoseconds.
Weight::from_parts(14_800_402, 3735)
// Standard Error: 146
.saturating_add(Weight::from_parts(940, 0).saturating_mul(n.into()))
// Minimum execution time: 13_705_000 picoseconds.
Weight::from_parts(14_594_211, 3735)
// Standard Error: 141
.saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(3_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -511,8 +517,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_373_000 picoseconds.
Weight::from_parts(4_592_000, 0)
// Minimum execution time: 4_222_000 picoseconds.
Weight::from_parts(4_527_000, 0)
.saturating_add(T::DbWeight::get().writes(6_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:1)
@@ -521,17 +527,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_662_000 picoseconds.
Weight::from_parts(17_531_000, 3719)
// Minimum execution time: 16_106_000 picoseconds.
Weight::from_parts(17_365_000, 3719)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:0)
/// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::PoolMembers` (r:1 w:0)
/// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`)
/// Storage: `Staking::Bonded` (r:1 w:0)
/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::Ledger` (r:1 w:0)
/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
/// Storage: `Staking::MinNominatorBond` (r:1 w:0)
/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `Staking::Validators` (r:1 w:0)
/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -546,11 +556,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn chill() -> Weight {
// Proof Size summary in bytes:
// Measured: `1971`
// Measured: `2143`
// Estimated: `4556`
// Minimum execution time: 61_348_000 picoseconds.
Weight::from_parts(63_712_000, 4556)
.saturating_add(T::DbWeight::get().reads(9_u64))
// Minimum execution time: 71_301_000 picoseconds.
Weight::from_parts(73_626_000, 4556)
.saturating_add(T::DbWeight::get().reads(11_u64))
.saturating_add(T::DbWeight::get().writes(5_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:1)
@@ -565,8 +575,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `804`
// Estimated: `3719`
// Minimum execution time: 32_232_000 picoseconds.
Weight::from_parts(33_433_000, 3719)
// Minimum execution time: 32_363_000 picoseconds.
Weight::from_parts(33_400_000, 3719)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -578,8 +588,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `572`
// Estimated: `3719`
// Minimum execution time: 16_774_000 picoseconds.
Weight::from_parts(17_671_000, 3719)
// Minimum execution time: 16_686_000 picoseconds.
Weight::from_parts(17_294_000, 3719)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -589,8 +599,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_724_000 picoseconds.
Weight::from_parts(17_181_000, 3719)
// Minimum execution time: 16_417_000 picoseconds.
Weight::from_parts(17_028_000, 3719)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -600,8 +610,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_362_000 picoseconds.
Weight::from_parts(17_135_000, 3719)
// Minimum execution time: 16_350_000 picoseconds.
Weight::from_parts(16_905_000, 3719)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -613,8 +623,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `542`
// Estimated: `3702`
// Minimum execution time: 14_125_000 picoseconds.
Weight::from_parts(14_705_000, 3702)
// Minimum execution time: 14_081_000 picoseconds.
Weight::from_parts(14_608_000, 3702)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
@@ -630,8 +640,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `1002`
// Estimated: `3719`
// Minimum execution time: 61_699_000 picoseconds.
Weight::from_parts(63_605_000, 3719)
// Minimum execution time: 62_178_000 picoseconds.
Weight::from_parts(64_039_000, 3719)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -647,8 +657,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `901`
// Estimated: `4764`
// Minimum execution time: 64_930_000 picoseconds.
Weight::from_parts(66_068_000, 4764)
// Minimum execution time: 64_880_000 picoseconds.
Weight::from_parts(66_541_000, 4764)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
@@ -692,8 +702,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `3425`
// Estimated: `8877`
// Minimum execution time: 181_861_000 picoseconds.
Weight::from_parts(186_375_000, 8877)
// Minimum execution time: 182_643_000 picoseconds.
Weight::from_parts(186_106_000, 8877)
.saturating_add(RocksDbWeight::get().reads(20_u64))
.saturating_add(RocksDbWeight::get().writes(13_u64))
}
@@ -725,8 +735,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `3435`
// Estimated: `8877`
// Minimum execution time: 182_273_000 picoseconds.
Weight::from_parts(186_635_000, 8877)
// Minimum execution time: 181_464_000 picoseconds.
Weight::from_parts(184_672_000, 8877)
.saturating_add(RocksDbWeight::get().reads(17_u64))
.saturating_add(RocksDbWeight::get().writes(13_u64))
}
@@ -760,8 +770,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `3500`
// Estimated: `8877`
// Minimum execution time: 217_878_000 picoseconds.
Weight::from_parts(221_493_000, 8877)
// Minimum execution time: 216_182_000 picoseconds.
Weight::from_parts(218_448_000, 8877)
.saturating_add(RocksDbWeight::get().reads(18_u64))
.saturating_add(RocksDbWeight::get().writes(14_u64))
}
@@ -781,8 +791,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `1172`
// Estimated: `3719`
// Minimum execution time: 74_509_000 picoseconds.
Weight::from_parts(76_683_000, 3719)
// Minimum execution time: 73_830_000 picoseconds.
Weight::from_parts(75_271_000, 3719)
.saturating_add(RocksDbWeight::get().reads(6_u64))
.saturating_add(RocksDbWeight::get().writes(4_u64))
}
@@ -822,8 +832,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `3622`
// Estimated: `27847`
// Minimum execution time: 166_424_000 picoseconds.
Weight::from_parts(169_698_000, 27847)
// Minimum execution time: 166_099_000 picoseconds.
Weight::from_parts(170_355_000, 27847)
.saturating_add(RocksDbWeight::get().reads(20_u64))
.saturating_add(RocksDbWeight::get().writes(13_u64))
}
@@ -848,10 +858,10 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `1848`
// Estimated: `4764`
// Minimum execution time: 66_110_000 picoseconds.
Weight::from_parts(68_620_141, 4764)
// Standard Error: 1_379
.saturating_add(Weight::from_parts(54_961, 0).saturating_mul(s.into()))
// Minimum execution time: 64_787_000 picoseconds.
Weight::from_parts(67_920_914, 4764)
// Standard Error: 1_482
.saturating_add(Weight::from_parts(43_264, 0).saturating_mul(s.into()))
.saturating_add(RocksDbWeight::get().reads(8_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
@@ -886,10 +896,10 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `2238`
// Estimated: `27847`
// Minimum execution time: 125_669_000 picoseconds.
Weight::from_parts(130_907_641, 27847)
// Standard Error: 2_490
.saturating_add(Weight::from_parts(75_219, 0).saturating_mul(s.into()))
// Minimum execution time: 124_990_000 picoseconds.
Weight::from_parts(129_041_398, 27847)
// Standard Error: 2_335
.saturating_add(Weight::from_parts(67_889, 0).saturating_mul(s.into()))
.saturating_add(RocksDbWeight::get().reads(12_u64))
.saturating_add(RocksDbWeight::get().writes(9_u64))
}
@@ -940,12 +950,14 @@ impl WeightInfo for () {
/// Storage: `NominationPools::ClaimPermissions` (r:0 w:1)
/// Proof: `NominationPools::ClaimPermissions` (`max_values`: None, `max_size`: Some(41), added: 2516, mode: `MaxEncodedLen`)
/// The range of component `s` is `[0, 100]`.
fn withdraw_unbonded_kill(_s: u32, ) -> Weight {
fn withdraw_unbonded_kill(s: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `2525`
// Estimated: `27847`
// Minimum execution time: 225_700_000 picoseconds.
Weight::from_parts(234_390_990, 27847)
// Minimum execution time: 221_955_000 picoseconds.
Weight::from_parts(230_244_437, 27847)
// Standard Error: 4_059
.saturating_add(Weight::from_parts(7_522, 0).saturating_mul(s.into()))
.saturating_add(RocksDbWeight::get().reads(24_u64))
.saturating_add(RocksDbWeight::get().writes(20_u64))
}
@@ -997,19 +1009,25 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `1169`
// Estimated: `8538`
// Minimum execution time: 167_171_000 picoseconds.
Weight::from_parts(170_531_000, 8538)
// Minimum execution time: 165_358_000 picoseconds.
Weight::from_parts(169_683_000, 8538)
.saturating_add(RocksDbWeight::get().reads(23_u64))
.saturating_add(RocksDbWeight::get().writes(17_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:0)
/// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::PoolMembers` (r:1 w:0)
/// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`)
/// Storage: `Staking::Bonded` (r:1 w:0)
/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::Ledger` (r:1 w:0)
/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
/// Storage: `Staking::MinNominatorBond` (r:1 w:0)
/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::MinCreateBond` (r:1 w:0)
/// Proof: `NominationPools::MinCreateBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::MinJoinBond` (r:1 w:0)
/// Proof: `NominationPools::MinJoinBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `Staking::Nominators` (r:1 w:1)
/// Proof: `Staking::Nominators` (`max_values`: None, `max_size`: Some(558), added: 3033, mode: `MaxEncodedLen`)
/// Storage: `Staking::MaxNominatorsCount` (r:1 w:0)
@@ -1029,13 +1047,13 @@ impl WeightInfo for () {
/// The range of component `n` is `[1, 16]`.
fn nominate(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `1808`
// Measured: `1976`
// Estimated: `4556 + n * (2520 ±0)`
// Minimum execution time: 63_785_000 picoseconds.
Weight::from_parts(64_592_302, 4556)
// Standard Error: 9_416
.saturating_add(Weight::from_parts(1_524_398, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(12_u64))
// Minimum execution time: 74_483_000 picoseconds.
Weight::from_parts(76_611_288, 4556)
// Standard Error: 9_013
.saturating_add(Weight::from_parts(1_475_128, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(15_u64))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
.saturating_add(RocksDbWeight::get().writes(5_u64))
.saturating_add(Weight::from_parts(0, 2520).saturating_mul(n.into()))
@@ -1050,8 +1068,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `1434`
// Estimated: `4556`
// Minimum execution time: 32_096_000 picoseconds.
Weight::from_parts(33_533_000, 4556)
// Minimum execution time: 32_598_000 picoseconds.
Weight::from_parts(33_350_000, 4556)
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -1066,10 +1084,10 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3735`
// Minimum execution time: 13_914_000 picoseconds.
Weight::from_parts(14_800_402, 3735)
// Standard Error: 146
.saturating_add(Weight::from_parts(940, 0).saturating_mul(n.into()))
// Minimum execution time: 13_705_000 picoseconds.
Weight::from_parts(14_594_211, 3735)
// Standard Error: 141
.saturating_add(Weight::from_parts(2_119, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(3_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -1089,8 +1107,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_373_000 picoseconds.
Weight::from_parts(4_592_000, 0)
// Minimum execution time: 4_222_000 picoseconds.
Weight::from_parts(4_527_000, 0)
.saturating_add(RocksDbWeight::get().writes(6_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:1)
@@ -1099,17 +1117,21 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_662_000 picoseconds.
Weight::from_parts(17_531_000, 3719)
// Minimum execution time: 16_106_000 picoseconds.
Weight::from_parts(17_365_000, 3719)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:0)
/// Proof: `NominationPools::BondedPools` (`max_values`: None, `max_size`: Some(254), added: 2729, mode: `MaxEncodedLen`)
/// Storage: `NominationPools::PoolMembers` (r:1 w:0)
/// Proof: `NominationPools::PoolMembers` (`max_values`: None, `max_size`: Some(237), added: 2712, mode: `MaxEncodedLen`)
/// Storage: `Staking::Bonded` (r:1 w:0)
/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::Ledger` (r:1 w:0)
/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
/// Storage: `Staking::MinNominatorBond` (r:1 w:0)
/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
/// Storage: `Staking::Validators` (r:1 w:0)
/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -1124,11 +1146,11 @@ impl WeightInfo for () {
/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn chill() -> Weight {
// Proof Size summary in bytes:
// Measured: `1971`
// Measured: `2143`
// Estimated: `4556`
// Minimum execution time: 61_348_000 picoseconds.
Weight::from_parts(63_712_000, 4556)
.saturating_add(RocksDbWeight::get().reads(9_u64))
// Minimum execution time: 71_301_000 picoseconds.
Weight::from_parts(73_626_000, 4556)
.saturating_add(RocksDbWeight::get().reads(11_u64))
.saturating_add(RocksDbWeight::get().writes(5_u64))
}
/// Storage: `NominationPools::BondedPools` (r:1 w:1)
@@ -1143,8 +1165,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `804`
// Estimated: `3719`
// Minimum execution time: 32_232_000 picoseconds.
Weight::from_parts(33_433_000, 3719)
// Minimum execution time: 32_363_000 picoseconds.
Weight::from_parts(33_400_000, 3719)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -1156,8 +1178,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `572`
// Estimated: `3719`
// Minimum execution time: 16_774_000 picoseconds.
Weight::from_parts(17_671_000, 3719)
// Minimum execution time: 16_686_000 picoseconds.
Weight::from_parts(17_294_000, 3719)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -1167,8 +1189,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_724_000 picoseconds.
Weight::from_parts(17_181_000, 3719)
// Minimum execution time: 16_417_000 picoseconds.
Weight::from_parts(17_028_000, 3719)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -1178,8 +1200,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `532`
// Estimated: `3719`
// Minimum execution time: 16_362_000 picoseconds.
Weight::from_parts(17_135_000, 3719)
// Minimum execution time: 16_350_000 picoseconds.
Weight::from_parts(16_905_000, 3719)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -1191,8 +1213,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `542`
// Estimated: `3702`
// Minimum execution time: 14_125_000 picoseconds.
Weight::from_parts(14_705_000, 3702)
// Minimum execution time: 14_081_000 picoseconds.
Weight::from_parts(14_608_000, 3702)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
@@ -1208,8 +1230,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `1002`
// Estimated: `3719`
// Minimum execution time: 61_699_000 picoseconds.
Weight::from_parts(63_605_000, 3719)
// Minimum execution time: 62_178_000 picoseconds.
Weight::from_parts(64_039_000, 3719)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -1225,8 +1247,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `901`
// Estimated: `4764`
// Minimum execution time: 64_930_000 picoseconds.
Weight::from_parts(66_068_000, 4764)
// Minimum execution time: 64_880_000 picoseconds.
Weight::from_parts(66_541_000, 4764)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
@@ -22,10 +22,12 @@ mod mock;
use frame_support::{assert_noop, assert_ok, traits::Currency};
use mock::*;
use pallet_nomination_pools::{
BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember, PoolMembers,
PoolState,
BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember,
PoolMembers, PoolState,
};
use pallet_staking::{
CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination,
};
use pallet_staking::{CurrentEra, Event as StakingEvent, Payee, RewardDestination};
use sp_runtime::{bounded_btree_map, traits::Zero};
#[test]
@@ -191,6 +193,131 @@ fn pool_lifecycle_e2e() {
})
}
#[test]
fn pool_chill_e2e() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::minimum_balance(), 5);
assert_eq!(Staking::current_era(), None);
// create the pool, we know this has id 1.
assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
assert_eq!(LastPoolId::<Runtime>::get(), 1);
// have the pool nominate.
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
assert_eq!(
staking_events_since_last_call(),
vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Created { depositor: 10, pool_id: 1 },
PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
]
);
// have two members join
assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
]
);
assert_eq!(
pool_events_since_last_call(),
vec![
PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
]
);
// in case depositor does not have more than `MinNominatorBond` staked, we can end up in
// situation where a member unbonding would cause pool balance to drop below
// `MinNominatorBond` and hence not allowed. This can happen if the `MinNominatorBond` is
// increased after the pool is created.
assert_ok!(Staking::set_staking_configs(
RuntimeOrigin::root(),
pallet_staking::ConfigOp::Set(55), // minimum nominator bond
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
pallet_staking::ConfigOp::Noop,
));
// members can unbond as long as total stake of the pool is above min nominator bond
assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
// this member cannot unbond since it will cause `pool stake < MinNominatorBond`
assert_noop!(
Pools::unbond(RuntimeOrigin::signed(21), 21, 10),
StakingError::<Runtime>::InsufficientBond,
);
// members can call `chill` permissionlessly now
assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1));
// now another member can unbond.
assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
// nominator can not resume nomination until depositor have enough stake
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
PoolsError::<Runtime>::MinimumBondNotMet,
);
// other members joining pool does not affect the depositor's ability to resume nomination
assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1));
assert_noop!(
Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
PoolsError::<Runtime>::MinimumBondNotMet,
);
// depositor can bond extra stake
assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10)));
// `chill` can not be called permissionlessly anymore
assert_noop!(
Pools::chill(RuntimeOrigin::signed(20), 1),
PoolsError::<Runtime>::NotNominator,
);
// now nominator can resume nomination
assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
// skip to make the unbonding period end.
CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
// members can now withdraw.
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
assert_eq!(
staking_events_since_last_call(),
vec![
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Chilled { stash: POOL1_BONDED },
StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // other member bonding
StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // depositor bond extra
StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },
]
);
})
}
#[test]
fn pool_slash_e2e() {
new_test_ext().execute_with(|| {