From 9599fcf4f0f10a70e1dcb496f868c061793aa56a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 11:57:26 +0100 Subject: [PATCH 001/221] add Commission to BondedPoolInner --- frame/nomination-pools/src/lib.rs | 49 +++++++-- frame/nomination-pools/src/migration.rs | 1 + frame/nomination-pools/src/mock.rs | 9 +- frame/nomination-pools/src/tests.rs | 128 ++++++++++++++++-------- 4 files changed, 133 insertions(+), 54 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 9e77adaeee677..68d441e1a4d67 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -286,7 +286,7 @@ use sp_runtime::{ AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero, }, - FixedPointNumber, FixedPointOperand, + FixedPointNumber, FixedPointOperand, Perbill, }; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; @@ -538,19 +538,41 @@ pub struct PoolRoles { pub state_toggler: Option, } +/// Pool commission. +/// +/// The pool depositor can set a commission upon a pool creation, from which the pool owner can +/// update thereafter. The `max` commission value can only be set once, as to prevent the commission +/// from repeatedly increasing. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] +pub struct Commission { + /// The active commission rate of the pool. + current: Perbill, + /// An optional maximum commission that can be set by the pool. Once set, this value cannot be + /// updated. + max: Option, +} + +impl Default for Commission { + fn default() -> Self { + Self { current: Perbill::zero(), max: Some(Perbill::zero()) } + } +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { - /// Total points of all the members in the pool who are actively bonded. - pub points: BalanceOf, - /// The current state of the pool. - pub state: PoolState, + /// The commission rate, if any, of the pool. + pub commission: Commission, /// Count of members that belong to the pool. pub member_counter: u32, + /// Total points of all the members in the pool who are actively bonded. + pub points: BalanceOf, /// See [`PoolRoles`]. pub roles: PoolRoles, + /// The current state of the pool. + pub state: PoolState, } /// A wrapper for bonded pools, with utility functions. @@ -581,14 +603,15 @@ impl sp_std::ops::DerefMut for BondedPool { impl BondedPool { /// Create a new bonded pool with the given roles and identifier. - fn new(id: PoolId, roles: PoolRoles) -> Self { + fn new(id: PoolId, commission: Commission, roles: PoolRoles) -> Self { Self { id, inner: BondedPoolInner { + commission, + member_counter: Zero::zero(), + points: Zero::zero(), roles, state: PoolState::Open, - points: Zero::zero(), - member_counter: Zero::zero(), }, } } @@ -1138,7 +1161,7 @@ pub mod pallet { use super::*; use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; - use sp_runtime::traits::CheckedAdd; + use sp_runtime::{traits::CheckedAdd, Perbill}; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); @@ -1451,6 +1474,8 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, + /// The pool commission has been misconfigured. + CommissionMisconfigured, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -1870,6 +1895,7 @@ pub mod pallet { pub fn create( origin: OriginFor, #[pallet::compact] amount: BalanceOf, + commission: Commission, root: AccountIdLookupOf, nominator: AccountIdLookupOf, state_toggler: AccountIdLookupOf, @@ -1886,6 +1912,10 @@ pub mod pallet { Error::::MaxPools ); ensure!(!PoolMembers::::contains_key(&who), Error::::AccountBelongsToOtherPool); + ensure!( + commission.max.unwrap_or(Perbill::zero()) >= commission.current, + Error::::CommissionMisconfigured + ); let pool_id = LastPoolId::::try_mutate::<_, Error, _>(|id| { *id = id.checked_add(1).ok_or(Error::::OverflowRisk)?; @@ -1893,6 +1923,7 @@ pub mod pallet { })?; let mut bonded_pool = BondedPool::::new( pool_id, + commission, PoolRoles { root: Some(root), nominator: Some(nominator), diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index b73141c95f72c..e2bf5de30a2d0 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -53,6 +53,7 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { BondedPoolInner { + commission: Commission::default(), member_counter: self.member_counter, points: self.points, state: self.state, diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 1b3372dae56ee..2c935b1c6761a 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -290,7 +290,14 @@ impl ExtBuilder { // make a pool let amount_to_bond = Pools::depositor_min_bond(); Balances::make_free_balance_be(&10, amount_to_bond * 5); - assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902)); + assert_ok!(Pools::create( + RawOrigin::Signed(10).into(), + amount_to_bond, + Commission::default(), + 900, + 901, + 902 + )); assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); let last_pool = LastPoolId::::get(); for (account_id, bonded) in self.members { diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5074a7ffa695a..6676fbaab3649 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -55,10 +55,11 @@ fn test_setup_works() { BondedPool:: { id: last_pool, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, }, } ); @@ -98,10 +99,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -153,10 +155,11 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -201,10 +204,11 @@ mod bonded_pool { let pool = BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; @@ -431,10 +435,11 @@ mod join { let bonded = |points, member_counter| BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points, + commission: Commission::default(), member_counter, + points, roles: DEFAULT_ROLES, + state: PoolState::Open, }, }; ExtBuilder::default().build_and_execute(|| { @@ -514,10 +519,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { + commission: Commission::default(), member_counter: 1, - state: PoolState::Open, points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -583,10 +589,11 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - state: PoolState::Open, - points: 100, + commission: Commission::default(), member_counter: 1, + points: 100, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -629,7 +636,14 @@ mod join { assert_eq!(MaxPoolMembers::::get(), Some(4)); Balances::make_free_balance_be(&104, 100 + Balances::minimum_balance()); - assert_ok!(Pools::create(RuntimeOrigin::signed(104), 100, 104, 104, 104)); + assert_ok!(Pools::create( + RuntimeOrigin::signed(104), + 100, + Commission::default(), + 104, + 104, + 104 + )); let pool_account = BondedPools::::iter() .find(|(_, bonded_pool)| bonded_pool.roles.depositor == 104) @@ -1599,7 +1613,14 @@ mod claim_payout { // create pool 2 Balances::make_free_balance_be(&20, 100); - assert_ok!(Pools::create(RuntimeOrigin::signed(20), 10, 20, 20, 20)); + assert_ok!(Pools::create( + RuntimeOrigin::signed(20), + 10, + Commission::default(), + 20, + 20, + 20 + )); // has no impact -- initial let (member_20, _, reward_pool_20) = Pools::get_member_with_pools(&20).unwrap(); @@ -1615,7 +1636,14 @@ mod claim_payout { // create pool 3 Balances::make_free_balance_be(&30, 100); - assert_ok!(Pools::create(RuntimeOrigin::signed(30), 10, 30, 30, 30)); + assert_ok!(Pools::create( + RuntimeOrigin::signed(30), + 10, + Commission::default(), + 30, + 30, + 30 + )); // reward counter is still the same. let (member_30, _, reward_pool_30) = Pools::get_member_with_pools(&30).unwrap(); @@ -2360,10 +2388,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, + points: 0, roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2396,10 +2425,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 560, + commission: Commission::default(), member_counter: 3, + points: 560, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -2436,10 +2466,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 10, + commission: Commission::default(), member_counter: 3, - roles: DEFAULT_ROLES + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2479,10 +2510,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - state: PoolState::Destroying, - points: 0, + commission: Commission::default(), member_counter: 1, - roles: DEFAULT_ROLES + points: 0, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, } } ); @@ -2607,10 +2639,11 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, // Only 10 points because 200 + 100 was unbonded roles: DEFAULT_ROLES, state: PoolState::Blocked, - points: 10, // Only 10 points because 200 + 100 was unbonded - member_counter: 3, } } ); @@ -2757,10 +2790,11 @@ mod unbond { BondedPool:: { id: 1, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -3469,10 +3503,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, points: 10, + roles: DEFAULT_ROLES, state: PoolState::Open, - member_counter: 3, - roles: DEFAULT_ROLES } } ); @@ -3549,10 +3584,11 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - points: 10, - state: PoolState::Open, + commission: Commission::default(), member_counter: 2, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, } } ); @@ -4080,6 +4116,7 @@ mod create { assert_ok!(Pools::create( RuntimeOrigin::signed(11), StakingMock::minimum_bond(), + Commission::default(), 123, 456, 789 @@ -4099,15 +4136,16 @@ mod create { BondedPool { id: 2, inner: BondedPoolInner { - points: StakingMock::minimum_bond(), + commission: Commission::default(), member_counter: 1, - state: PoolState::Open, + points: StakingMock::minimum_bond(), roles: PoolRoles { depositor: 11, root: Some(123), nominator: Some(456), state_toggler: Some(789) - } + }, + state: PoolState::Open, } } ); @@ -4136,7 +4174,7 @@ mod create { fn create_errors_correctly() { ExtBuilder::default().with_check(0).build_and_execute(|| { assert_noop!( - Pools::create(RuntimeOrigin::signed(10), 420, 123, 456, 789), + Pools::create(RuntimeOrigin::signed(10), 420, Commission::default(), 123, 456, 789), Error::::AccountBelongsToOtherPool ); @@ -4146,7 +4184,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 9, 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 9, Commission::default(), 123, 456, 789), Error::::MinimumBondNotMet ); @@ -4155,7 +4193,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 19, 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 19, Commission::default(), 123, 456, 789), Error::::MinimumBondNotMet ); @@ -4163,10 +4201,11 @@ mod create { BondedPool:: { id: 2, inner: BondedPoolInner { - state: PoolState::Open, - points: 10, + commission: Commission::default(), member_counter: 1, + points: 10, roles: DEFAULT_ROLES, + state: PoolState::Open, }, } .put(); @@ -4175,7 +4214,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 20, 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 20, Commission::default(), 123, 456, 789), Error::::MaxPools ); @@ -4188,6 +4227,7 @@ mod create { // Then let create = RuntimeCall::Pools(crate::Call::::create { amount: 20, + commission: Commission::default(), root: 11, nominator: 11, state_toggler: 11, From 79a0b9dba7563fbb30b5131672c6c53733cdfd76 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 13:00:00 +0100 Subject: [PATCH 002/221] add set_commission call --- frame/nomination-pools/src/lib.rs | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 68d441e1a4d67..16175ad0752ce 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -741,6 +741,10 @@ impl BondedPool { self.is_root(who) || self.is_state_toggler(who) } + fn can_set_commission(&self, who: &T::AccountId) -> bool { + self.is_root(who) + } + fn is_destroying(&self) -> bool { matches!(self.state, PoolState::Destroying) } @@ -1419,6 +1423,8 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, + /// A pool's commission settings have been changed. + PoolCommissionUpdated { pool_id: PoolId, commission: Commission }, } #[pallet::error] @@ -1476,6 +1482,8 @@ pub mod pallet { PartialUnbondNotAllowedPermissionlessly, /// The pool commission has been misconfigured. CommissionMisconfigured, + /// The pool's max commission has already been set. + MaxCommissionAlreadySet, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -2057,6 +2065,53 @@ pub mod pallet { Ok(()) } + /// Set the commission of a pool. + /// + /// The dispatch origin of this call must be signed by the root role of the pool. + /// The supplied commission is firstly checked for validity. If the pool already has + /// max commission set, and a commission.max is provided, then this call fails. + /// + /// If a max commission has been provided and has _not_ yet been set, then it is set + /// alongside the current commission. Otherwise, the pool's max commission is + /// maintained and the current commission is set only. + #[pallet::weight(0)] + #[transactional] + pub fn set_commission( + origin: OriginFor, + pool_id: PoolId, + commission: Commission, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!( + BondedPool::::get(pool_id) + .ok_or(Error::::PoolNotFound)? + .can_set_commission(&who), + Error::::DoesNotHavePermission + ); + ensure!( + commission.max.unwrap_or(Perbill::zero()) >= commission.current, + Error::::CommissionMisconfigured + ); + + BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; + ensure!( + !(pool.commission.max.is_some() && commission.max.is_some()), + Error::::MaxCommissionAlreadySet + ); + let new_commission = Commission { + current: commission.current, + max: if let Some(m) = commission.max { Some(m) } else { pool.commission.max }, + }; + pool.commission = new_commission.clone(); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + commission: new_commission, + }); + Ok(()) + }) + } + /// Update configurations for the nomination pools. The origin for this call must be /// Root. /// From b8d041a09035a66e499e269b17bcb7af34f7eea6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 14:39:55 +0100 Subject: [PATCH 003/221] add CommissionConfig + commission.config --- frame/nomination-pools/src/lib.rs | 44 +++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 16175ad0752ce..d86f4bb6e7b63 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -543,18 +543,39 @@ pub struct PoolRoles { /// The pool depositor can set a commission upon a pool creation, from which the pool owner can /// update thereafter. The `max` commission value can only be set once, as to prevent the commission /// from repeatedly increasing. +/// +/// The commission config is also optional, allowing the pool to set strict limits to how much +/// commission can change in each update, and how often updates can take place. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] -pub struct Commission { +#[codec(mel_bound(T: Config))] +#[scale_info(skip_type_params(T))] +pub struct Commission { /// The active commission rate of the pool. current: Perbill, - /// An optional maximum commission that can be set by the pool. Once set, this value cannot be - /// updated. + /// An optional maximum commission that can be set by the pool root. Once set, this value + /// cannot be updated. max: Option, + /// Configiration around how often the commission can be updated, and metadata around the + /// previous round of updates. + config: Option>, } -impl Default for Commission { +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] +#[codec(mel_bound(T: Config))] +#[scale_info(skip_type_params(T))] +pub struct CommissionConfig { + /// The pool root is able to set the maximum amount the commission can be updated by, and how + /// often an update can take place. + pub change_rate: (Perbill, T::BlockNumber), + /// The previous commission, prior to the latest change. + previous: Perbill, + /// The block the previous commission update took place. + previous_set_at: T::BlockNumber, +} + +impl Default for Commission { fn default() -> Self { - Self { current: Perbill::zero(), max: Some(Perbill::zero()) } + Self { current: Perbill::zero(), max: Some(Perbill::zero()), config: None } } } @@ -564,7 +585,7 @@ impl Default for Commission { #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { /// The commission rate, if any, of the pool. - pub commission: Commission, + pub commission: Commission, /// Count of members that belong to the pool. pub member_counter: u32, /// Total points of all the members in the pool who are actively bonded. @@ -603,7 +624,7 @@ impl sp_std::ops::DerefMut for BondedPool { impl BondedPool { /// Create a new bonded pool with the given roles and identifier. - fn new(id: PoolId, commission: Commission, roles: PoolRoles) -> Self { + fn new(id: PoolId, commission: Commission, roles: PoolRoles) -> Self { Self { id, inner: BondedPoolInner { @@ -1424,7 +1445,7 @@ pub mod pallet { /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, /// A pool's commission settings have been changed. - PoolCommissionUpdated { pool_id: PoolId, commission: Commission }, + PoolCommissionUpdated { pool_id: PoolId, commission: Commission }, } #[pallet::error] @@ -1903,7 +1924,7 @@ pub mod pallet { pub fn create( origin: OriginFor, #[pallet::compact] amount: BalanceOf, - commission: Commission, + commission: Commission, root: AccountIdLookupOf, nominator: AccountIdLookupOf, state_toggler: AccountIdLookupOf, @@ -2079,7 +2100,7 @@ pub mod pallet { pub fn set_commission( origin: OriginFor, pool_id: PoolId, - commission: Commission, + commission: Commission, ) -> DispatchResult { let who = ensure_signed(origin)?; ensure!( @@ -2092,7 +2113,7 @@ pub mod pallet { commission.max.unwrap_or(Perbill::zero()) >= commission.current, Error::::CommissionMisconfigured ); - + BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; ensure!( @@ -2102,6 +2123,7 @@ pub mod pallet { let new_commission = Commission { current: commission.current, max: if let Some(m) = commission.max { Some(m) } else { pool.commission.max }, + config: None, }; pool.commission = new_commission.clone(); Self::deposit_event(Event::::PoolCommissionUpdated { From 5fbc8d0a633e1136b0e2089ca8f6bfc8d26edfbc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 18:37:59 +0100 Subject: [PATCH 004/221] default commission on create --- frame/nomination-pools/src/lib.rs | 10 ++-------- frame/nomination-pools/src/mock.rs | 1 - frame/nomination-pools/src/tests.rs | 13 ++++--------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d86f4bb6e7b63..f387873af710c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -624,11 +624,11 @@ impl sp_std::ops::DerefMut for BondedPool { impl BondedPool { /// Create a new bonded pool with the given roles and identifier. - fn new(id: PoolId, commission: Commission, roles: PoolRoles) -> Self { + fn new(id: PoolId, roles: PoolRoles) -> Self { Self { id, inner: BondedPoolInner { - commission, + commission: Commission::default(), member_counter: Zero::zero(), points: Zero::zero(), roles, @@ -1924,7 +1924,6 @@ pub mod pallet { pub fn create( origin: OriginFor, #[pallet::compact] amount: BalanceOf, - commission: Commission, root: AccountIdLookupOf, nominator: AccountIdLookupOf, state_toggler: AccountIdLookupOf, @@ -1941,10 +1940,6 @@ pub mod pallet { Error::::MaxPools ); ensure!(!PoolMembers::::contains_key(&who), Error::::AccountBelongsToOtherPool); - ensure!( - commission.max.unwrap_or(Perbill::zero()) >= commission.current, - Error::::CommissionMisconfigured - ); let pool_id = LastPoolId::::try_mutate::<_, Error, _>(|id| { *id = id.checked_add(1).ok_or(Error::::OverflowRisk)?; @@ -1952,7 +1947,6 @@ pub mod pallet { })?; let mut bonded_pool = BondedPool::::new( pool_id, - commission, PoolRoles { root: Some(root), nominator: Some(nominator), diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 2c935b1c6761a..eae01b0bee8ec 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -293,7 +293,6 @@ impl ExtBuilder { assert_ok!(Pools::create( RawOrigin::Signed(10).into(), amount_to_bond, - Commission::default(), 900, 901, 902 diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 6676fbaab3649..48215a7f66719 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -639,7 +639,6 @@ mod join { assert_ok!(Pools::create( RuntimeOrigin::signed(104), 100, - Commission::default(), 104, 104, 104 @@ -1616,7 +1615,6 @@ mod claim_payout { assert_ok!(Pools::create( RuntimeOrigin::signed(20), 10, - Commission::default(), 20, 20, 20 @@ -1639,7 +1637,6 @@ mod claim_payout { assert_ok!(Pools::create( RuntimeOrigin::signed(30), 10, - Commission::default(), 30, 30, 30 @@ -4116,7 +4113,6 @@ mod create { assert_ok!(Pools::create( RuntimeOrigin::signed(11), StakingMock::minimum_bond(), - Commission::default(), 123, 456, 789 @@ -4174,7 +4170,7 @@ mod create { fn create_errors_correctly() { ExtBuilder::default().with_check(0).build_and_execute(|| { assert_noop!( - Pools::create(RuntimeOrigin::signed(10), 420, Commission::default(), 123, 456, 789), + Pools::create(RuntimeOrigin::signed(10), 420, 123, 456, 789), Error::::AccountBelongsToOtherPool ); @@ -4184,7 +4180,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 9, Commission::default(), 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 9, 123, 456, 789), Error::::MinimumBondNotMet ); @@ -4193,7 +4189,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 19, Commission::default(), 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 19, 123, 456, 789), Error::::MinimumBondNotMet ); @@ -4214,7 +4210,7 @@ mod create { // Then assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 20, Commission::default(), 123, 456, 789), + Pools::create(RuntimeOrigin::signed(11), 20, 123, 456, 789), Error::::MaxPools ); @@ -4227,7 +4223,6 @@ mod create { // Then let create = RuntimeCall::Pools(crate::Call::::create { amount: 20, - commission: Commission::default(), root: 11, nominator: 11, state_toggler: 11, From ff9825e3e7bb47a246009dba426e71b407b4eb05 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 19:15:07 +0100 Subject: [PATCH 005/221] separate set_commission & set_max_commission --- frame/nomination-pools/src/lib.rs | 89 ++++++++++++++++++++++------- frame/nomination-pools/src/mock.rs | 8 +-- frame/nomination-pools/src/tests.rs | 24 +------- 3 files changed, 71 insertions(+), 50 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f387873af710c..f1fb66c9a3d09 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1444,8 +1444,10 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, - /// A pool's commission settings have been changed. - PoolCommissionUpdated { pool_id: PoolId, commission: Commission }, + /// A pool's commission setting has been changed. + PoolCommissionUpdated { pool_id: PoolId, commission: Perbill }, + /// A pool's maximum commission setting has been changed. + PoolMaxCommissionUpdated { pool_id: PoolId, commission: Perbill, max_commission: Perbill }, } #[pallet::error] @@ -1503,8 +1505,10 @@ pub mod pallet { PartialUnbondNotAllowedPermissionlessly, /// The pool commission has been misconfigured. CommissionMisconfigured, - /// The pool's max commission has already been set. - MaxCommissionAlreadySet, + /// The pool's max commission cannot be set higher than the existing value. + MaxCommissionRestricted, + /// The supplied commission exceeds the max allowed commission. + CommissionExceedsMaximum, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -2083,18 +2087,17 @@ pub mod pallet { /// Set the commission of a pool. /// /// The dispatch origin of this call must be signed by the root role of the pool. - /// The supplied commission is firstly checked for validity. If the pool already has - /// max commission set, and a commission.max is provided, then this call fails. + /// If the pool has a max commission set, the commission supplied must be less or + /// equal to the max. /// - /// If a max commission has been provided and has _not_ yet been set, then it is set - /// alongside the current commission. Otherwise, the pool's max commission is - /// maintained and the current commission is set only. + /// If the max commission has _not_ yet been set, then the commission is not + /// restricted. #[pallet::weight(0)] #[transactional] pub fn set_commission( origin: OriginFor, pool_id: PoolId, - commission: Commission, + commission: Perbill, ) -> DispatchResult { let who = ensure_signed(origin)?; ensure!( @@ -2103,26 +2106,68 @@ pub mod pallet { .can_set_commission(&who), Error::::DoesNotHavePermission ); + + BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; + + // TODO: check if CommissionConfig.previous exists, and ensure that enough + // blocks have passed since the previous update if it does. + + ensure!( + commission <= pool.commission.max.unwrap_or(Perbill::max_value()), + Error::::CommissionExceedsMaximum + ); + pool.commission.current = commission.clone(); + + // TODO: update CommissionConfig.previous and CommissionConfig.previous_set_at + // if a CommissionConfig is set. + + Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); + Ok(()) + }) + } + + /// Set the maximum commission of a pool. + /// + /// The dispatch origin of this call must be signed by the root role of the pool. + /// If a maximum commission already exists prior to this call, then the updated + /// max commission must be lower, otherwise this call will fail. + /// + /// This call also updates the pool's current commission to the new maximum if the + /// current commission is higher than the supplied maximum. + #[pallet::weight(0)] + #[transactional] + pub fn set_max_commission( + origin: OriginFor, + pool_id: PoolId, + max_commission: Perbill, + ) -> DispatchResult { + let who = ensure_signed(origin)?; ensure!( - commission.max.unwrap_or(Perbill::zero()) >= commission.current, - Error::::CommissionMisconfigured + BondedPool::::get(pool_id) + .ok_or(Error::::PoolNotFound)? + .can_set_commission(&who), + Error::::DoesNotHavePermission ); BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; ensure!( - !(pool.commission.max.is_some() && commission.max.is_some()), - Error::::MaxCommissionAlreadySet + pool.commission.max.unwrap_or(Perbill::max_value()) > max_commission, + Error::::MaxCommissionRestricted ); - let new_commission = Commission { - current: commission.current, - max: if let Some(m) = commission.max { Some(m) } else { pool.commission.max }, - config: None, - }; - pool.commission = new_commission.clone(); - Self::deposit_event(Event::::PoolCommissionUpdated { + + pool.commission.max = Some(max_commission.clone()); + + // if the pool's current commission is higher than the updated maximum commission, + // decrease it to the new maximum commission. + if pool.commission.current > max_commission { + pool.commission.current = max_commission.clone(); + } + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, - commission: new_commission, + commission: pool.commission.current.clone(), + max_commission, }); Ok(()) }) diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index eae01b0bee8ec..1b3372dae56ee 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -290,13 +290,7 @@ impl ExtBuilder { // make a pool let amount_to_bond = Pools::depositor_min_bond(); Balances::make_free_balance_be(&10, amount_to_bond * 5); - assert_ok!(Pools::create( - RawOrigin::Signed(10).into(), - amount_to_bond, - 900, - 901, - 902 - )); + assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902)); assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); let last_pool = LastPoolId::::get(); for (account_id, bonded) in self.members { diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 48215a7f66719..5004a8766fe81 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -636,13 +636,7 @@ mod join { assert_eq!(MaxPoolMembers::::get(), Some(4)); Balances::make_free_balance_be(&104, 100 + Balances::minimum_balance()); - assert_ok!(Pools::create( - RuntimeOrigin::signed(104), - 100, - 104, - 104, - 104 - )); + assert_ok!(Pools::create(RuntimeOrigin::signed(104), 100, 104, 104, 104)); let pool_account = BondedPools::::iter() .find(|(_, bonded_pool)| bonded_pool.roles.depositor == 104) @@ -1612,13 +1606,7 @@ mod claim_payout { // create pool 2 Balances::make_free_balance_be(&20, 100); - assert_ok!(Pools::create( - RuntimeOrigin::signed(20), - 10, - 20, - 20, - 20 - )); + assert_ok!(Pools::create(RuntimeOrigin::signed(20), 10, 20, 20, 20)); // has no impact -- initial let (member_20, _, reward_pool_20) = Pools::get_member_with_pools(&20).unwrap(); @@ -1634,13 +1622,7 @@ mod claim_payout { // create pool 3 Balances::make_free_balance_be(&30, 100); - assert_ok!(Pools::create( - RuntimeOrigin::signed(30), - 10, - 30, - 30, - 30 - )); + assert_ok!(Pools::create(RuntimeOrigin::signed(30), 10, 30, 30, 30)); // reward counter is still the same. let (member_30, _, reward_pool_30) = Pools::get_member_with_pools(&30).unwrap(); From 6514d5065b16d53d16d7cc300c76ae6928b468ad Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 19:18:25 +0100 Subject: [PATCH 006/221] rename CommissionConfig to CommissionThrottle --- frame/nomination-pools/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f1fb66c9a3d09..3467a3b709583 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -544,7 +544,7 @@ pub struct PoolRoles { /// update thereafter. The `max` commission value can only be set once, as to prevent the commission /// from repeatedly increasing. /// -/// The commission config is also optional, allowing the pool to set strict limits to how much +/// A commission throttle is also optional, allowing the pool to set strict limits to how much /// commission can change in each update, and how often updates can take place. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] @@ -557,13 +557,13 @@ pub struct Commission { max: Option, /// Configiration around how often the commission can be updated, and metadata around the /// previous round of updates. - config: Option>, + throttle: Option>, } #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] -pub struct CommissionConfig { +pub struct CommissionThrottle { /// The pool root is able to set the maximum amount the commission can be updated by, and how /// often an update can take place. pub change_rate: (Perbill, T::BlockNumber), @@ -575,7 +575,7 @@ pub struct CommissionConfig { impl Default for Commission { fn default() -> Self { - Self { current: Perbill::zero(), max: Some(Perbill::zero()), config: None } + Self { current: Perbill::zero(), max: Some(Perbill::zero()), throttle: None } } } @@ -2110,7 +2110,7 @@ pub mod pallet { BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; - // TODO: check if CommissionConfig.previous exists, and ensure that enough + // TODO: check if CommissionThrottle.previous exists, and ensure that enough // blocks have passed since the previous update if it does. ensure!( @@ -2119,8 +2119,8 @@ pub mod pallet { ); pool.commission.current = commission.clone(); - // TODO: update CommissionConfig.previous and CommissionConfig.previous_set_at - // if a CommissionConfig is set. + // TODO: update CommissionThrottle.previous and CommissionThrottle.previous_set_at + // if a CommissionThrottle is set. Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); Ok(()) From b438a800ba98b2142ee28b8c9b28bfc8a08a6b9e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 19:45:56 +0100 Subject: [PATCH 007/221] add throttling in set_commission --- frame/nomination-pools/src/lib.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3467a3b709583..1afd9f402650d 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -567,8 +567,6 @@ pub struct CommissionThrottle { /// The pool root is able to set the maximum amount the commission can be updated by, and how /// often an update can take place. pub change_rate: (Perbill, T::BlockNumber), - /// The previous commission, prior to the latest change. - previous: Perbill, /// The block the previous commission update took place. previous_set_at: T::BlockNumber, } @@ -1509,6 +1507,8 @@ pub mod pallet { MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. CommissionExceedsMaximum, + /// Not enough blocks have surpassed since the last commission update. + CommissionChangeThrottled, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -2109,19 +2109,31 @@ pub mod pallet { BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; - - // TODO: check if CommissionThrottle.previous exists, and ensure that enough - // blocks have passed since the previous update if it does. - + let block_number = >::block_number(); + + // if a commission throttle exists, ensure that: + // 1. enough blocks have passed since the previous update took place, and + // 2. the new commission is within the maximum allowed change. + if let Some(throttle) = &pool.commission.throttle { + let (max_change, min_wait) = throttle.change_rate; + ensure!( + (block_number.saturating_sub(throttle.previous_set_at)) >= min_wait && + (pool.commission.current.saturating_sub(commission)) <= max_change, + Error::::CommissionChangeThrottled + ); + } ensure!( commission <= pool.commission.max.unwrap_or(Perbill::max_value()), Error::::CommissionExceedsMaximum ); - pool.commission.current = commission.clone(); - // TODO: update CommissionThrottle.previous and CommissionThrottle.previous_set_at - // if a CommissionThrottle is set. + pool.commission.current = commission.clone(); + // if throttle is configured, update throttle.previous_set_at property. + if let Some(throttle) = &pool.commission.throttle { + pool.commission.throttle = + Some(CommissionThrottle { previous_set_at: block_number, ..*throttle }); + } Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); Ok(()) }) From 4bded455f4991fee635629007304e10750ee40b2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 20:01:05 +0100 Subject: [PATCH 008/221] abstract CommissionThrottle::can_update --- frame/nomination-pools/src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 1afd9f402650d..6c3a87c6b4e92 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -577,6 +577,18 @@ impl Default for Commission { } } +impl CommissionThrottle { + // for an existing throttle to update, ensure that: + // 1. enough blocks have passed since the previous update took place, and + // 2. the new commission is within the maximum allowed change. + fn can_update(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { + let (max_change, min_wait) = self.change_rate; + + (current_block.saturating_sub(self.previous_set_at)) >= min_wait && + (from.saturating_sub(*to)) <= max_change + } +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] @@ -2111,14 +2123,9 @@ pub mod pallet { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; let block_number = >::block_number(); - // if a commission throttle exists, ensure that: - // 1. enough blocks have passed since the previous update took place, and - // 2. the new commission is within the maximum allowed change. if let Some(throttle) = &pool.commission.throttle { - let (max_change, min_wait) = throttle.change_rate; ensure!( - (block_number.saturating_sub(throttle.previous_set_at)) >= min_wait && - (pool.commission.current.saturating_sub(commission)) <= max_change, + throttle.can_update(&pool.commission.current, &commission, &block_number), Error::::CommissionChangeThrottled ); } From ad41ef2d9d81cacba1f7b3699287c1b9343a63ea Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 20:10:13 +0100 Subject: [PATCH 009/221] mv Default Commission impl --- frame/nomination-pools/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6c3a87c6b4e92..c6fe21d022601 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -560,6 +560,12 @@ pub struct Commission { throttle: Option>, } +impl Default for Commission { + fn default() -> Self { + Self { current: Perbill::zero(), max: Some(Perbill::zero()), throttle: None } + } +} + #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] @@ -571,12 +577,6 @@ pub struct CommissionThrottle { previous_set_at: T::BlockNumber, } -impl Default for Commission { - fn default() -> Self { - Self { current: Perbill::zero(), max: Some(Perbill::zero()), throttle: None } - } -} - impl CommissionThrottle { // for an existing throttle to update, ensure that: // 1. enough blocks have passed since the previous update took place, and From fd96a70c0c6aa4452e2d800b58263c36025e7be6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 21:56:52 +0100 Subject: [PATCH 010/221] add set_commission_throttle --- frame/nomination-pools/src/lib.rs | 60 +++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c6fe21d022601..4e4c8c1a7cc2c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -574,7 +574,7 @@ pub struct CommissionThrottle { /// often an update can take place. pub change_rate: (Perbill, T::BlockNumber), /// The block the previous commission update took place. - previous_set_at: T::BlockNumber, + previous_set_at: Option, } impl CommissionThrottle { @@ -582,10 +582,10 @@ impl CommissionThrottle { // 1. enough blocks have passed since the previous update took place, and // 2. the new commission is within the maximum allowed change. fn can_update(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { - let (max_change, min_wait) = self.change_rate; + let (max_increase, min_delay) = self.change_rate; - (current_block.saturating_sub(self.previous_set_at)) >= min_wait && - (from.saturating_sub(*to)) <= max_change + current_block.saturating_sub(self.previous_set_at.unwrap_or(T::BlockNumber::zero())) >= + min_delay && (from.saturating_sub(*to)) <= max_increase } } @@ -1521,6 +1521,8 @@ pub mod pallet { CommissionExceedsMaximum, /// Not enough blocks have surpassed since the last commission update. CommissionChangeThrottled, + /// The submitted changes to commission throttle are not allowed. + CommissionThrottleNotAllowed, } #[derive(Encode, Decode, PartialEq, TypeInfo, frame_support::PalletError, RuntimeDebug)] @@ -2138,8 +2140,10 @@ pub mod pallet { // if throttle is configured, update throttle.previous_set_at property. if let Some(throttle) = &pool.commission.throttle { - pool.commission.throttle = - Some(CommissionThrottle { previous_set_at: block_number, ..*throttle }); + pool.commission.throttle = Some(CommissionThrottle { + previous_set_at: Some(block_number), + ..*throttle + }); } Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); Ok(()) @@ -2192,6 +2196,50 @@ pub mod pallet { }) } + /// Set the commission throttle for a pool. + /// + /// The dispatch origin of this call must be signed by the root role of the pool. + /// If a throttle is already present, this call will only succeed if a more restrictive + /// throttle configuration is given. + /// + /// If a throttle configuration does not yet exist, the values are set. + #[pallet::weight(0)] + #[transactional] + pub fn set_commission_throttle( + origin: OriginFor, + pool_id: PoolId, + max_increase: Perbill, + min_delay: T::BlockNumber, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + ensure!( + BondedPool::::get(pool_id) + .ok_or(Error::::PoolNotFound)? + .can_set_commission(&who), + Error::::DoesNotHavePermission + ); + BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; + if let Some(throttle) = &pool.commission.throttle { + let (current_max_increase, current_min_delay) = throttle.change_rate; + ensure!( + max_increase <= current_max_increase && min_delay >= current_min_delay, + Error::::CommissionThrottleNotAllowed + ); + pool.commission.throttle = Some(CommissionThrottle { + change_rate: (max_increase, min_delay), + ..*throttle + }); + } else { + pool.commission.throttle = Some(CommissionThrottle { + change_rate: (max_increase, min_delay), + previous_set_at: None, + }); + } + Ok(()) + }) + } + /// Update configurations for the nomination pools. The origin for this call must be /// Root. /// From 655ef7562c682732feda3e2d5ad1aee06b556697 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 22:03:26 +0100 Subject: [PATCH 011/221] change can_update to throttling --- frame/nomination-pools/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 4e4c8c1a7cc2c..8519532fe1dcd 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -578,14 +578,14 @@ pub struct CommissionThrottle { } impl CommissionThrottle { - // for an existing throttle to update, ensure that: - // 1. enough blocks have passed since the previous update took place, and - // 2. the new commission is within the maximum allowed change. - fn can_update(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { + // A commission change will be throttled (disallowed) if: + // 1. not enough blocks have passed since the previous commission update took place, and + // 2. the new commission is larger than the maximum allowed increase. + fn throttling(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { let (max_increase, min_delay) = self.change_rate; - current_block.saturating_sub(self.previous_set_at.unwrap_or(T::BlockNumber::zero())) >= - min_delay && (from.saturating_sub(*to)) <= max_increase + current_block.saturating_sub(self.previous_set_at.unwrap_or(T::BlockNumber::zero())) < + min_delay || (from.saturating_sub(*to)) > max_increase } } @@ -2127,7 +2127,7 @@ pub mod pallet { if let Some(throttle) = &pool.commission.throttle { ensure!( - throttle.can_update(&pool.commission.current, &commission, &block_number), + !throttle.throttling(&pool.commission.current, &commission, &block_number), Error::::CommissionChangeThrottled ); } From d2fe74a2a07a8d1657e648ac290639d7e2fd301b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 14 Oct 2022 22:04:43 +0100 Subject: [PATCH 012/221] rm unused Error --- frame/nomination-pools/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 8519532fe1dcd..d378559536dec 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1513,8 +1513,6 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, - /// The pool commission has been misconfigured. - CommissionMisconfigured, /// The pool's max commission cannot be set higher than the existing value. MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. From 3adbaa5cef6a15a33eff5b60b7054bed2994e225 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 15 Oct 2022 15:18:19 +0100 Subject: [PATCH 013/221] deduct & transfer commission to depositor --- frame/nomination-pools/src/lib.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d378559536dec..036e20a3d1e7c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1655,7 +1655,7 @@ pub mod pallet { } /// A bonded member can use this to claim their payout based on the rewards that the pool - /// has accumulated since their last claimed payout (OR since joining if this is there first + /// has accumulated since their last claimed payout (OR since joining if this is their first /// time claiming rewards). The payout will be transferred to the member's account. /// /// The member will earn rewards pro rata based on the members stake vs the sum of the @@ -2549,7 +2549,7 @@ impl Pallet { let current_reward_counter = reward_pool.current_reward_counter(bonded_pool.id, bonded_pool.points)?; - let pending_rewards = member.pending_rewards(current_reward_counter)?; + let mut pending_rewards = member.pending_rewards(current_reward_counter)?; if pending_rewards.is_zero() { return Ok(pending_rewards) @@ -2559,7 +2559,26 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // Transfer payout to the member. + // If a non-zero commission has been applied to the pool, deduct the share from + // `pending_rewards` and send that amount to the pool `depositor`. + if bonded_pool.commission.current > Perbill::zero() { + let pool_commission = bonded_pool.commission.current * pending_rewards; + pending_rewards -= pool_commission; + + // TODO: discuss a better solution than sending commission to the depositor. + // Currently, all other roles are optional and may not be set when commission + // needs to be received. + // For now: + // Transfer commission to the depositor. + T::Currency::transfer( + &bonded_pool.reward_account(), + &bonded_pool.roles.depositor, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } + + // Transfer remaining payout to the member. T::Currency::transfer( &bonded_pool.reward_account(), &member_account, From 2a2ebf5ddf347d108c8d0bc7db471a8dc8ca6b77 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 15 Oct 2022 15:35:48 +0100 Subject: [PATCH 014/221] use try_mutate --- frame/nomination-pools/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 036e20a3d1e7c..e91cca160bc78 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2119,7 +2119,7 @@ pub mod pallet { Error::::DoesNotHavePermission ); - BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + BondedPools::::try_mutate(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; let block_number = >::block_number(); @@ -2171,7 +2171,7 @@ pub mod pallet { Error::::DoesNotHavePermission ); - BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + BondedPools::::try_mutate(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; ensure!( pool.commission.max.unwrap_or(Perbill::max_value()) > max_commission, @@ -2216,7 +2216,7 @@ pub mod pallet { .can_set_commission(&who), Error::::DoesNotHavePermission ); - BondedPools::::try_mutate_exists(pool_id, |maybe_pool| { + BondedPools::::try_mutate(pool_id, |maybe_pool| { let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; if let Some(throttle) = &pool.commission.throttle { let (current_max_increase, current_min_delay) = throttle.change_rate; From f33f92b82e880b9868ef7e91371ef29a2b41b334 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 15 Oct 2022 17:11:57 +0100 Subject: [PATCH 015/221] mv setters to BondedPool --- frame/nomination-pools/src/lib.rs | 164 +++++++++++++++--------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e91cca160bc78..7b2f47347ba4d 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -740,6 +740,46 @@ impl BondedPool { .saturating_sub(T::StakingInterface::active_stake(&account).unwrap_or_default()) } + /// Set the pool's commission. + fn set_commission(mut self, commission: &Perbill) -> Self { + self.commission.current = *commission; + + // if throttle is configured, record the current block as the previously + // updated commission. + if let Some(throttle) = &self.commission.throttle { + self.commission.throttle = Some(CommissionThrottle { + previous_set_at: Some(>::block_number()), + ..*throttle + }); + } + self + } + + /// Set the pool's maximum commission. + fn set_max_commission(&mut self, max_commission: Perbill) -> (Perbill, Perbill) { + self.commission.max = Some(max_commission.clone()); + // if the pool's current commission is higher than the updated maximum commission, + // decrease it to the new maximum commission. + if self.commission.current > max_commission { + self.commission.current = max_commission; + } + (self.commission.current, max_commission) + } + + /// Set the pool's commission throttle settings. + fn set_commission_throttle(mut self, max_increase: Perbill, min_delay: T::BlockNumber) -> Self { + if let Some(throttle) = &self.commission.throttle { + self.commission.throttle = + Some(CommissionThrottle { change_rate: (max_increase, min_delay), ..*throttle }); + } else { + self.commission.throttle = Some(CommissionThrottle { + change_rate: (max_increase, min_delay), + previous_set_at: None, + }); + } + self + } + fn is_root(&self, who: &T::AccountId) -> bool { self.roles.root.as_ref().map_or(false, |root| root == who) } @@ -2112,40 +2152,26 @@ pub mod pallet { commission: Perbill, ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!( - BondedPool::::get(pool_id) - .ok_or(Error::::PoolNotFound)? - .can_set_commission(&who), - Error::::DoesNotHavePermission - ); - - BondedPools::::try_mutate(pool_id, |maybe_pool| { - let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; - let block_number = >::block_number(); - - if let Some(throttle) = &pool.commission.throttle { - ensure!( - !throttle.throttling(&pool.commission.current, &commission, &block_number), - Error::::CommissionChangeThrottled - ); - } + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + if let Some(throttle) = &bonded_pool.commission.throttle { ensure!( - commission <= pool.commission.max.unwrap_or(Perbill::max_value()), - Error::::CommissionExceedsMaximum + !throttle.throttling( + &bonded_pool.commission.current, + &commission, + &>::block_number() + ), + Error::::CommissionChangeThrottled ); + } + ensure!( + commission <= bonded_pool.commission.max.unwrap_or(Perbill::max_value()), + Error::::CommissionExceedsMaximum + ); - pool.commission.current = commission.clone(); - - // if throttle is configured, update throttle.previous_set_at property. - if let Some(throttle) = &pool.commission.throttle { - pool.commission.throttle = Some(CommissionThrottle { - previous_set_at: Some(block_number), - ..*throttle - }); - } - Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); - Ok(()) - }) + bonded_pool.set_commission(&commission).put(); + Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); + Ok(()) } /// Set the maximum commission of a pool. @@ -2164,34 +2190,22 @@ pub mod pallet { max_commission: Perbill, ) -> DispatchResult { let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); ensure!( - BondedPool::::get(pool_id) - .ok_or(Error::::PoolNotFound)? - .can_set_commission(&who), - Error::::DoesNotHavePermission + bonded_pool.commission.max.unwrap_or(Perbill::max_value()) > max_commission, + Error::::MaxCommissionRestricted ); - BondedPools::::try_mutate(pool_id, |maybe_pool| { - let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; - ensure!( - pool.commission.max.unwrap_or(Perbill::max_value()) > max_commission, - Error::::MaxCommissionRestricted - ); - - pool.commission.max = Some(max_commission.clone()); + let (current, max) = bonded_pool.set_max_commission(max_commission); + Self::deposit_event(Event::::PoolMaxCommissionUpdated { + pool_id, + commission: current, + max_commission: max, + }); - // if the pool's current commission is higher than the updated maximum commission, - // decrease it to the new maximum commission. - if pool.commission.current > max_commission { - pool.commission.current = max_commission.clone(); - } - Self::deposit_event(Event::::PoolMaxCommissionUpdated { - pool_id, - commission: pool.commission.current.clone(), - max_commission, - }); - Ok(()) - }) + bonded_pool.put(); + Ok(()) } /// Set the commission throttle for a pool. @@ -2210,32 +2224,18 @@ pub mod pallet { min_delay: T::BlockNumber, ) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!( - BondedPool::::get(pool_id) - .ok_or(Error::::PoolNotFound)? - .can_set_commission(&who), - Error::::DoesNotHavePermission - ); - BondedPools::::try_mutate(pool_id, |maybe_pool| { - let pool = maybe_pool.as_mut().ok_or(Error::::PoolNotFound)?; - if let Some(throttle) = &pool.commission.throttle { - let (current_max_increase, current_min_delay) = throttle.change_rate; - ensure!( - max_increase <= current_max_increase && min_delay >= current_min_delay, - Error::::CommissionThrottleNotAllowed - ); - pool.commission.throttle = Some(CommissionThrottle { - change_rate: (max_increase, min_delay), - ..*throttle - }); - } else { - pool.commission.throttle = Some(CommissionThrottle { - change_rate: (max_increase, min_delay), - previous_set_at: None, - }); - } - Ok(()) - }) + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + if let Some(throttle) = &bonded_pool.commission.throttle { + let (current_max_increase, current_min_delay) = throttle.change_rate; + ensure!( + max_increase <= current_max_increase && min_delay >= current_min_delay, + Error::::CommissionThrottleNotAllowed + ); + } + bonded_pool.set_commission_throttle(max_increase, min_delay).put(); + Ok(()) } /// Update configurations for the nomination pools. The origin for this call must be @@ -2560,7 +2560,7 @@ impl Pallet { reward_pool.register_claimed_reward(pending_rewards); // If a non-zero commission has been applied to the pool, deduct the share from - // `pending_rewards` and send that amount to the pool `depositor`. + // `pending_rewards` and send that amount to the pool `depositor`. if bonded_pool.commission.current > Perbill::zero() { let pool_commission = bonded_pool.commission.current * pending_rewards; pending_rewards -= pool_commission; From dcccc5db9c77c021271d545473bbf72452d16400 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 17 Oct 2022 12:03:03 +0100 Subject: [PATCH 016/221] calls work tests + fixes --- frame/nomination-pools/src/lib.rs | 19 +++++------ frame/nomination-pools/src/tests.rs | 52 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 7b2f47347ba4d..7f3060aa0b738 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -562,7 +562,7 @@ pub struct Commission { impl Default for Commission { fn default() -> Self { - Self { current: Perbill::zero(), max: Some(Perbill::zero()), throttle: None } + Self { current: Perbill::from_percent(0), max: None, throttle: None } } } @@ -2164,10 +2164,9 @@ pub mod pallet { Error::::CommissionChangeThrottled ); } - ensure!( - commission <= bonded_pool.commission.max.unwrap_or(Perbill::max_value()), - Error::::CommissionExceedsMaximum - ); + if let Some(max) = bonded_pool.commission.max { + ensure!(commission <= max, Error::::CommissionExceedsMaximum); + } bonded_pool.set_commission(&commission).put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); @@ -2192,10 +2191,10 @@ pub mod pallet { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - ensure!( - bonded_pool.commission.max.unwrap_or(Perbill::max_value()) > max_commission, - Error::::MaxCommissionRestricted - ); + + if let Some(existing_max) = bonded_pool.commission.max { + ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); + } let (current, max) = bonded_pool.set_max_commission(max_commission); Self::deposit_event(Event::::PoolMaxCommissionUpdated { @@ -2561,7 +2560,7 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. - if bonded_pool.commission.current > Perbill::zero() { + if bonded_pool.commission.current > Perbill::from_percent(0) { let pool_commission = bonded_pool.commission.current * pending_rewards; pending_rewards -= pool_commission; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5004a8766fe81..99b4d4c737f18 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -244,6 +244,58 @@ mod bonded_pool { assert_ok!(pool.ok_to_join(0)); }); } + + #[test] + fn set_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Set a commission pool 1 + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(50) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.current, + Perbill::from_percent(50) + ); + }); + } + + #[test] + fn set_max_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Set a max commission commission pool 1 + assert_ok!(Pools::set_max_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(100) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.max, + Some(Perbill::from_percent(100)) + ); + }); + } + + #[test] + fn set_commission_throttle_works() { + ExtBuilder::default().build_and_execute(|| { + // Set a commission throttle for pool 1 + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + 1000_u64 + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.throttle, + Some(CommissionThrottle { + change_rate: (Perbill::from_percent(5), 1000_u64), + previous_set_at: None + }) + ); + }); + } } mod reward_pool { From da2e32e009d9d19ce69b8b262d5764755fde598f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 17 Oct 2022 13:28:41 +0100 Subject: [PATCH 017/221] add commission tests + fixes --- frame/nomination-pools/src/lib.rs | 16 +- frame/nomination-pools/src/mock.rs | 17 ++ frame/nomination-pools/src/tests.rs | 289 +++++++++++++++++++++++++++- 3 files changed, 311 insertions(+), 11 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 7f3060aa0b738..31b5f1724bb99 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -546,7 +546,7 @@ pub struct PoolRoles { /// /// A commission throttle is also optional, allowing the pool to set strict limits to how much /// commission can change in each update, and how often updates can take place. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct Commission { @@ -566,7 +566,7 @@ impl Default for Commission { } } -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct CommissionThrottle { @@ -584,8 +584,14 @@ impl CommissionThrottle { fn throttling(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { let (max_increase, min_delay) = self.change_rate; - current_block.saturating_sub(self.previous_set_at.unwrap_or(T::BlockNumber::zero())) < - min_delay || (from.saturating_sub(*to)) > max_increase + // check enough blocks have passed since the previous commission update took place + if let Some(previous_set_at) = self.previous_set_at { + if current_block.saturating_sub(previous_set_at) < min_delay { + return true + } + } + // check the commission change is larger than the maximum allowed increase + (*to).saturating_sub(*from) > max_increase } } @@ -2229,7 +2235,7 @@ pub mod pallet { if let Some(throttle) = &bonded_pool.commission.throttle { let (current_max_increase, current_min_delay) = throttle.change_rate; ensure!( - max_increase <= current_max_increase && min_delay >= current_min_delay, + !(max_increase > current_max_increase || min_delay < current_min_delay), Error::::CommissionThrottleNotAllowed ); } diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 1b3372dae56ee..8d25f9a072a29 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -324,6 +324,23 @@ parameter_types! { storage BalancesEvents: u32 = 0; } +/// Helper to tun a specified amount of blocks. +pub(crate) fn run_blocks(n: u64) { + let current_block = System::block_number(); + run_to_block(n + current_block); +} + +/// Helper to run to a specific block. +pub(crate) fn run_to_block(n: u64) { + let current_block = System::block_number(); + assert!(n > current_block); + while System::block_number() < n { + Pools::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + Pools::on_initialize(System::block_number()); + } +} + /// All events of this pallet. pub(crate) fn pool_events_since_last_call() -> Vec> { let events = System::events() diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 99b4d4c737f18..6689263d2c875 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -255,31 +255,262 @@ mod bonded_pool { Perbill::from_percent(50) )); assert_eq!( - BondedPools::::get(1).unwrap().commission.current, + BondedPool::::get(1).unwrap().commission.current, Perbill::from_percent(50) ); + // Commission change events triggered successfully + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(50) + } + ] + ); + }); + } + + #[test] + fn set_commission_handles_errors() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1)), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission + ); + + // Throttle test. We will throttle commission to be a +1% commission increase every 2 + // blocks. + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(1), + 2_u64 + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Perbill::from_percent(0), + max: None, + throttle: Some(CommissionThrottle { + change_rate: (Perbill::from_percent(1), 2_u64), + previous_set_at: None, + }) + } + ); + + // We now try to increase commission to 5% (5% increase): this should be throttled. + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), + Error::::CommissionChangeThrottled + ); + + // We now try to increase commission by 1%. This should work, and set the + // `previous_set_at` field. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(1) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Perbill::from_percent(1), + max: None, + throttle: Some(CommissionThrottle { + change_rate: (Perbill::from_percent(1), 2_u64), + previous_set_at: Some(1_u64), + }) + } + ); + + // Attempt to increase the commission an additional 1% (now 2%) again immediately. + // this will fail as `previous_set_at` is now the current block, and at least 2 + // blocks need to pass before we can set commission again. + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(2)), + Error::::CommissionChangeThrottled + ); + + // Run 2 blocks into the future, to block 3. + run_blocks(2); + + // We can now successfully increase the commission again, to 2%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(2) + )); + + // Run 2 blocks into the future, to block 5. + run_blocks(2); + + // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is + // still at play. An attempted commission change now to 4% (+2% increase) should fail. + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(4)), + Error::::CommissionChangeThrottled + ); + + // We will now set a max commission to the current 2% + assert_ok!(Pools::set_max_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(2) + )); + + // Run 2 blocks into the future so we are eligible to update commission again. + run_blocks(2); + + // We will now attempt again to increase the commission by 1%, to 3%. This is within the + // change rate allowance, but the max_commission will now prevent us from going any + // higher. + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(3)), + Error::::CommissionExceedsMaximum + ); + + // Commission change events triggered successfully + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(1) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(2) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(2), + max_commission: Perbill::from_percent(2) + }, + ] + ); }); } #[test] - fn set_max_commission_works() { + fn set_max_commission_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { - // Set a max commission commission pool 1 + // Provided pool does not exist + assert_noop!( + Pools::set_max_commission( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(1) + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_max_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission + ); + + // Set a max commission commission pool 1 to 90% assert_ok!(Pools::set_max_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(100) + Perbill::from_percent(90) )); assert_eq!( BondedPools::::get(1).unwrap().commission.max, - Some(Perbill::from_percent(100)) + Some(Perbill::from_percent(90)) + ); + + // We attempt to increase the max commission to 100%, but increasing is disallowed. + assert_noop!( + Pools::set_max_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(100) + ), + Error::::MaxCommissionRestricted + ); + + // We will now set a commission to 75% and then amend the max commission to 50%. + // The max commission change should decrease the current commission to 50%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(75) + )); + assert_ok!(Pools::set_max_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(50) + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Perbill::from_percent(50), + max: Some(Perbill::from_percent(50)), + throttle: None + } + ); + + // Commission change events triggered successfully + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(0), + max_commission: Perbill::from_percent(90) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(75) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(50), + max_commission: Perbill::from_percent(50) + } + ] ); }); } #[test] - fn set_commission_throttle_works() { + fn set_commission_throttle_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(5), + 1000_u64 + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(1), + 1, + Perbill::from_percent(5), + 1000_u64 + ), + Error::::DoesNotHavePermission + ); + // Set a commission throttle for pool 1 assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), @@ -294,6 +525,52 @@ mod bonded_pool { previous_set_at: None }) ); + + // We now try to half the min_delay - this will be disallowed. + // A greater delay between commission changes is seen as more restrictive. + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + 500_u64 + ), + Error::::CommissionThrottleNotAllowed + ); + + // We now try to increase the allowed max_increase - this will fail. + // A smaller allowed commission change is seen as more restrictive. + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(10), + 1000_u64 + ), + Error::::CommissionThrottleNotAllowed + ); + + // Successful more restrictive change of min_delay with the current max_increase + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + 2000_u64 + )); + // Successful more restrictive change of max_increase with the current min_delay + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + 2000_u64 + )); + // Successful more restrictive change of both max_increase and min_delay + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(3), + 3000_u64 + )); }); } } From ddea6ba67c4ea0588e009c845d7169591dd82415 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 17 Oct 2022 13:29:03 +0100 Subject: [PATCH 018/221] fmt --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 6689263d2c875..88a52970aa89e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -549,7 +549,7 @@ mod bonded_pool { ), Error::::CommissionThrottleNotAllowed ); - + // Successful more restrictive change of min_delay with the current max_increase assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), From 4e3ff0d0487a7882f7559b741ba9445f4da0daf3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 17 Oct 2022 15:07:39 +0100 Subject: [PATCH 019/221] add commission.receiver --- frame/nomination-pools/src/lib.rs | 67 +++++++++++++++++++++-------- frame/nomination-pools/src/tests.rs | 49 ++++++++++++++++++++- 2 files changed, 97 insertions(+), 19 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 31b5f1724bb99..0562f71be3a1f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -555,6 +555,8 @@ pub struct Commission { /// An optional maximum commission that can be set by the pool root. Once set, this value /// cannot be updated. max: Option, + /// The account commission is paid to. + receiver: Option, /// Configiration around how often the commission can be updated, and metadata around the /// previous round of updates. throttle: Option>, @@ -562,7 +564,7 @@ pub struct Commission { impl Default for Commission { fn default() -> Self { - Self { current: Perbill::from_percent(0), max: None, throttle: None } + Self { current: Perbill::from_percent(0), max: None, receiver: None, throttle: None } } } @@ -761,6 +763,12 @@ impl BondedPool { self } + /// Set the pool's commission receiver. + fn set_commission_receiver(mut self, receiver: T::AccountId) -> Self { + self.commission.receiver = Some(receiver); + self + } + /// Set the pool's maximum commission. fn set_max_commission(&mut self, max_commission: Perbill) -> (Perbill, Perbill) { self.commission.max = Some(max_commission.clone()); @@ -1500,6 +1508,8 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, + /// A pool's commission receiver has been changed. + PoolCommissionReceiverChanged { pool_id: PoolId, receiver: T::AccountId }, /// A pool's commission setting has been changed. PoolCommissionUpdated { pool_id: PoolId, commission: Perbill }, /// A pool's maximum commission setting has been changed. @@ -1559,6 +1569,8 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, + /// No account has been set to receive commission. + NoCommissionReceiverSet, /// The pool's max commission cannot be set higher than the existing value. MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. @@ -2142,14 +2154,34 @@ pub mod pallet { Ok(()) } + /// Set the commissionr receiver of a pool. + /// + /// The dispatch origin of this call must be signed by the root role of the pool. + #[pallet::weight(0)] + #[transactional] + pub fn set_commission_receiver( + origin: OriginFor, + pool_id: PoolId, + receiver: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.set_commission_receiver(receiver.clone()).put(); + Self::deposit_event(Event::::PoolCommissionReceiverChanged { pool_id, receiver }); + Ok(()) + } + /// Set the commission of a pool. /// /// The dispatch origin of this call must be signed by the root role of the pool. /// If the pool has a max commission set, the commission supplied must be less or /// equal to the max. /// - /// If the max commission has _not_ yet been set, then the commission is not + /// If the max commission has _not yet_ been set, then the commission is not /// restricted. + /// A `receiver` must already be present before commission can be set. #[pallet::weight(0)] #[transactional] pub fn set_commission( @@ -2160,6 +2192,8 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + ensure!(bonded_pool.commission.receiver.is_some(), Error::::NoCommissionReceiverSet); + if let Some(throttle) = &bonded_pool.commission.throttle { ensure!( !throttle.throttling( @@ -2565,22 +2599,21 @@ impl Pallet { reward_pool.register_claimed_reward(pending_rewards); // If a non-zero commission has been applied to the pool, deduct the share from - // `pending_rewards` and send that amount to the pool `depositor`. + // `pending_rewards` and send that amount to the pool `depositor`. + // Defensive: The commission receiver is also checked for existence. if bonded_pool.commission.current > Perbill::from_percent(0) { - let pool_commission = bonded_pool.commission.current * pending_rewards; - pending_rewards -= pool_commission; - - // TODO: discuss a better solution than sending commission to the depositor. - // Currently, all other roles are optional and may not be set when commission - // needs to be received. - // For now: - // Transfer commission to the depositor. - T::Currency::transfer( - &bonded_pool.reward_account(), - &bonded_pool.roles.depositor, - pool_commission, - ExistenceRequirement::KeepAlive, - )?; + if let Some(receiver) = bonded_pool.commission.receiver { + let pool_commission = bonded_pool.commission.current * pending_rewards; + pending_rewards -= pool_commission; + + // Transfer pool_commission to the `receiver` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + &receiver, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } } // Transfer remaining payout to the member. diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 88a52970aa89e..38f1b8ef936a8 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -245,9 +245,39 @@ mod bonded_pool { }); } + #[test] + fn set_commission_receiver_works_with_error_tests() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1)), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission + ); + // Set a commission receiver for pool 1 to be the root account. + assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); + assert_eq!(BondedPool::::get(1).unwrap().commission.receiver.unwrap(), 900); + // Commission change events triggered successfully + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, + ] + ); + }); + } + #[test] fn set_commission_works() { ExtBuilder::default().build_and_execute(|| { + // Pre-requisite of setting commission is to have a receiver. + assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); // Set a commission pool 1 assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -264,6 +294,7 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(50) @@ -286,7 +317,13 @@ mod bonded_pool { Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), Error::::DoesNotHavePermission ); - + // Receiver has to be set before commission can be sed + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), + Error::::NoCommisionReceiverSet + ); + // Set a commission receiver to continue tests. + assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -300,6 +337,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(0), max: None, + receiver: Some(900), throttle: Some(CommissionThrottle { change_rate: (Perbill::from_percent(1), 2_u64), previous_set_at: None, @@ -325,6 +363,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(1), max: None, + receiver: Some(900), throttle: Some(CommissionThrottle { change_rate: (Perbill::from_percent(1), 2_u64), previous_set_at: Some(1_u64), @@ -384,6 +423,7 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(1) @@ -396,7 +436,7 @@ mod bonded_pool { pool_id: 1, commission: Perbill::from_percent(2), max_commission: Perbill::from_percent(2) - }, + } ] ); }); @@ -441,6 +481,9 @@ mod bonded_pool { Error::::MaxCommissionRestricted ); + // Set a commission receiver to continue tests. + assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); + // We will now set a commission to 75% and then amend the max commission to 50%. // The max commission change should decrease the current commission to 50%. assert_ok!(Pools::set_commission( @@ -458,6 +501,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(50), max: Some(Perbill::from_percent(50)), + receiver: Some(900), throttle: None } ); @@ -473,6 +517,7 @@ mod bonded_pool { commission: Perbill::from_percent(0), max_commission: Perbill::from_percent(90) }, + Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(75) From e29c5cceb9c10cb4fa4b5d9ef156508b28ee6c5c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 17 Oct 2022 15:09:15 +0100 Subject: [PATCH 020/221] fixes --- frame/nomination-pools/src/lib.rs | 6 +++--- frame/nomination-pools/src/tests.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 0562f71be3a1f..1bc63bce04120 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2599,17 +2599,17 @@ impl Pallet { reward_pool.register_claimed_reward(pending_rewards); // If a non-zero commission has been applied to the pool, deduct the share from - // `pending_rewards` and send that amount to the pool `depositor`. + // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission receiver is also checked for existence. if bonded_pool.commission.current > Perbill::from_percent(0) { - if let Some(receiver) = bonded_pool.commission.receiver { + if let Some(receiver) = &bonded_pool.commission.receiver { let pool_commission = bonded_pool.commission.current * pending_rewards; pending_rewards -= pool_commission; // Transfer pool_commission to the `receiver` account. T::Currency::transfer( &bonded_pool.reward_account(), - &receiver, + receiver, pool_commission, ExistenceRequirement::KeepAlive, )?; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 38f1b8ef936a8..5f357c310d45e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -320,7 +320,7 @@ mod bonded_pool { // Receiver has to be set before commission can be sed assert_noop!( Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), - Error::::NoCommisionReceiverSet + Error::::NoCommissionReceiverSet ); // Set a commission receiver to continue tests. assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); From 90fefd063f21c61211afc0420a1ef1485076a7ee Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 11:37:20 +0100 Subject: [PATCH 021/221] receive -> payee --- frame/nomination-pools/src/lib.rs | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 1bc63bce04120..282474953c1fc 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -541,8 +541,8 @@ pub struct PoolRoles { /// Pool commission. /// /// The pool depositor can set a commission upon a pool creation, from which the pool owner can -/// update thereafter. The `max` commission value can only be set once, as to prevent the commission -/// from repeatedly increasing. +/// update thereafter. The `max` commission value can only be decreased after the initial value is +/// set, as to prevent the commission from repeatedly increasing. /// /// A commission throttle is also optional, allowing the pool to set strict limits to how much /// commission can change in each update, and how often updates can take place. @@ -556,7 +556,7 @@ pub struct Commission { /// cannot be updated. max: Option, /// The account commission is paid to. - receiver: Option, + payee: Option, /// Configiration around how often the commission can be updated, and metadata around the /// previous round of updates. throttle: Option>, @@ -564,7 +564,7 @@ pub struct Commission { impl Default for Commission { fn default() -> Self { - Self { current: Perbill::from_percent(0), max: None, receiver: None, throttle: None } + Self { current: Perbill::from_percent(0), max: None, payee: None, throttle: None } } } @@ -763,9 +763,9 @@ impl BondedPool { self } - /// Set the pool's commission receiver. - fn set_commission_receiver(mut self, receiver: T::AccountId) -> Self { - self.commission.receiver = Some(receiver); + /// Set the pool's commission payee. + fn set_commission_payee(mut self, payee: T::AccountId) -> Self { + self.commission.payee = Some(payee); self } @@ -1508,8 +1508,8 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, - /// A pool's commission receiver has been changed. - PoolCommissionReceiverChanged { pool_id: PoolId, receiver: T::AccountId }, + /// A pool's commission payee has been changed. + PoolCommissionPayeeChanged { pool_id: PoolId, payee: T::AccountId }, /// A pool's commission setting has been changed. PoolCommissionUpdated { pool_id: PoolId, commission: Perbill }, /// A pool's maximum commission setting has been changed. @@ -1570,7 +1570,7 @@ pub mod pallet { /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, /// No account has been set to receive commission. - NoCommissionReceiverSet, + NoCommissionPayeeSet, /// The pool's max commission cannot be set higher than the existing value. MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. @@ -2154,22 +2154,22 @@ pub mod pallet { Ok(()) } - /// Set the commissionr receiver of a pool. + /// Set the commissionr payee of a pool. /// /// The dispatch origin of this call must be signed by the root role of the pool. #[pallet::weight(0)] #[transactional] - pub fn set_commission_receiver( + pub fn set_commission_payee( origin: OriginFor, pool_id: PoolId, - receiver: T::AccountId, + payee: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.set_commission_receiver(receiver.clone()).put(); - Self::deposit_event(Event::::PoolCommissionReceiverChanged { pool_id, receiver }); + bonded_pool.set_commission_payee(payee.clone()).put(); + Self::deposit_event(Event::::PoolCommissionPayeeChanged { pool_id, payee }); Ok(()) } @@ -2181,7 +2181,7 @@ pub mod pallet { /// /// If the max commission has _not yet_ been set, then the commission is not /// restricted. - /// A `receiver` must already be present before commission can be set. + /// A `payee` must already be present before commission can be set. #[pallet::weight(0)] #[transactional] pub fn set_commission( @@ -2192,7 +2192,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - ensure!(bonded_pool.commission.receiver.is_some(), Error::::NoCommissionReceiverSet); + ensure!(bonded_pool.commission.payee.is_some(), Error::::NoCommissionPayeeSet); if let Some(throttle) = &bonded_pool.commission.throttle { ensure!( @@ -2600,16 +2600,16 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. - // Defensive: The commission receiver is also checked for existence. + // Defensive: The commission payee is also checked for existence. if bonded_pool.commission.current > Perbill::from_percent(0) { - if let Some(receiver) = &bonded_pool.commission.receiver { + if let Some(payee) = &bonded_pool.commission.payee { let pool_commission = bonded_pool.commission.current * pending_rewards; pending_rewards -= pool_commission; - // Transfer pool_commission to the `receiver` account. + // Transfer pool_commission to the `payee` account. T::Currency::transfer( &bonded_pool.reward_account(), - receiver, + payee, pool_commission, ExistenceRequirement::KeepAlive, )?; From 72c16b0a3ce675f9b58274235f4c34f8480aff75 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 11:54:26 +0100 Subject: [PATCH 022/221] tests use payee --- frame/nomination-pools/src/tests.rs | 38 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5f357c310d45e..59ece8b66bb1e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -246,7 +246,7 @@ mod bonded_pool { } #[test] - fn set_commission_receiver_works_with_error_tests() { + fn set_commission_payee_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { // Provided pool does not exist assert_noop!( @@ -258,16 +258,16 @@ mod bonded_pool { Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), Error::::DoesNotHavePermission ); - // Set a commission receiver for pool 1 to be the root account. - assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); - assert_eq!(BondedPool::::get(1).unwrap().commission.receiver.unwrap(), 900); + // Set a commission payee for pool 1 to be the root account. + assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); + assert_eq!(BondedPool::::get(1).unwrap().commission.payee.unwrap(), 900); // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, + Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, ] ); }); @@ -276,8 +276,8 @@ mod bonded_pool { #[test] fn set_commission_works() { ExtBuilder::default().build_and_execute(|| { - // Pre-requisite of setting commission is to have a receiver. - assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); + // Pre-requisite of setting commission is to have a payee. + assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); // Set a commission pool 1 assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -294,7 +294,7 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, + Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(50) @@ -317,13 +317,13 @@ mod bonded_pool { Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), Error::::DoesNotHavePermission ); - // Receiver has to be set before commission can be sed + // Payee has to be set before commission can be sed assert_noop!( Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), - Error::::NoCommissionReceiverSet + Error::::NoCommissionPayeeSet ); - // Set a commission receiver to continue tests. - assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); + // Set a commission payee to continue tests. + assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -337,7 +337,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(0), max: None, - receiver: Some(900), + payee: Some(900), throttle: Some(CommissionThrottle { change_rate: (Perbill::from_percent(1), 2_u64), previous_set_at: None, @@ -363,7 +363,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(1), max: None, - receiver: Some(900), + payee: Some(900), throttle: Some(CommissionThrottle { change_rate: (Perbill::from_percent(1), 2_u64), previous_set_at: Some(1_u64), @@ -423,7 +423,7 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, + Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(1) @@ -481,8 +481,8 @@ mod bonded_pool { Error::::MaxCommissionRestricted ); - // Set a commission receiver to continue tests. - assert_ok!(Pools::set_commission_receiver(RuntimeOrigin::signed(900), 1, 900,)); + // Set a commission payee to continue tests. + assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); // We will now set a commission to 75% and then amend the max commission to 50%. // The max commission change should decrease the current commission to 50%. @@ -501,7 +501,7 @@ mod bonded_pool { Commission { current: Perbill::from_percent(50), max: Some(Perbill::from_percent(50)), - receiver: Some(900), + payee: Some(900), throttle: None } ); @@ -517,7 +517,7 @@ mod bonded_pool { commission: Perbill::from_percent(0), max_commission: Perbill::from_percent(90) }, - Event::PoolCommissionReceiverChanged { pool_id: 1, receiver: 900 }, + Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(75) From b15a086578ea339bcdbf771252e96f855c57b7cb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 13:01:11 +0100 Subject: [PATCH 023/221] use CommissionThrottlePrefs --- frame/nomination-pools/src/lib.rs | 41 ++++++++++------- frame/nomination-pools/src/tests.rs | 69 ++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 282474953c1fc..1cf1360ce0434 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -568,13 +568,25 @@ impl Default for Commission { } } +/// Pool commission throttle preferences. +/// +/// Throttle prefs need to be passed and configured together. This struct is used in +/// the `set_commission_throttle` call as well as in CommissionThrottle. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] +pub struct CommissionThrottlePrefs { + /// The maximum amount the commission can be updated by. + pub max_increase: Perbill, + /// How often an update can take place. + pub min_delay: BlockNumber, +} + +/// The pool root is able to set a commission throttle for their pool. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct CommissionThrottle { - /// The pool root is able to set the maximum amount the commission can be updated by, and how - /// often an update can take place. - pub change_rate: (Perbill, T::BlockNumber), + /// The change rate dictates how often and by how much commission can be updated. + pub change_rate: CommissionThrottlePrefs, /// The block the previous commission update took place. previous_set_at: Option, } @@ -584,16 +596,14 @@ impl CommissionThrottle { // 1. not enough blocks have passed since the previous commission update took place, and // 2. the new commission is larger than the maximum allowed increase. fn throttling(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { - let (max_increase, min_delay) = self.change_rate; - // check enough blocks have passed since the previous commission update took place if let Some(previous_set_at) = self.previous_set_at { - if current_block.saturating_sub(previous_set_at) < min_delay { + if current_block.saturating_sub(previous_set_at) < self.change_rate.min_delay { return true } } // check the commission change is larger than the maximum allowed increase - (*to).saturating_sub(*from) > max_increase + (*to).saturating_sub(*from) > self.change_rate.max_increase } } @@ -783,11 +793,13 @@ impl BondedPool { /// Set the pool's commission throttle settings. fn set_commission_throttle(mut self, max_increase: Perbill, min_delay: T::BlockNumber) -> Self { if let Some(throttle) = &self.commission.throttle { - self.commission.throttle = - Some(CommissionThrottle { change_rate: (max_increase, min_delay), ..*throttle }); + self.commission.throttle = Some(CommissionThrottle { + change_rate: CommissionThrottlePrefs { max_increase, min_delay }, + ..*throttle + }); } else { self.commission.throttle = Some(CommissionThrottle { - change_rate: (max_increase, min_delay), + change_rate: CommissionThrottlePrefs { max_increase, min_delay }, previous_set_at: None, }); } @@ -2259,21 +2271,20 @@ pub mod pallet { pub fn set_commission_throttle( origin: OriginFor, pool_id: PoolId, - max_increase: Perbill, - min_delay: T::BlockNumber, + prefs: CommissionThrottlePrefs, ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); if let Some(throttle) = &bonded_pool.commission.throttle { - let (current_max_increase, current_min_delay) = throttle.change_rate; ensure!( - !(max_increase > current_max_increase || min_delay < current_min_delay), + !(prefs.max_increase > throttle.change_rate.max_increase || + prefs.min_delay < throttle.change_rate.min_delay), Error::::CommissionThrottleNotAllowed ); } - bonded_pool.set_commission_throttle(max_increase, min_delay).put(); + bonded_pool.set_commission_throttle(prefs.max_increase, prefs.min_delay).put(); Ok(()) } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 59ece8b66bb1e..2ebf1e5f1da6e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -329,8 +329,10 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(1), - 2_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + } )); assert_eq!( BondedPool::::get(1).unwrap().commission, @@ -339,7 +341,10 @@ mod bonded_pool { max: None, payee: Some(900), throttle: Some(CommissionThrottle { - change_rate: (Perbill::from_percent(1), 2_u64), + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }, previous_set_at: None, }) } @@ -365,7 +370,10 @@ mod bonded_pool { max: None, payee: Some(900), throttle: Some(CommissionThrottle { - change_rate: (Perbill::from_percent(1), 2_u64), + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }, previous_set_at: Some(1_u64), }) } @@ -540,8 +548,10 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 9999, - Perbill::from_percent(5), - 1000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } ), Error::::PoolNotFound ); @@ -550,8 +560,10 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(1), 1, - Perbill::from_percent(5), - 1000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } ), Error::::DoesNotHavePermission ); @@ -560,13 +572,18 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), - 1000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } )); assert_eq!( BondedPools::::get(1).unwrap().commission.throttle, Some(CommissionThrottle { - change_rate: (Perbill::from_percent(5), 1000_u64), + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + }, previous_set_at: None }) ); @@ -577,8 +594,10 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), - 500_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 500_u64 + } ), Error::::CommissionThrottleNotAllowed ); @@ -589,8 +608,10 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(10), - 1000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(10), + min_delay: 1000_u64 + } ), Error::::CommissionThrottleNotAllowed ); @@ -599,22 +620,28 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), - 2000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 2000_u64 + } )); // Successful more restrictive change of max_increase with the current min_delay assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), - 2000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 2000_u64 + } )); // Successful more restrictive change of both max_increase and min_delay assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(3), - 3000_u64 + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(3), + min_delay: 3000_u64 + } )); }); } From 42a0ce494436ef9d6c702d95726fe8d0f76ffdbf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 13:01:46 +0100 Subject: [PATCH 024/221] fmt --- frame/nomination-pools/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 1cf1360ce0434..2419e5875026e 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -569,8 +569,8 @@ impl Default for Commission { } /// Pool commission throttle preferences. -/// -/// Throttle prefs need to be passed and configured together. This struct is used in +/// +/// Throttle prefs need to be passed and configured together. This struct is used in /// the `set_commission_throttle` call as well as in CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionThrottlePrefs { @@ -580,7 +580,7 @@ pub struct CommissionThrottlePrefs { pub min_delay: BlockNumber, } -/// The pool root is able to set a commission throttle for their pool. +/// The pool root is able to set a commission throttle for their pool. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] From 1c0a180f3aecba199a106829267610b864c98e0d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 13:41:17 +0100 Subject: [PATCH 025/221] make commission.current Option --- frame/nomination-pools/src/lib.rs | 46 ++++++++++++++++------------- frame/nomination-pools/src/tests.rs | 8 ++--- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2419e5875026e..d32cb52ff991b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -551,7 +551,7 @@ pub struct PoolRoles { #[scale_info(skip_type_params(T))] pub struct Commission { /// The active commission rate of the pool. - current: Perbill, + current: Option, /// An optional maximum commission that can be set by the pool root. Once set, this value /// cannot be updated. max: Option, @@ -564,7 +564,7 @@ pub struct Commission { impl Default for Commission { fn default() -> Self { - Self { current: Perbill::from_percent(0), max: None, payee: None, throttle: None } + Self { current: None, max: None, payee: None, throttle: None } } } @@ -760,7 +760,7 @@ impl BondedPool { /// Set the pool's commission. fn set_commission(mut self, commission: &Perbill) -> Self { - self.commission.current = *commission; + self.commission.current = Some(*commission); // if throttle is configured, record the current block as the previously // updated commission. @@ -784,10 +784,12 @@ impl BondedPool { self.commission.max = Some(max_commission.clone()); // if the pool's current commission is higher than the updated maximum commission, // decrease it to the new maximum commission. - if self.commission.current > max_commission { - self.commission.current = max_commission; + if let Some(commission) = self.commission.current { + if commission > max_commission { + self.commission.current = Some(max_commission); + } } - (self.commission.current, max_commission) + (self.commission.current.unwrap_or(Perbill::from_percent(0)), max_commission) } /// Set the pool's commission throttle settings. @@ -2209,7 +2211,7 @@ pub mod pallet { if let Some(throttle) = &bonded_pool.commission.throttle { ensure!( !throttle.throttling( - &bonded_pool.commission.current, + &bonded_pool.commission.current.unwrap_or(Perbill::from_percent(0)), &commission, &>::block_number() ), @@ -2248,10 +2250,10 @@ pub mod pallet { ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); } - let (current, max) = bonded_pool.set_max_commission(max_commission); + let (commission, max) = bonded_pool.set_max_commission(max_commission); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, - commission: current, + commission, max_commission: max, }); @@ -2612,18 +2614,20 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission payee is also checked for existence. - if bonded_pool.commission.current > Perbill::from_percent(0) { - if let Some(payee) = &bonded_pool.commission.payee { - let pool_commission = bonded_pool.commission.current * pending_rewards; - pending_rewards -= pool_commission; - - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - payee, - pool_commission, - ExistenceRequirement::KeepAlive, - )?; + if let Some(commission) = bonded_pool.commission.current { + if commission > Perbill::from_percent(0) { + if let Some(payee) = &bonded_pool.commission.payee { + let pool_commission = commission * pending_rewards; + pending_rewards -= pool_commission; + + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + payee, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 2ebf1e5f1da6e..a86e747138e4f 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -285,7 +285,7 @@ mod bonded_pool { Perbill::from_percent(50) )); assert_eq!( - BondedPool::::get(1).unwrap().commission.current, + BondedPool::::get(1).unwrap().commission.current.unwrap(), Perbill::from_percent(50) ); // Commission change events triggered successfully @@ -337,7 +337,7 @@ mod bonded_pool { assert_eq!( BondedPool::::get(1).unwrap().commission, Commission { - current: Perbill::from_percent(0), + current: None, max: None, payee: Some(900), throttle: Some(CommissionThrottle { @@ -366,7 +366,7 @@ mod bonded_pool { assert_eq!( BondedPool::::get(1).unwrap().commission, Commission { - current: Perbill::from_percent(1), + current: Some(Perbill::from_percent(1)), max: None, payee: Some(900), throttle: Some(CommissionThrottle { @@ -507,7 +507,7 @@ mod bonded_pool { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { - current: Perbill::from_percent(50), + current: Some(Perbill::from_percent(50)), max: Some(Perbill::from_percent(50)), payee: Some(900), throttle: None From de05cec9e8ed3c9f757c2168ab36572472523655 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 13:52:08 +0100 Subject: [PATCH 026/221] set None if 0% commission --- frame/nomination-pools/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d32cb52ff991b..855ec0b2f3b26 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -760,8 +760,11 @@ impl BondedPool { /// Set the pool's commission. fn set_commission(mut self, commission: &Perbill) -> Self { - self.commission.current = Some(*commission); - + if commission > &Perbill::from_percent(0) { + self.commission.current = Some(*commission); + } else { + self.commission.current = None; + } // if throttle is configured, record the current block as the previously // updated commission. if let Some(throttle) = &self.commission.throttle { From 3562d63c88fe05aaf4eabf98ef864214ece4d84e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 14:59:48 +0100 Subject: [PATCH 027/221] make commission Option --- frame/nomination-pools/src/lib.rs | 187 +++++++++++++++--------- frame/nomination-pools/src/migration.rs | 2 +- frame/nomination-pools/src/tests.rs | 54 +++---- 3 files changed, 145 insertions(+), 98 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 855ec0b2f3b26..c0d503b1c84dd 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -613,7 +613,7 @@ impl CommissionThrottle { #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { /// The commission rate, if any, of the pool. - pub commission: Commission, + pub commission: Option>, /// Count of members that belong to the pool. pub member_counter: u32, /// Total points of all the members in the pool who are actively bonded. @@ -656,7 +656,7 @@ impl BondedPool { Self { id, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: Zero::zero(), points: Zero::zero(), roles, @@ -760,54 +760,91 @@ impl BondedPool { /// Set the pool's commission. fn set_commission(mut self, commission: &Perbill) -> Self { - if commission > &Perbill::from_percent(0) { - self.commission.current = Some(*commission); - } else { - self.commission.current = None; - } + // force a commission of `None` if a 0% commission is provided. + let new_current = + if commission > &Perbill::from_percent(0) { Some(*commission) } else { None }; + // update commission if `Some(commission)` is to be inserted, or `None` otherwise. + self.commission = match &self.commission { + Some(c) => Some(Commission { current: new_current, ..c.clone() }), + None => match new_current.is_some() { + true => Some(Commission { current: new_current, ..Commission::default() }), + false => None, + }, + }; + // if throttle is configured, record the current block as the previously // updated commission. - if let Some(throttle) = &self.commission.throttle { - self.commission.throttle = Some(CommissionThrottle { - previous_set_at: Some(>::block_number()), - ..*throttle - }); + if let Some(c) = &self.commission { + if let Some(throttle) = &c.throttle { + self.commission = Some(Commission { + throttle: Some(CommissionThrottle { + previous_set_at: Some(>::block_number()), + ..*throttle + }), + ..c.clone() + }); + } } self } /// Set the pool's commission payee. fn set_commission_payee(mut self, payee: T::AccountId) -> Self { - self.commission.payee = Some(payee); + self.commission = match &self.commission { + Some(c) => Some(Commission { payee: Some(payee), ..c.clone() }), + None => Some(Commission { payee: Some(payee), ..Commission::default() }), + }; self } /// Set the pool's maximum commission. fn set_max_commission(&mut self, max_commission: Perbill) -> (Perbill, Perbill) { - self.commission.max = Some(max_commission.clone()); - // if the pool's current commission is higher than the updated maximum commission, - // decrease it to the new maximum commission. - if let Some(commission) = self.commission.current { - if commission > max_commission { - self.commission.current = Some(max_commission); - } - } - (self.commission.current.unwrap_or(Perbill::from_percent(0)), max_commission) + self.commission = match &self.commission { + Some(c) => { + Some(Commission { + max: Some(max_commission), + // if the pool's current commission is higher than the updated maximum + // commission, decrease it to the new maximum commission. + current: if c.current.unwrap_or(Perbill::from_percent(0)) > max_commission { + Some(max_commission) + } else { + c.current + }, + ..c.clone() + }) + }, + None => Some(Commission { max: Some(max_commission), ..Commission::default() }), + }; + ( + self.commission.clone() + .unwrap_or(Commission::default()) + .current + .unwrap_or(Perbill::from_percent(0)), + max_commission, + ) } /// Set the pool's commission throttle settings. - fn set_commission_throttle(mut self, max_increase: Perbill, min_delay: T::BlockNumber) -> Self { - if let Some(throttle) = &self.commission.throttle { - self.commission.throttle = Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { max_increase, min_delay }, - ..*throttle - }); - } else { - self.commission.throttle = Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { max_increase, min_delay }, - previous_set_at: None, - }); - } + fn set_commission_throttle( + mut self, + change_rate: CommissionThrottlePrefs, + ) -> Self { + self.commission = match &self.commission { + Some(c) => match &c.throttle { + Some(t) => Some(Commission { + throttle: Some(CommissionThrottle { change_rate, ..t.clone() }), + ..c.clone() + }), + None => Some(Commission { + throttle: Some(CommissionThrottle { change_rate, previous_set_at: None }), + ..c.clone() + }), + }, + None => Some(Commission { + throttle: Some(CommissionThrottle { change_rate, previous_set_at: None }), + ..Commission::default() + }), + }; self } @@ -2209,20 +2246,24 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - ensure!(bonded_pool.commission.payee.is_some(), Error::::NoCommissionPayeeSet); - - if let Some(throttle) = &bonded_pool.commission.throttle { - ensure!( - !throttle.throttling( - &bonded_pool.commission.current.unwrap_or(Perbill::from_percent(0)), - &commission, - &>::block_number() - ), - Error::::CommissionChangeThrottled - ); - } - if let Some(max) = bonded_pool.commission.max { - ensure!(commission <= max, Error::::CommissionExceedsMaximum); + ensure!(bonded_pool.commission.is_some(), Error::::NoCommissionPayeeSet); + + if let Some(c) = &bonded_pool.commission { + ensure!(c.payee.is_some(), Error::::NoCommissionPayeeSet); + + if let Some(throttle) = &c.throttle { + ensure!( + !throttle.throttling( + &c.current.unwrap_or(Perbill::from_percent(0)), + &commission, + &>::block_number() + ), + Error::::CommissionChangeThrottled + ); + } + if let Some(max) = c.max { + ensure!(commission <= max, Error::::CommissionExceedsMaximum); + } } bonded_pool.set_commission(&commission).put(); @@ -2249,8 +2290,10 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - if let Some(existing_max) = bonded_pool.commission.max { - ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); + if let Some(c) = &bonded_pool.commission { + if let Some(existing_max) = c.max { + ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); + } } let (commission, max) = bonded_pool.set_max_commission(max_commission); @@ -2282,14 +2325,16 @@ pub mod pallet { let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - if let Some(throttle) = &bonded_pool.commission.throttle { - ensure!( - !(prefs.max_increase > throttle.change_rate.max_increase || - prefs.min_delay < throttle.change_rate.min_delay), - Error::::CommissionThrottleNotAllowed - ); + if let Some(c) = &bonded_pool.commission { + if let Some(throttle) = &c.throttle { + ensure!( + !(prefs.max_increase > throttle.change_rate.max_increase || + prefs.min_delay < throttle.change_rate.min_delay), + Error::::CommissionThrottleNotAllowed + ); + } } - bonded_pool.set_commission_throttle(prefs.max_increase, prefs.min_delay).put(); + bonded_pool.set_commission_throttle(prefs).put(); Ok(()) } @@ -2617,19 +2662,21 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission payee is also checked for existence. - if let Some(commission) = bonded_pool.commission.current { - if commission > Perbill::from_percent(0) { - if let Some(payee) = &bonded_pool.commission.payee { - let pool_commission = commission * pending_rewards; - pending_rewards -= pool_commission; - - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - payee, - pool_commission, - ExistenceRequirement::KeepAlive, - )?; + if let Some(c) = &bonded_pool.commission { + if let Some(commission) = c.current { + if commission > Perbill::from_percent(0) { + if let Some(payee) = &c.payee { + let pool_commission = commission * pending_rewards; + pending_rewards -= pool_commission; + + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + payee, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } } } } diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index e2bf5de30a2d0..bd15aed2006ec 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -53,7 +53,7 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: self.member_counter, points: self.points, state: self.state, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a86e747138e4f..a993fd5f8b2e3 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -55,7 +55,7 @@ fn test_setup_works() { BondedPool:: { id: last_pool, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 10, roles: DEFAULT_ROLES, @@ -99,7 +99,7 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -155,7 +155,7 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -204,7 +204,7 @@ mod bonded_pool { let pool = BondedPool:: { id: 123, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -260,7 +260,7 @@ mod bonded_pool { ); // Set a commission payee for pool 1 to be the root account. assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); - assert_eq!(BondedPool::::get(1).unwrap().commission.payee.unwrap(), 900); + assert_eq!(BondedPool::::get(1).unwrap().commission.unwrap().payee.unwrap(), 900); // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), @@ -285,7 +285,7 @@ mod bonded_pool { Perbill::from_percent(50) )); assert_eq!( - BondedPool::::get(1).unwrap().commission.current.unwrap(), + BondedPool::::get(1).unwrap().commission.unwrap().current.unwrap(), Perbill::from_percent(50) ); // Commission change events triggered successfully @@ -336,7 +336,7 @@ mod bonded_pool { )); assert_eq!( BondedPool::::get(1).unwrap().commission, - Commission { + Some(Commission { current: None, max: None, payee: Some(900), @@ -347,7 +347,7 @@ mod bonded_pool { }, previous_set_at: None, }) - } + }) ); // We now try to increase commission to 5% (5% increase): this should be throttled. @@ -365,7 +365,7 @@ mod bonded_pool { )); assert_eq!( BondedPool::::get(1).unwrap().commission, - Commission { + Some(Commission { current: Some(Perbill::from_percent(1)), max: None, payee: Some(900), @@ -376,7 +376,7 @@ mod bonded_pool { }, previous_set_at: Some(1_u64), }) - } + }) ); // Attempt to increase the commission an additional 1% (now 2%) again immediately. @@ -475,7 +475,7 @@ mod bonded_pool { Perbill::from_percent(90) )); assert_eq!( - BondedPools::::get(1).unwrap().commission.max, + BondedPools::::get(1).unwrap().commission.unwrap().max, Some(Perbill::from_percent(90)) ); @@ -506,12 +506,12 @@ mod bonded_pool { )); assert_eq!( BondedPools::::get(1).unwrap().commission, - Commission { + Some(Commission { current: Some(Perbill::from_percent(50)), max: Some(Perbill::from_percent(50)), payee: Some(900), throttle: None - } + }) ); // Commission change events triggered successfully @@ -578,7 +578,7 @@ mod bonded_pool { } )); assert_eq!( - BondedPools::::get(1).unwrap().commission.throttle, + BondedPools::::get(1).unwrap().commission.unwrap().throttle, Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { max_increase: Perbill::from_percent(5), @@ -836,7 +836,7 @@ mod join { let bonded = |points, member_counter| BondedPool:: { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter, points, roles: DEFAULT_ROLES, @@ -920,7 +920,7 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -990,7 +990,7 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -2768,7 +2768,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 0, roles: DEFAULT_ROLES, @@ -2805,7 +2805,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 3, points: 560, roles: DEFAULT_ROLES, @@ -2846,7 +2846,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 3, points: 10, roles: DEFAULT_ROLES, @@ -2890,7 +2890,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 0, roles: DEFAULT_ROLES, @@ -3019,7 +3019,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 3, points: 10, // Only 10 points because 200 + 100 was unbonded roles: DEFAULT_ROLES, @@ -3170,7 +3170,7 @@ mod unbond { BondedPool:: { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 10, roles: DEFAULT_ROLES, @@ -3883,7 +3883,7 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 3, points: 10, roles: DEFAULT_ROLES, @@ -3964,7 +3964,7 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 2, points: 10, roles: DEFAULT_ROLES, @@ -4515,7 +4515,7 @@ mod create { BondedPool { id: 2, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: StakingMock::minimum_bond(), roles: PoolRoles { @@ -4580,7 +4580,7 @@ mod create { BondedPool:: { id: 2, inner: BondedPoolInner { - commission: Commission::default(), + commission: None, member_counter: 1, points: 10, roles: DEFAULT_ROLES, From 185a0a990fa193b1ac30a78b5630789921dfabc2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 16:33:43 +0100 Subject: [PATCH 028/221] combine commission + payee --- frame/nomination-pools/src/lib.rs | 144 ++++++++++++---------------- frame/nomination-pools/src/tests.rs | 96 ++++++------------- 2 files changed, 93 insertions(+), 147 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c0d503b1c84dd..8343a808d32d5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -550,13 +550,11 @@ pub struct PoolRoles { #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct Commission { - /// The active commission rate of the pool. - current: Option, + /// The active commission rate of the pool along with the account commission is paid to. + current: Option<(Perbill, T::AccountId)>, /// An optional maximum commission that can be set by the pool root. Once set, this value /// cannot be updated. max: Option, - /// The account commission is paid to. - payee: Option, /// Configiration around how often the commission can be updated, and metadata around the /// previous round of updates. throttle: Option>, @@ -564,7 +562,7 @@ pub struct Commission { impl Default for Commission { fn default() -> Self { - Self { current: None, max: None, payee: None, throttle: None } + Self { current: None, max: None, throttle: None } } } @@ -680,6 +678,28 @@ impl BondedPool { Pallet::::create_reward_account(self.id) } + /// Get the current commission percentage of this pool. + fn commission(&self) -> Perbill { + match &self.commission { + Some(c) => match &c.current { + Some(cur) => cur.0.clone(), + None => Perbill::from_percent(0), + }, + None => Perbill::from_percent(0), + } + } + + /// Get the current commission payee of this pool. + fn commission_payee(&self) -> Option { + match &self.commission { + Some(c) => match &c.current { + Some(cur) => Some(cur.1.clone()), + None => None, + }, + None => None, + } + } + /// Consume self and put into storage. fn put(self) { BondedPools::::insert(self.id, BondedPoolInner { ..self.inner }); @@ -759,10 +779,11 @@ impl BondedPool { } /// Set the pool's commission. - fn set_commission(mut self, commission: &Perbill) -> Self { + fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { // force a commission of `None` if a 0% commission is provided. let new_current = - if commission > &Perbill::from_percent(0) { Some(*commission) } else { None }; + if commission > &Perbill::from_percent(0) { Some((*commission, payee)) } else { None }; + // update commission if `Some(commission)` is to be inserted, or `None` otherwise. self.commission = match &self.commission { Some(c) => Some(Commission { current: new_current, ..c.clone() }), @@ -772,7 +793,7 @@ impl BondedPool { }, }; - // if throttle is configured, record the current block as the previously + // if throttle is present, record the current block as the previously // updated commission. if let Some(c) = &self.commission { if let Some(throttle) = &c.throttle { @@ -788,40 +809,29 @@ impl BondedPool { self } - /// Set the pool's commission payee. - fn set_commission_payee(mut self, payee: T::AccountId) -> Self { - self.commission = match &self.commission { - Some(c) => Some(Commission { payee: Some(payee), ..c.clone() }), - None => Some(Commission { payee: Some(payee), ..Commission::default() }), - }; - self - } - /// Set the pool's maximum commission. - fn set_max_commission(&mut self, max_commission: Perbill) -> (Perbill, Perbill) { + fn set_max_commission(mut self, max_commission: Perbill) -> Self { self.commission = match &self.commission { Some(c) => { Some(Commission { max: Some(max_commission), // if the pool's current commission is higher than the updated maximum // commission, decrease it to the new maximum commission. - current: if c.current.unwrap_or(Perbill::from_percent(0)) > max_commission { - Some(max_commission) + current: if let Some(cur) = &c.current { + if cur.0 > max_commission { + Some((max_commission, cur.1.clone())) + } else { + Some((cur.0, cur.1.clone())) + } } else { - c.current + c.current.clone() }, ..c.clone() }) }, None => Some(Commission { max: Some(max_commission), ..Commission::default() }), }; - ( - self.commission.clone() - .unwrap_or(Commission::default()) - .current - .unwrap_or(Perbill::from_percent(0)), - max_commission, - ) + self } /// Set the pool's commission throttle settings. @@ -1565,9 +1575,9 @@ pub mod pallet { /// A pool's commission payee has been changed. PoolCommissionPayeeChanged { pool_id: PoolId, payee: T::AccountId }, /// A pool's commission setting has been changed. - PoolCommissionUpdated { pool_id: PoolId, commission: Perbill }, + PoolCommissionUpdated { pool_id: PoolId, commission: Perbill, payee: T::AccountId }, /// A pool's maximum commission setting has been changed. - PoolMaxCommissionUpdated { pool_id: PoolId, commission: Perbill, max_commission: Perbill }, + PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, } #[pallet::error] @@ -2208,25 +2218,6 @@ pub mod pallet { Ok(()) } - /// Set the commissionr payee of a pool. - /// - /// The dispatch origin of this call must be signed by the root role of the pool. - #[pallet::weight(0)] - #[transactional] - pub fn set_commission_payee( - origin: OriginFor, - pool_id: PoolId, - payee: T::AccountId, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; - ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - - bonded_pool.set_commission_payee(payee.clone()).put(); - Self::deposit_event(Event::::PoolCommissionPayeeChanged { pool_id, payee }); - Ok(()) - } - /// Set the commission of a pool. /// /// The dispatch origin of this call must be signed by the root role of the pool. @@ -2242,19 +2233,20 @@ pub mod pallet { origin: OriginFor, pool_id: PoolId, commission: Perbill, + payee: T::AccountId, ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - ensure!(bonded_pool.commission.is_some(), Error::::NoCommissionPayeeSet); + let commission_percentage = bonded_pool.commission(); if let Some(c) = &bonded_pool.commission { - ensure!(c.payee.is_some(), Error::::NoCommissionPayeeSet); - + // TODO: enable this when payee becomes optional. + // ensure!(!(c.is_none() && payee.is_none()), Error::::NoCommissionPayeeSet); if let Some(throttle) = &c.throttle { ensure!( !throttle.throttling( - &c.current.unwrap_or(Perbill::from_percent(0)), + &commission_percentage, &commission, &>::block_number() ), @@ -2265,9 +2257,8 @@ pub mod pallet { ensure!(commission <= max, Error::::CommissionExceedsMaximum); } } - - bonded_pool.set_commission(&commission).put(); - Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission }); + bonded_pool.set_commission_current(&commission, payee.clone()).put(); + Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission, payee }); Ok(()) } @@ -2287,7 +2278,7 @@ pub mod pallet { max_commission: Perbill, ) -> DispatchResult { let who = ensure_signed(origin)?; - let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); if let Some(c) = &bonded_pool.commission { @@ -2296,14 +2287,8 @@ pub mod pallet { } } - let (commission, max) = bonded_pool.set_max_commission(max_commission); - Self::deposit_event(Event::::PoolMaxCommissionUpdated { - pool_id, - commission, - max_commission: max, - }); - - bonded_pool.put(); + bonded_pool.set_max_commission(max_commission.clone()).put(); + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) } @@ -2662,22 +2647,19 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission payee is also checked for existence. - if let Some(c) = &bonded_pool.commission { - if let Some(commission) = c.current { - if commission > Perbill::from_percent(0) { - if let Some(payee) = &c.payee { - let pool_commission = commission * pending_rewards; - pending_rewards -= pool_commission; - - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - payee, - pool_commission, - ExistenceRequirement::KeepAlive, - )?; - } - } + let commission = &bonded_pool.commission(); + if commission > &Perbill::from_percent(0) { + if let Some(payee) = &bonded_pool.commission_payee() { + let pool_commission = *commission * pending_rewards; + pending_rewards -= pool_commission; + + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + payee, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a993fd5f8b2e3..a338872813d24 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -245,47 +245,18 @@ mod bonded_pool { }); } - #[test] - fn set_commission_payee_works_with_error_tests() { - ExtBuilder::default().build_and_execute(|| { - // Provided pool does not exist - assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1)), - Error::::PoolNotFound - ); - // Sender does not have permission to set commission - assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), - Error::::DoesNotHavePermission - ); - // Set a commission payee for pool 1 to be the root account. - assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); - assert_eq!(BondedPool::::get(1).unwrap().commission.unwrap().payee.unwrap(), 900); - // Commission change events triggered successfully - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, - ] - ); - }); - } - #[test] fn set_commission_works() { ExtBuilder::default().build_and_execute(|| { - // Pre-requisite of setting commission is to have a payee. - assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); // Set a commission pool 1 assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(50) + Perbill::from_percent(50), + 900 )); assert_eq!( - BondedPool::::get(1).unwrap().commission.unwrap().current.unwrap(), + BondedPool::::get(1).unwrap().commission.unwrap().current.unwrap().0, Perbill::from_percent(50) ); // Commission change events triggered successfully @@ -294,10 +265,10 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(50) + commission: Perbill::from_percent(50), + payee: 900 } ] ); @@ -309,21 +280,19 @@ mod bonded_pool { ExtBuilder::default().build_and_execute(|| { // Provided pool does not exist assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1)), + Pools::set_commission( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(1), + 900 + ), Error::::PoolNotFound ); // Sender does not have permission to set commission assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5), 900), Error::::DoesNotHavePermission ); - // Payee has to be set before commission can be sed - assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), - Error::::NoCommissionPayeeSet - ); - // Set a commission payee to continue tests. - assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -339,7 +308,6 @@ mod bonded_pool { Some(Commission { current: None, max: None, - payee: Some(900), throttle: Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { max_increase: Perbill::from_percent(1), @@ -352,7 +320,7 @@ mod bonded_pool { // We now try to increase commission to 5% (5% increase): this should be throttled. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5)), + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5), 900), Error::::CommissionChangeThrottled ); @@ -361,14 +329,14 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(1) + Perbill::from_percent(1), + 900 )); assert_eq!( BondedPool::::get(1).unwrap().commission, Some(Commission { - current: Some(Perbill::from_percent(1)), + current: Some((Perbill::from_percent(1), 900)), max: None, - payee: Some(900), throttle: Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { max_increase: Perbill::from_percent(1), @@ -383,7 +351,7 @@ mod bonded_pool { // this will fail as `previous_set_at` is now the current block, and at least 2 // blocks need to pass before we can set commission again. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(2)), + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(2), 900), Error::::CommissionChangeThrottled ); @@ -394,7 +362,8 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(2) + Perbill::from_percent(2), + 900 )); // Run 2 blocks into the future, to block 5. @@ -403,7 +372,7 @@ mod bonded_pool { // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is // still at play. An attempted commission change now to 4% (+2% increase) should fail. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(4)), + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(4), 900), Error::::CommissionChangeThrottled ); @@ -421,7 +390,7 @@ mod bonded_pool { // change rate allowance, but the max_commission will now prevent us from going any // higher. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(3)), + Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(3), 900), Error::::CommissionExceedsMaximum ); @@ -431,18 +400,18 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(1) + commission: Perbill::from_percent(1), + payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(2) + commission: Perbill::from_percent(2), + payee: 900 }, Event::PoolMaxCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(2), max_commission: Perbill::from_percent(2) } ] @@ -489,15 +458,13 @@ mod bonded_pool { Error::::MaxCommissionRestricted ); - // Set a commission payee to continue tests. - assert_ok!(Pools::set_commission_payee(RuntimeOrigin::signed(900), 1, 900,)); - // We will now set a commission to 75% and then amend the max commission to 50%. // The max commission change should decrease the current commission to 50%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(75) + Perbill::from_percent(75), + 900, )); assert_ok!(Pools::set_max_commission( RuntimeOrigin::signed(900), @@ -507,9 +474,8 @@ mod bonded_pool { assert_eq!( BondedPools::::get(1).unwrap().commission, Some(Commission { - current: Some(Perbill::from_percent(50)), + current: Some((Perbill::from_percent(50), 900)), max: Some(Perbill::from_percent(50)), - payee: Some(900), throttle: None }) ); @@ -522,17 +488,15 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolMaxCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(0), max_commission: Perbill::from_percent(90) }, - Event::PoolCommissionPayeeChanged { pool_id: 1, payee: 900 }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(75) + commission: Perbill::from_percent(75), + payee: 900 }, Event::PoolMaxCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(50), max_commission: Perbill::from_percent(50) } ] From 316c4488e07aa696b8134379a32a7ddb1f41872d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 17:34:21 +0100 Subject: [PATCH 029/221] make payee optional after set --- frame/nomination-pools/src/lib.rs | 20 +++++++--- frame/nomination-pools/src/tests.rs | 61 +++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 8343a808d32d5..d051759c18fe6 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2233,16 +2233,22 @@ pub mod pallet { origin: OriginFor, pool_id: PoolId, commission: Perbill, - payee: T::AccountId, + payee: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + // fallback to the pool's current commission payee if `None` is provided, + // otherwise fail the call. + let new_payee = if let Some(p) = payee { + p + } else { + bonded_pool.commission_payee().ok_or(Error::::NoCommissionPayeeSet)? + }; + let commission_percentage = bonded_pool.commission(); if let Some(c) = &bonded_pool.commission { - // TODO: enable this when payee becomes optional. - // ensure!(!(c.is_none() && payee.is_none()), Error::::NoCommissionPayeeSet); if let Some(throttle) = &c.throttle { ensure!( !throttle.throttling( @@ -2257,8 +2263,12 @@ pub mod pallet { ensure!(commission <= max, Error::::CommissionExceedsMaximum); } } - bonded_pool.set_commission_current(&commission, payee.clone()).put(); - Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission, payee }); + bonded_pool.set_commission_current(&commission, new_payee.clone()).put(); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + commission, + payee: new_payee, + }); Ok(()) } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a338872813d24..9cc1f5c664c20 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -253,7 +253,7 @@ mod bonded_pool { RuntimeOrigin::signed(900), 1, Perbill::from_percent(50), - 900 + Some(900) )); assert_eq!( BondedPool::::get(1).unwrap().commission.unwrap().current.unwrap().0, @@ -284,15 +284,31 @@ mod bonded_pool { RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1), - 900 + Some(900) ), Error::::PoolNotFound ); // Sender does not have permission to set commission assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5), 900), + Pools::set_commission( + RuntimeOrigin::signed(1), + 1, + Perbill::from_percent(5), + Some(900) + ), Error::::DoesNotHavePermission ); + // No payee has been provided, and none is present. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + None + ), + Error::::NoCommissionPayeeSet + ); + // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -320,17 +336,22 @@ mod bonded_pool { // We now try to increase commission to 5% (5% increase): this should be throttled. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(5), 900), + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + Some(900) + ), Error::::CommissionChangeThrottled ); - // We now try to increase commission by 1%. This should work, and set the - // `previous_set_at` field. + // We now try to increase commission by 1%, and provide an initial payee. + // This should work, and set the `previous_set_at` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, Perbill::from_percent(1), - 900 + Some(900) )); assert_eq!( BondedPool::::get(1).unwrap().commission, @@ -351,7 +372,12 @@ mod bonded_pool { // this will fail as `previous_set_at` is now the current block, and at least 2 // blocks need to pass before we can set commission again. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(2), 900), + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(2), + Some(900) + ), Error::::CommissionChangeThrottled ); @@ -359,11 +385,12 @@ mod bonded_pool { run_blocks(2); // We can now successfully increase the commission again, to 2%. + // We no longer need to provide the payee again. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, Perbill::from_percent(2), - 900 + None )); // Run 2 blocks into the future, to block 5. @@ -372,7 +399,12 @@ mod bonded_pool { // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is // still at play. An attempted commission change now to 4% (+2% increase) should fail. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(4), 900), + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(4), + None, + ), Error::::CommissionChangeThrottled ); @@ -390,7 +422,12 @@ mod bonded_pool { // change rate allowance, but the max_commission will now prevent us from going any // higher. assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, Perbill::from_percent(3), 900), + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(3), + None, + ), Error::::CommissionExceedsMaximum ); @@ -464,7 +501,7 @@ mod bonded_pool { RuntimeOrigin::signed(900), 1, Perbill::from_percent(75), - 900, + Some(900), )); assert_ok!(Pools::set_max_commission( RuntimeOrigin::signed(900), From 5ef7d0c2dd7beeb1bbd84b94ba6e1fd7edbf81da Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 23 Oct 2022 23:43:27 +0100 Subject: [PATCH 030/221] combinators for set_commission_throttle --- frame/nomination-pools/src/lib.rs | 51 +++++++++++++------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d051759c18fe6..e89efcdc08685 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -593,10 +593,12 @@ impl CommissionThrottle { // A commission change will be throttled (disallowed) if: // 1. not enough blocks have passed since the previous commission update took place, and // 2. the new commission is larger than the maximum allowed increase. - fn throttling(&self, from: &Perbill, to: &Perbill, current_block: &T::BlockNumber) -> bool { + fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { // check enough blocks have passed since the previous commission update took place if let Some(previous_set_at) = self.previous_set_at { - if current_block.saturating_sub(previous_set_at) < self.change_rate.min_delay { + if >::block_number().saturating_sub(previous_set_at) < + self.change_rate.min_delay + { return true } } @@ -780,7 +782,8 @@ impl BondedPool { /// Set the pool's commission. fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { - // force a commission of `None` if a 0% commission is provided. + // Force a commission of `None` if a 0% commission is provided. + // Note that a 0% commission will replace the whole tuple with None. let new_current = if commission > &Perbill::from_percent(0) { Some((*commission, payee)) } else { None }; @@ -839,22 +842,17 @@ impl BondedPool { mut self, change_rate: CommissionThrottlePrefs, ) -> Self { - self.commission = match &self.commission { - Some(c) => match &c.throttle { - Some(t) => Some(Commission { - throttle: Some(CommissionThrottle { change_rate, ..t.clone() }), - ..c.clone() - }), - None => Some(Commission { - throttle: Some(CommissionThrottle { change_rate, previous_set_at: None }), - ..c.clone() - }), - }, - None => Some(Commission { - throttle: Some(CommissionThrottle { change_rate, previous_set_at: None }), - ..Commission::default() - }), - }; + self.commission = + self.commission + .clone() + .map_or(Some(Commission::default()), |c| Some(c)) + .map(|c| Commission { + throttle: c.throttle.map_or( + Some(CommissionThrottle { change_rate, previous_set_at: None }), + |t| Some(CommissionThrottle { change_rate, ..t }), + ), + ..c + }); self } @@ -2238,10 +2236,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - - // fallback to the pool's current commission payee if `None` is provided, - // otherwise fail the call. - let new_payee = if let Some(p) = payee { + let final_payee = if let Some(p) = payee { p } else { bonded_pool.commission_payee().ok_or(Error::::NoCommissionPayeeSet)? @@ -2251,11 +2246,7 @@ pub mod pallet { if let Some(c) = &bonded_pool.commission { if let Some(throttle) = &c.throttle { ensure!( - !throttle.throttling( - &commission_percentage, - &commission, - &>::block_number() - ), + !throttle.throttling(&commission_percentage, &commission), Error::::CommissionChangeThrottled ); } @@ -2263,11 +2254,11 @@ pub mod pallet { ensure!(commission <= max, Error::::CommissionExceedsMaximum); } } - bonded_pool.set_commission_current(&commission, new_payee.clone()).put(); + bonded_pool.set_commission_current(&commission, final_payee.clone()).put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission, - payee: new_payee, + payee: final_payee, }); Ok(()) } From 51073aaf77f2e06bccbf94b580577430bf7e4080 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 00:33:27 +0100 Subject: [PATCH 031/221] CommissionThrottle::default/ use take() --- frame/nomination-pools/src/lib.rs | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e89efcdc08685..f1216f2c84a14 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -588,6 +588,20 @@ pub struct CommissionThrottle { /// The block the previous commission update took place. previous_set_at: Option, } +/// The commission throttle defaults, used for readability and convenience. +/// `change_rate` is not utilised in this code, but the provided values would +/// be quite sensible for throttling. +impl Default for CommissionThrottle { + fn default() -> Self { + Self { + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(1), + min_delay: T::BlockNumber::from(1000_u32), + }, + previous_set_at: None, + } + } +} impl CommissionThrottle { // A commission change will be throttled (disallowed) if: @@ -842,17 +856,17 @@ impl BondedPool { mut self, change_rate: CommissionThrottlePrefs, ) -> Self { - self.commission = - self.commission - .clone() - .map_or(Some(Commission::default()), |c| Some(c)) - .map(|c| Commission { - throttle: c.throttle.map_or( - Some(CommissionThrottle { change_rate, previous_set_at: None }), - |t| Some(CommissionThrottle { change_rate, ..t }), - ), - ..c - }); + self.commission = self + .commission + .take() + .map_or(Some(Commission::default()), |c| Some(c)) + .map(|c| Commission { + throttle: c.throttle.map_or( + Some(CommissionThrottle { change_rate, ..CommissionThrottle::default() }), + |t| Some(CommissionThrottle { change_rate, ..t }), + ), + ..c + }); self } From e1fcfb7ac150d6e5dcc29a68cdfd5252b471f11a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 15:47:22 +0100 Subject: [PATCH 032/221] refactor bonded_pool.commission() Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/nomination-pools/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f1216f2c84a14..673859c2b02e1 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -696,13 +696,7 @@ impl BondedPool { /// Get the current commission percentage of this pool. fn commission(&self) -> Perbill { - match &self.commission { - Some(c) => match &c.current { - Some(cur) => cur.0.clone(), - None => Perbill::from_percent(0), - }, - None => Perbill::from_percent(0), - } + self.commission.and_then(|c| c.current.map(|(x, _)| x)).unwrap_or(Zero::zero) } /// Get the current commission payee of this pool. From 5bdb153bc8a8a614f7f89b5fb0288fa8f2830b7d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 16:03:49 +0100 Subject: [PATCH 033/221] fix commission() & rm Default --- frame/nomination-pools/src/lib.rs | 41 +++++++++++-------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 673859c2b02e1..a711a601d81d3 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -588,20 +588,6 @@ pub struct CommissionThrottle { /// The block the previous commission update took place. previous_set_at: Option, } -/// The commission throttle defaults, used for readability and convenience. -/// `change_rate` is not utilised in this code, but the provided values would -/// be quite sensible for throttling. -impl Default for CommissionThrottle { - fn default() -> Self { - Self { - change_rate: CommissionThrottlePrefs { - max_increase: Perbill::from_percent(1), - min_delay: T::BlockNumber::from(1000_u32), - }, - previous_set_at: None, - } - } -} impl CommissionThrottle { // A commission change will be throttled (disallowed) if: @@ -696,7 +682,10 @@ impl BondedPool { /// Get the current commission percentage of this pool. fn commission(&self) -> Perbill { - self.commission.and_then(|c| c.current.map(|(x, _)| x)).unwrap_or(Zero::zero) + self.commission + .as_ref() + .and_then(|c| c.current.as_ref().map(|(x, _)| *x)) + .unwrap_or(Perbill::zero()) } /// Get the current commission payee of this pool. @@ -850,17 +839,17 @@ impl BondedPool { mut self, change_rate: CommissionThrottlePrefs, ) -> Self { - self.commission = self - .commission - .take() - .map_or(Some(Commission::default()), |c| Some(c)) - .map(|c| Commission { - throttle: c.throttle.map_or( - Some(CommissionThrottle { change_rate, ..CommissionThrottle::default() }), - |t| Some(CommissionThrottle { change_rate, ..t }), - ), - ..c - }); + self.commission = + self.commission + .take() + .map_or(Some(Commission::default()), |c| Some(c)) + .map(|c| Commission { + throttle: c.throttle.map_or( + Some(CommissionThrottle { change_rate, previous_set_at: None }), + |t| Some(CommissionThrottle { change_rate, ..t }), + ), + ..c + }); self } From 80906cbf6510421bee519298996cbaef1d2e66b3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 16:05:36 +0100 Subject: [PATCH 034/221] amend comments --- frame/nomination-pools/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a711a601d81d3..0f49e5e0ca67f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -590,11 +590,12 @@ pub struct CommissionThrottle { } impl CommissionThrottle { - // A commission change will be throttled (disallowed) if: - // 1. not enough blocks have passed since the previous commission update took place, and - // 2. the new commission is larger than the maximum allowed increase. + /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. + /// A commission change will be throttled (disallowed) if: + /// 1. not enough blocks have passed since the previous commission update took place, and + /// 2. the new commission is larger than the maximum allowed increase. fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { - // check enough blocks have passed since the previous commission update took place + // check enough blocks have passed since the previous commission update took place. if let Some(previous_set_at) = self.previous_set_at { if >::block_number().saturating_sub(previous_set_at) < self.change_rate.min_delay From 48965e59409dd53c27d5d09c71c3bda1aa43a1b4 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 16:16:57 +0100 Subject: [PATCH 035/221] use commission --- frame/nomination-pools/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 0f49e5e0ca67f..f4df63bc46c56 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -782,14 +782,14 @@ impl BondedPool { fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { // Force a commission of `None` if a 0% commission is provided. // Note that a 0% commission will replace the whole tuple with None. - let new_current = + let commission = if commission > &Perbill::from_percent(0) { Some((*commission, payee)) } else { None }; // update commission if `Some(commission)` is to be inserted, or `None` otherwise. self.commission = match &self.commission { - Some(c) => Some(Commission { current: new_current, ..c.clone() }), - None => match new_current.is_some() { - true => Some(Commission { current: new_current, ..Commission::default() }), + Some(c) => Some(Commission { current: commission, ..c.clone() }), + None => match commission.is_some() { + true => Some(Commission { current: commission, ..Commission::default() }), false => None, }, }; From e6084f12d36e7d1254db1bbde15f160aaffe5526 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 16:19:59 +0100 Subject: [PATCH 036/221] rm deprecated event --- frame/nomination-pools/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f4df63bc46c56..dd7e6b2146c3e 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1568,8 +1568,6 @@ pub mod pallet { PoolSlashed { pool_id: PoolId, balance: BalanceOf }, /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, - /// A pool's commission payee has been changed. - PoolCommissionPayeeChanged { pool_id: PoolId, payee: T::AccountId }, /// A pool's commission setting has been changed. PoolCommissionUpdated { pool_id: PoolId, commission: Perbill, payee: T::AccountId }, /// A pool's maximum commission setting has been changed. From b3ba86963dbbd59bb4705e52cbbfb3ea6d99ba1d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 18:05:09 +0100 Subject: [PATCH 037/221] refactor throttle() --- frame/nomination-pools/src/lib.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index dd7e6b2146c3e..b4f3f4d9d18cb 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -590,20 +590,23 @@ pub struct CommissionThrottle { } impl CommissionThrottle { - /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. + /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. /// A commission change will be throttled (disallowed) if: /// 1. not enough blocks have passed since the previous commission update took place, and /// 2. the new commission is larger than the maximum allowed increase. fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { // check enough blocks have passed since the previous commission update took place. - if let Some(previous_set_at) = self.previous_set_at { - if >::block_number().saturating_sub(previous_set_at) < - self.change_rate.min_delay - { - return true + if self + .previous_set_at + .as_ref() + .map(|p| { + >::block_number().saturating_sub(*p) < + self.change_rate.min_delay + }) + .unwrap_or(false) { + return true; } - } - // check the commission change is larger than the maximum allowed increase + // check the commission change is larger than the maximum allowed increase. (*to).saturating_sub(*from) > self.change_rate.max_increase } } From 5550651e1c0bfcd3a8e5d1c1ab1d4725a8f86f45 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 24 Oct 2022 18:45:48 +0100 Subject: [PATCH 038/221] commission_payee revise --- frame/nomination-pools/src/lib.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b4f3f4d9d18cb..b0c8582d33a07 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -603,9 +603,10 @@ impl CommissionThrottle { >::block_number().saturating_sub(*p) < self.change_rate.min_delay }) - .unwrap_or(false) { - return true; - } + .unwrap_or(false) + { + return true + } // check the commission change is larger than the maximum allowed increase. (*to).saturating_sub(*from) > self.change_rate.max_increase } @@ -694,13 +695,9 @@ impl BondedPool { /// Get the current commission payee of this pool. fn commission_payee(&self) -> Option { - match &self.commission { - Some(c) => match &c.current { - Some(cur) => Some(cur.1.clone()), - None => None, - }, - None => None, - } + self.commission + .as_ref() + .map_or(None, |c| c.current.as_ref().map_or(None, |(_, p)| Some(p.clone()))) } /// Consume self and put into storage. From 359cce05175af7fefef401bdb7ed79a9a016433c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 25 Oct 2022 04:09:33 +0100 Subject: [PATCH 039/221] commission_payee returns Option<&T::AccountId> --- frame/nomination-pools/src/lib.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b0c8582d33a07..2366f2befcadb 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -693,11 +693,12 @@ impl BondedPool { .unwrap_or(Perbill::zero()) } - /// Get the current commission payee of this pool. - fn commission_payee(&self) -> Option { + /// Gets the current commission payee of this pool as a reference. + fn commission_payee(&self) -> Option<&T::AccountId> { self.commission .as_ref() - .map_or(None, |c| c.current.as_ref().map_or(None, |(_, p)| Some(p.clone()))) + .and_then(|c| c.current.as_ref().map(|(_, p)| p)) + .or(None) } /// Consume self and put into storage. @@ -2235,7 +2236,10 @@ pub mod pallet { let final_payee = if let Some(p) = payee { p } else { - bonded_pool.commission_payee().ok_or(Error::::NoCommissionPayeeSet)? + bonded_pool + .commission_payee() + .map(|p| p.clone()) + .ok_or(Error::::NoCommissionPayeeSet)? }; let commission_percentage = bonded_pool.commission(); @@ -2653,7 +2657,7 @@ impl Pallet { // Transfer pool_commission to the `payee` account. T::Currency::transfer( &bonded_pool.reward_account(), - payee, + payee.clone(), pool_commission, ExistenceRequirement::KeepAlive, )?; From bd49e966ac457511d34aaa70bb0b5e2ed35af77f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 25 Oct 2022 04:57:34 +0100 Subject: [PATCH 040/221] refactor set_commission_current --- frame/nomination-pools/src/lib.rs | 32 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2366f2befcadb..17c917ebbf3f1 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -780,20 +780,26 @@ impl BondedPool { } /// Set the pool's commission. + /// + /// update commission accordingly based on `commission` and `payee`. If Commission does not yet + /// exist, it is bootstrapped with default() and populated with the supplied `commission` and + /// `payee` values. Otherwise, the existing commission is updated. + /// + /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { - // Force a commission of `None` if a 0% commission is provided. - // Note that a 0% commission will replace the whole tuple with None. - let commission = - if commission > &Perbill::from_percent(0) { Some((*commission, payee)) } else { None }; - - // update commission if `Some(commission)` is to be inserted, or `None` otherwise. - self.commission = match &self.commission { - Some(c) => Some(Commission { current: commission, ..c.clone() }), - None => match commission.is_some() { - true => Some(Commission { current: commission, ..Commission::default() }), - false => None, - }, - }; + self.commission = self + .commission + .take() + .map_or(Some(Commission::default()), |c| Some(c)) + .map(|c| Commission { + current: if commission > &Perbill::from_percent(0) { + Some((*commission, payee)) + } else { + None + }, + ..c + }) + .or(None); // if throttle is present, record the current block as the previously // updated commission. From a0507f2c27fd91bb5af37c7a0ae258ba768c2ded Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 13:44:54 +0200 Subject: [PATCH 041/221] refactor set_commission_current --- frame/nomination-pools/src/lib.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 17c917ebbf3f1..c02e10d69ee74 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -781,10 +781,12 @@ impl BondedPool { /// Set the pool's commission. /// - /// update commission accordingly based on `commission` and `payee`. If Commission does not yet + /// Update commission accordingly based on `commission` and `payee`. If Commission does not yet /// exist, it is bootstrapped with default() and populated with the supplied `commission` and /// `payee` values. Otherwise, the existing commission is updated. /// + /// If throttle is present, record the current block as the previously updated commission. + /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { self.commission = self @@ -797,23 +799,13 @@ impl BondedPool { } else { None }, + throttle: c.throttle.map(|t| CommissionThrottle { + previous_set_at: Some(>::block_number()), + ..t + }).or(None), ..c }) .or(None); - - // if throttle is present, record the current block as the previously - // updated commission. - if let Some(c) = &self.commission { - if let Some(throttle) = &c.throttle { - self.commission = Some(Commission { - throttle: Some(CommissionThrottle { - previous_set_at: Some(>::block_number()), - ..*throttle - }), - ..c.clone() - }); - } - } self } From 088df1ec052bb68d54dc46f0550f6970d7b9f8cb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 14:01:54 +0200 Subject: [PATCH 042/221] some mapping --- frame/nomination-pools/src/lib.rs | 66 ++++++++++++++----------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c02e10d69ee74..adc108eb18ff2 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -786,7 +786,7 @@ impl BondedPool { /// `payee` values. Otherwise, the existing commission is updated. /// /// If throttle is present, record the current block as the previously updated commission. - /// + /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { self.commission = self @@ -799,38 +799,34 @@ impl BondedPool { } else { None }, - throttle: c.throttle.map(|t| CommissionThrottle { - previous_set_at: Some(>::block_number()), - ..t - }).or(None), + throttle: c + .throttle + .map(|t| CommissionThrottle { + previous_set_at: Some(>::block_number()), + ..t + }) + .or(None), ..c - }) - .or(None); + }); self } /// Set the pool's maximum commission. fn set_max_commission(mut self, max_commission: Perbill) -> Self { - self.commission = match &self.commission { - Some(c) => { - Some(Commission { - max: Some(max_commission), - // if the pool's current commission is higher than the updated maximum - // commission, decrease it to the new maximum commission. - current: if let Some(cur) = &c.current { - if cur.0 > max_commission { - Some((max_commission, cur.1.clone())) - } else { - Some((cur.0, cur.1.clone())) - } - } else { - c.current.clone() - }, - ..c.clone() - }) - }, - None => Some(Commission { max: Some(max_commission), ..Commission::default() }), - }; + self.commission = self + .commission + .take() + .map_or(Some(Commission::default()), |c| Some(c)) + .map(|c| Commission { + max: Some(max_commission), + // if the pool's current commission is higher than the updated maximum + // commission, decrease it to the new maximum commission. + current: c + .current + .map(|x| if x.0 > max_commission { (max_commission, x.1) } else { x }) + .or(None), + ..c + }); self } @@ -2231,16 +2227,15 @@ pub mod pallet { let who = ensure_signed(origin)?; let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - let final_payee = if let Some(p) = payee { - p - } else { - bonded_pool - .commission_payee() - .map(|p| p.clone()) - .ok_or(Error::::NoCommissionPayeeSet)? - }; + + let final_payee = payee + .map(|p| p) + .or(bonded_pool.commission_payee().map(|p| p.clone())) + .ok_or(Error::::NoCommissionPayeeSet)?; let commission_percentage = bonded_pool.commission(); + + // TODO: probably a better way to write this, with self.commission_throttled. if let Some(c) = &bonded_pool.commission { if let Some(throttle) = &c.throttle { ensure!( @@ -2285,7 +2280,6 @@ pub mod pallet { ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); } } - bonded_pool.set_max_commission(max_commission.clone()).put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) From f492894fd28f373a7a63b68b313753e307cfaa51 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 14:32:19 +0200 Subject: [PATCH 043/221] clearer ensures, mv throttling() --- frame/nomination-pools/src/lib.rs | 77 +++++++++++++++++-------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index adc108eb18ff2..76decdcf10ac9 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -566,6 +566,31 @@ impl Default for Commission { } } +impl Commission { + /// TODO: fine-tune documentation + /// + /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. + /// A commission change will be throttled (disallowed) if: + /// 1. not enough blocks have passed since the previous commission update took place, and + /// 2. the new commission is larger than the maximum allowed increase. + fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { + let throttle = self.throttle.as_ref().map(|t| t).or(None); + if let Some(t) = throttle { + // check for `min_delay` throttling + if t.previous_set_at.map_or(false, |p| { + >::block_number().saturating_sub(p) < + t.change_rate.min_delay + }) { + return true + } + // check for `max_increase` throttling + (*to).saturating_sub(*from) > t.change_rate.max_increase + } else { + return false + } + } +} + /// Pool commission throttle preferences. /// /// Throttle prefs need to be passed and configured together. This struct is used in @@ -589,29 +614,6 @@ pub struct CommissionThrottle { previous_set_at: Option, } -impl CommissionThrottle { - /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. - /// A commission change will be throttled (disallowed) if: - /// 1. not enough blocks have passed since the previous commission update took place, and - /// 2. the new commission is larger than the maximum allowed increase. - fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { - // check enough blocks have passed since the previous commission update took place. - if self - .previous_set_at - .as_ref() - .map(|p| { - >::block_number().saturating_sub(*p) < - self.change_rate.min_delay - }) - .unwrap_or(false) - { - return true - } - // check the commission change is larger than the maximum allowed increase. - (*to).saturating_sub(*from) > self.change_rate.max_increase - } -} - /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] @@ -2235,18 +2237,23 @@ pub mod pallet { let commission_percentage = bonded_pool.commission(); - // TODO: probably a better way to write this, with self.commission_throttled. - if let Some(c) = &bonded_pool.commission { - if let Some(throttle) = &c.throttle { - ensure!( - !throttle.throttling(&commission_percentage, &commission), - Error::::CommissionChangeThrottled - ); - } - if let Some(max) = c.max { - ensure!(commission <= max, Error::::CommissionExceedsMaximum); - } - } + ensure!( + &bonded_pool + .commission + .as_ref() + .map(|c| !c.throttling(&commission_percentage, &commission)) + .unwrap_or(true), + Error::::CommissionChangeThrottled + ); + ensure!( + &bonded_pool + .commission + .as_ref() + .map(|c| c.max.map(|m| commission <= m).unwrap_or(true)) + .unwrap_or(true), + Error::::CommissionExceedsMaximum + ); + bonded_pool.set_commission_current(&commission, final_payee.clone()).put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, From 4feab193d3bd0477a1b23ee718eaf03d960a94e6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 14:50:48 +0200 Subject: [PATCH 044/221] refactor percentage() into Commission --- frame/nomination-pools/src/lib.rs | 66 +++++++++++++++++-------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 76decdcf10ac9..93b646c48f13b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -589,6 +589,13 @@ impl Commission { return false } } + + /// TODO: fine-tune documentation + /// Get the current commission percentage of this pool. Returns zero if commission has not yet + /// been configured. + fn percentage(&self) -> Perbill { + self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) + } } /// Pool commission throttle preferences. @@ -687,12 +694,9 @@ impl BondedPool { Pallet::::create_reward_account(self.id) } - /// Get the current commission percentage of this pool. - fn commission(&self) -> Perbill { - self.commission - .as_ref() - .and_then(|c| c.current.as_ref().map(|(x, _)| *x)) - .unwrap_or(Perbill::zero()) + /// Get the current commission of this pool. + fn commission(&self) -> Option<&Commission> { + self.commission.as_ref().map(|c| c).or(None) } /// Gets the current commission payee of this pool as a reference. @@ -2223,7 +2227,7 @@ pub mod pallet { pub fn set_commission( origin: OriginFor, pool_id: PoolId, - commission: Perbill, + new_commission: Perbill, payee: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -2235,29 +2239,27 @@ pub mod pallet { .or(bonded_pool.commission_payee().map(|p| p.clone())) .ok_or(Error::::NoCommissionPayeeSet)?; - let commission_percentage = bonded_pool.commission(); + let commission = &bonded_pool.commission(); ensure!( - &bonded_pool - .commission + commission .as_ref() - .map(|c| !c.throttling(&commission_percentage, &commission)) + .map(|c| !c.throttling(&c.percentage(), &new_commission)) .unwrap_or(true), Error::::CommissionChangeThrottled ); ensure!( - &bonded_pool - .commission + commission .as_ref() - .map(|c| c.max.map(|m| commission <= m).unwrap_or(true)) + .map(|c| c.max.map(|m| new_commission <= m).unwrap_or(true)) .unwrap_or(true), Error::::CommissionExceedsMaximum ); - bonded_pool.set_commission_current(&commission, final_payee.clone()).put(); + bonded_pool.set_commission_current(&new_commission, final_payee.clone()).put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, - commission, + commission: new_commission, payee: final_payee, }); Ok(()) @@ -2647,21 +2649,25 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission payee is also checked for existence. - let commission = &bonded_pool.commission(); - if commission > &Perbill::from_percent(0) { - if let Some(payee) = &bonded_pool.commission_payee() { - let pool_commission = *commission * pending_rewards; - pending_rewards -= pool_commission; - - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - payee.clone(), - pool_commission, - ExistenceRequirement::KeepAlive, - )?; + let _ = &bonded_pool.commission().map(|c| -> DispatchResult { + let commission_percent = c.percentage(); + + if commission_percent > Perbill::from_percent(0) { + if let Some(payee) = &bonded_pool.commission_payee() { + let pool_commission = commission_percent * pending_rewards; + pending_rewards -= pool_commission; + + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + payee.clone(), + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } } - } + Ok(()) + }); // Transfer remaining payout to the member. T::Currency::transfer( From 4ee687cbe085b66e25602331062356b6ff36b81a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 15:14:25 +0200 Subject: [PATCH 045/221] throttling uses self.percentage() --- frame/nomination-pools/src/lib.rs | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 93b646c48f13b..06cc8924b2921 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -567,13 +567,17 @@ impl Default for Commission { } impl Commission { - /// TODO: fine-tune documentation - /// - /// Returns true if this changing from `from` to `to` would exhaust the throttle limit. - /// A commission change will be throttled (disallowed) if: + /// Get the current commission percentage of this pool. + /// Returns zero if commission has not yet been configured. + fn percentage(&self) -> Perbill { + self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) + } + + /// Returns true if a commission percentage updating to `to` would exhaust the throttle limit. + /// A commission update will be throttled (disallowed) if: /// 1. not enough blocks have passed since the previous commission update took place, and /// 2. the new commission is larger than the maximum allowed increase. - fn throttling(&self, from: &Perbill, to: &Perbill) -> bool { + fn throttling(&self, to: &Perbill) -> bool { let throttle = self.throttle.as_ref().map(|t| t).or(None); if let Some(t) = throttle { // check for `min_delay` throttling @@ -584,18 +588,11 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(*from) > t.change_rate.max_increase + (*to).saturating_sub(self.percentage()) > t.change_rate.max_increase } else { return false } } - - /// TODO: fine-tune documentation - /// Get the current commission percentage of this pool. Returns zero if commission has not yet - /// been configured. - fn percentage(&self) -> Perbill { - self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) - } } /// Pool commission throttle preferences. @@ -800,7 +797,7 @@ impl BondedPool { .take() .map_or(Some(Commission::default()), |c| Some(c)) .map(|c| Commission { - current: if commission > &Perbill::from_percent(0) { + current: if commission > &Perbill::zero() { Some((*commission, payee)) } else { None @@ -2242,10 +2239,7 @@ pub mod pallet { let commission = &bonded_pool.commission(); ensure!( - commission - .as_ref() - .map(|c| !c.throttling(&c.percentage(), &new_commission)) - .unwrap_or(true), + commission.as_ref().map(|c| !c.throttling(&new_commission)).unwrap_or(true), Error::::CommissionChangeThrottled ); ensure!( @@ -2652,7 +2646,7 @@ impl Pallet { let _ = &bonded_pool.commission().map(|c| -> DispatchResult { let commission_percent = c.percentage(); - if commission_percent > Perbill::from_percent(0) { + if commission_percent > Perbill::zero() { if let Some(payee) = &bonded_pool.commission_payee() { let pool_commission = commission_percent * pending_rewards; pending_rewards -= pool_commission; From c836bfb96eb49a4efac91c4abf1c6fc6452bdc0a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 27 Oct 2022 16:05:08 +0200 Subject: [PATCH 046/221] don't return self --- frame/nomination-pools/src/lib.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 06cc8924b2921..b8f70b2993dd1 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -791,7 +791,7 @@ impl BondedPool { /// If throttle is present, record the current block as the previously updated commission. /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. - fn set_commission_current(mut self, commission: &Perbill, payee: T::AccountId) -> Self { + fn set_commission_current(&mut self, commission: &Perbill, payee: T::AccountId) { self.commission = self .commission .take() @@ -811,11 +811,10 @@ impl BondedPool { .or(None), ..c }); - self } /// Set the pool's maximum commission. - fn set_max_commission(mut self, max_commission: Perbill) -> Self { + fn set_max_commission(&mut self, max_commission: Perbill) { self.commission = self .commission .take() @@ -830,14 +829,10 @@ impl BondedPool { .or(None), ..c }); - self } /// Set the pool's commission throttle settings. - fn set_commission_throttle( - mut self, - change_rate: CommissionThrottlePrefs, - ) -> Self { + fn set_commission_throttle(&mut self, change_rate: CommissionThrottlePrefs) { self.commission = self.commission .take() @@ -849,7 +844,6 @@ impl BondedPool { ), ..c }); - self } fn is_root(&self, who: &T::AccountId) -> bool { @@ -2228,7 +2222,7 @@ pub mod pallet { payee: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); let final_payee = payee @@ -2250,7 +2244,8 @@ pub mod pallet { Error::::CommissionExceedsMaximum ); - bonded_pool.set_commission_current(&new_commission, final_payee.clone()).put(); + bonded_pool.set_commission_current(&new_commission, final_payee.clone()); + bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, commission: new_commission, @@ -2275,7 +2270,7 @@ pub mod pallet { max_commission: Perbill, ) -> DispatchResult { let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); if let Some(c) = &bonded_pool.commission { @@ -2283,7 +2278,8 @@ pub mod pallet { ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); } } - bonded_pool.set_max_commission(max_commission.clone()).put(); + bonded_pool.set_max_commission(max_commission.clone()); + bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) } @@ -2303,7 +2299,7 @@ pub mod pallet { prefs: CommissionThrottlePrefs, ) -> DispatchResult { let who = ensure_signed(origin)?; - let bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); if let Some(c) = &bonded_pool.commission { @@ -2315,7 +2311,8 @@ pub mod pallet { ); } } - bonded_pool.set_commission_throttle(prefs).put(); + bonded_pool.set_commission_throttle(prefs); + bonded_pool.put(); Ok(()) } From 983278a43c4cd8af59da0e9534659a0a15ec9862 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 28 Oct 2022 00:21:12 +0200 Subject: [PATCH 047/221] make closure + 1 payout test --- frame/nomination-pools/src/lib.rs | 49 ++++++++++++++------------- frame/nomination-pools/src/tests.rs | 52 ++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b8f70b2993dd1..3cc3a4e7c4dfe 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -691,11 +691,6 @@ impl BondedPool { Pallet::::create_reward_account(self.id) } - /// Get the current commission of this pool. - fn commission(&self) -> Option<&Commission> { - self.commission.as_ref().map(|c| c).or(None) - } - /// Gets the current commission payee of this pool as a reference. fn commission_payee(&self) -> Option<&T::AccountId> { self.commission @@ -2230,7 +2225,7 @@ pub mod pallet { .or(bonded_pool.commission_payee().map(|p| p.clone())) .ok_or(Error::::NoCommissionPayeeSet)?; - let commission = &bonded_pool.commission(); + let commission = &bonded_pool.commission; ensure!( commission.as_ref().map(|c| !c.throttling(&new_commission)).unwrap_or(true), @@ -2640,25 +2635,33 @@ impl Pallet { // If a non-zero commission has been applied to the pool, deduct the share from // `pending_rewards` and send that amount to the pool `depositor`. // Defensive: The commission payee is also checked for existence. - let _ = &bonded_pool.commission().map(|c| -> DispatchResult { - let commission_percent = c.percentage(); - - if commission_percent > Perbill::zero() { - if let Some(payee) = &bonded_pool.commission_payee() { - let pool_commission = commission_percent * pending_rewards; - pending_rewards -= pool_commission; - - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - payee.clone(), - pool_commission, - ExistenceRequirement::KeepAlive, - )?; + let get_commission = |b: &BondedPool| -> (BalanceOf, Option) { + if let Some(c) = &b.commission { + let commission_percent = c.percentage(); + if commission_percent > Perbill::zero() { + let payee = b.commission_payee().map(|p| p.clone()).or(None); + if payee.is_some() { + return (commission_percent * pending_rewards, None) + } } } - Ok(()) - }); + (Zero::zero(), None) + }; + + let (pool_commission, payee) = get_commission(&bonded_pool); + pending_rewards = pending_rewards.saturating_sub(pool_commission); + + if pool_commission != BalanceOf::::zero() { + if let Some(p) = payee { + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + &p, + pool_commission, + ExistenceRequirement::KeepAlive, + )?; + } + } // Transfer remaining payout to the member. T::Currency::transfer( diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 9cc1f5c664c20..17aba6aa40f58 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -255,10 +255,11 @@ mod bonded_pool { Perbill::from_percent(50), Some(900) )); - assert_eq!( - BondedPool::::get(1).unwrap().commission.unwrap().current.unwrap().0, - Perbill::from_percent(50) - ); + + let commission = BondedPool::::get(1).unwrap().commission.unwrap(); + + assert_eq!(commission.percentage(), Perbill::from_percent(50)); + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), @@ -1300,6 +1301,49 @@ mod claim_payout { }); } + #[test] + fn do_reward_payout_with_33_percent_commission() { + let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); + + ExtBuilder::default().build_and_execute(|| { + let (mut member, mut bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + let ed = Balances::minimum_balance(); + + // Set a commission pool 1 to 50%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Perbill::from_percent(33), + Some(2) + )); + + // fetch bonded pool again with upated commission + bonded_pool = BondedPool::::get(1).unwrap(); + + // Given the pool has earned some rewards for the first time + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut bonded_pool, + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(33), payee: 2 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 7 }, + ] + ); + }) + } + #[test] fn do_reward_payout_works_with_a_pool_of_1() { let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); From 62e617e53fa58b04a2e0cb0057466cf7b0ead7ab Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 28 Oct 2022 13:27:31 +0200 Subject: [PATCH 048/221] includ payee --- frame/nomination-pools/src/lib.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3cc3a4e7c4dfe..3f544a71d944b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2641,7 +2641,7 @@ impl Pallet { if commission_percent > Perbill::zero() { let payee = b.commission_payee().map(|p| p.clone()).or(None); if payee.is_some() { - return (commission_percent * pending_rewards, None) + return (commission_percent * pending_rewards, payee) } } } @@ -2651,24 +2651,12 @@ impl Pallet { let (pool_commission, payee) = get_commission(&bonded_pool); pending_rewards = pending_rewards.saturating_sub(pool_commission); - if pool_commission != BalanceOf::::zero() { - if let Some(p) = payee { - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - &p, - pool_commission, - ExistenceRequirement::KeepAlive, - )?; - } - } - // Transfer remaining payout to the member. T::Currency::transfer( &bonded_pool.reward_account(), &member_account, pending_rewards, - ExistenceRequirement::AllowDeath, + ExistenceRequirement::KeepAlive, )?; Self::deposit_event(Event::::PaidOut { @@ -2677,6 +2665,18 @@ impl Pallet { payout: pending_rewards, }); + if pool_commission != BalanceOf::::zero() { + if let Some(p) = payee { + // Transfer pool_commission to the `payee` account. + T::Currency::transfer( + &bonded_pool.reward_account(), + &p, + pool_commission, + ExistenceRequirement::AllowDeath, + )?; + } + } + Ok(pending_rewards) } From 25ec735bb49a386dd41d4495226cfa74f376e104 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 28 Oct 2022 14:03:28 +0200 Subject: [PATCH 049/221] add commission to `PaidOut`, use withdraw --- frame/nomination-pools/src/lib.rs | 32 ++-- frame/nomination-pools/src/migration.rs | 1 + frame/nomination-pools/src/tests.rs | 226 +++++++++++++----------- 3 files changed, 140 insertions(+), 119 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3f544a71d944b..f3433d018c002 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -275,7 +275,7 @@ use frame_support::{ storage::bounded_btree_map::BoundedBTreeMap, traits::{ Currency, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, - ExistenceRequirement, Get, + ExistenceRequirement, Get, tokens::WithdrawReasons, }, transactional, CloneNoBound, DefaultNoBound, RuntimeDebugNoBound, }; @@ -1505,7 +1505,7 @@ pub mod pallet { /// A member has became bonded in a pool. Bonded { member: T::AccountId, pool_id: PoolId, bonded: BalanceOf, joined: bool }, /// A payout has been made to a member. - PaidOut { member: T::AccountId, pool_id: PoolId, payout: BalanceOf }, + PaidOut { member: T::AccountId, pool_id: PoolId, payout: BalanceOf, commission: BalanceOf }, /// A member has unbonded from their pool. /// /// - `balance` is the corresponding balance of the number of points that has been @@ -2651,32 +2651,34 @@ impl Pallet { let (pool_commission, payee) = get_commission(&bonded_pool); pending_rewards = pending_rewards.saturating_sub(pool_commission); + if pool_commission != BalanceOf::::zero() { + if let Some(p) = payee { + + T::Currency::withdraw( + &bonded_pool.reward_account(), + pool_commission, + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + )?; + T::Currency::deposit_creating(&p, pool_commission); + } + } + // Transfer remaining payout to the member. T::Currency::transfer( &bonded_pool.reward_account(), &member_account, pending_rewards, - ExistenceRequirement::KeepAlive, + ExistenceRequirement::AllowDeath, )?; Self::deposit_event(Event::::PaidOut { member: member_account.clone(), pool_id: member.pool_id, payout: pending_rewards, + commission: pool_commission, }); - if pool_commission != BalanceOf::::zero() { - if let Some(p) = payee { - // Transfer pool_commission to the `payee` account. - T::Currency::transfer( - &bonded_pool.reward_account(), - &p, - pool_commission, - ExistenceRequirement::AllowDeath, - )?; - } - } - Ok(pending_rewards) } diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index bd15aed2006ec..4067a4b896e01 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -284,6 +284,7 @@ pub mod v2 { member: who.clone(), pool_id: id, payout: last_claim, + commission: Zero::zero(), }); }); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 17aba6aa40f58..901eb6a9560a3 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -259,7 +259,6 @@ mod bonded_pool { let commission = BondedPool::::get(1).unwrap().commission.unwrap(); assert_eq!(commission.percentage(), Perbill::from_percent(50)); - // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), @@ -1119,7 +1118,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 },] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 },] ); // last recorded reward counter at the time of this member's payout is 1 assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 1)); @@ -1135,7 +1134,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del(40, 1)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 50)); @@ -1148,7 +1147,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del(50, 1)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 100)); @@ -1164,7 +1163,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 1.5)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 105)); @@ -1177,7 +1176,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 1.5)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 125)); @@ -1194,7 +1193,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 2.0)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 175)); @@ -1207,7 +1206,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 180)); @@ -1224,7 +1223,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] ); // We expect a payout of 40 @@ -1243,7 +1242,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2, commission: 0 }] ); assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 222)); @@ -1256,7 +1255,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188, commission: 0 }] ); assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 410)); @@ -1269,7 +1268,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210, commission: 0 }] ); assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 6.2)); assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 620)); @@ -1302,13 +1301,10 @@ mod claim_payout { } #[test] - fn do_reward_payout_with_33_percent_commission() { - let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); - + fn do_reward_payout_with_various_commissions() { ExtBuilder::default().build_and_execute(|| { - let (mut member, mut bonded_pool, mut reward_pool) = + let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - let ed = Balances::minimum_balance(); // Set a commission pool 1 to 50%, with a payee set to `2` assert_ok!(Pools::set_commission( @@ -1318,16 +1314,41 @@ mod claim_payout { Some(2) )); - // fetch bonded pool again with upated commission - bonded_pool = BondedPool::::get(1).unwrap(); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(33), + payee: 2 + }, + ] + ); - // Given the pool has earned some rewards for the first time + // The pool earns 10 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); assert_ok!(Pools::do_reward_payout( &10, &mut member, - &mut bonded_pool, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] + ); + + // The pool earns 50 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), &mut reward_pool )); @@ -1335,10 +1356,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(33), payee: 2 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 }, ] ); }) @@ -1376,7 +1394,7 @@ mod claim_payout { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 5 } + Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 } ] ); assert_eq!(payout, 5); @@ -1394,7 +1412,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); assert_eq!(payout, 10); assert_eq!(reward_pool, rew(0, 0, 15)); @@ -1453,7 +1471,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); assert_eq!(payout, 10); assert_eq!(del_10, del(10, 1)); @@ -1467,7 +1485,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(payout, 40); assert_eq!(del_40, del(40, 1)); @@ -1481,7 +1499,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(payout, 50); assert_eq!(del_50, del(50, 1)); @@ -1498,7 +1516,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 1.5)); @@ -1512,7 +1530,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20 }] + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] ); assert_eq!(payout, 20); assert_eq!(del_40, del_float(40, 1.5)); @@ -1529,7 +1547,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50 }] + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); assert_eq!(payout, 50); assert_eq!(del_50, del_float(50, 2.0)); @@ -1543,7 +1561,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); assert_eq!(payout, 5); assert_eq!(del_10, del_float(10, 2.0)); @@ -1560,7 +1578,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] ); assert_eq!(payout, 40); assert_eq!(del_10, del_float(10, 6.0)); @@ -1623,8 +1641,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, ] ); @@ -1637,8 +1655,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, ] ); }); @@ -1666,8 +1684,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, ] ); @@ -1680,8 +1698,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 4 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 4 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 4, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 4, commission: 0 }, ] ); @@ -1694,8 +1712,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, ] ); }); @@ -1730,9 +1748,9 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 30 + 100 / 2 + 60 / 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 100 / 2 + 60 / 3 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 30 + 100 / 2 + 60 / 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 100 / 2 + 60 / 3, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3, commission: 0 }, ] ); @@ -1746,9 +1764,9 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 }, ] ); }); @@ -1831,9 +1849,9 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 } ] ); @@ -1851,9 +1869,9 @@ mod claim_payout { pool_events_since_last_call(), vec![ Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 40, commission: 0 } ] ); }); @@ -1879,8 +1897,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -1897,8 +1915,8 @@ mod claim_payout { pool_events_since_last_call(), vec![ Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 50 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 50 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 50, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 50, commission: 0 }, ] ); }); @@ -1928,8 +1946,8 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -1943,8 +1961,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); @@ -1958,8 +1976,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); @@ -1968,7 +1986,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 30, pool_id: 1, payout: 10 + 20 + 20 }] + vec![Event::PaidOut { member: 30, pool_id: 1, payout: 10 + 20 + 20, commission: 0 }] ); }); } @@ -1993,7 +2011,7 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 } ] ); @@ -2011,9 +2029,9 @@ mod claim_payout { pool_events_since_last_call(), vec![ // 20 + 40, which means the extra amount they bonded did not impact us. - Event::PaidOut { member: 20, pool_id: 1, payout: 60 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 60, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20 } + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 } ] ); @@ -2027,8 +2045,8 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 15 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 45 } + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 45, commission: 0 } ] ); }); @@ -2095,7 +2113,7 @@ mod claim_payout { Event::Bonded { member: 20, pool_id: 2, bonded: 10, joined: true }, Event::Created { depositor: 30, pool_id: 3 }, Event::Bonded { member: 30, pool_id: 3, bonded: 10, joined: true }, - Event::PaidOut { member: 30, pool_id: 3, payout: 10 } + Event::PaidOut { member: 30, pool_id: 3, payout: 10, commission: 0 } ] ); }) @@ -2242,9 +2260,9 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 15, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false } ] ); @@ -2274,7 +2292,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); } @@ -2291,7 +2309,7 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 20 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: false } ] ); @@ -2388,12 +2406,12 @@ mod claim_payout { Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 20, joined: true }, Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 15 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 15, commission: 0 }, Event::Unbonded { member: 30, pool_id: 1, balance: 10, points: 10, era: 3 }, Event::Unbonded { member: 30, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 7 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 7, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7 } + Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 0 } ] ); }) @@ -2424,8 +2442,8 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 13 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 26 } + Event::PaidOut { member: 10, pool_id: 1, payout: 13, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 26, commission: 0 } ] ); @@ -2510,10 +2528,10 @@ mod claim_payout { bonded: 5000000000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 100000000 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 150000000 }, - Event::PaidOut { member: 21, pool_id: 1, payout: 250000000 }, - Event::PaidOut { member: 22, pool_id: 1, payout: 500000000 } + Event::PaidOut { member: 10, pool_id: 1, payout: 100000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 150000000, commission: 0 }, + Event::PaidOut { member: 21, pool_id: 1, payout: 250000000, commission: 0 }, + Event::PaidOut { member: 22, pool_id: 1, payout: 500000000, commission: 0 } ] ); }) @@ -2865,7 +2883,7 @@ mod unbond { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, - Event::PaidOut { member: 40, pool_id: 1, payout: 40 }, + Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }, Event::Unbonded { member: 40, pool_id: 1, points: 6, balance: 6, era: 3 } ] ); @@ -2908,7 +2926,7 @@ mod unbond { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 550, pool_id: 1, payout: 550 }, + Event::PaidOut { member: 550, pool_id: 1, payout: 550, commission: 0 }, Event::Unbonded { member: 550, pool_id: 1, @@ -2953,7 +2971,7 @@ mod unbond { Event::MemberRemoved { pool_id: 1, member: 40 }, Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, Event::MemberRemoved { pool_id: 1, member: 550 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } ] ); @@ -3491,7 +3509,7 @@ mod unbond { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2, era: 3 } ] ); @@ -3507,7 +3525,7 @@ mod unbond { pool_events_since_last_call(), vec![ // 2/3 of ed, which is 20's share. - Event::PaidOut { member: 20, pool_id: 1, payout: 6 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 6, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 } ] ); @@ -3522,7 +3540,7 @@ mod unbond { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 3 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 } ] ); @@ -4943,7 +4961,7 @@ mod bond_extra { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: claimable_reward }, + Event::PaidOut { member: 10, pool_id: 1, payout: claimable_reward, commission: 0 }, Event::Bonded { member: 10, pool_id: 1, @@ -4998,9 +5016,9 @@ mod bond_extra { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 1 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 0 }, Event::Bonded { member: 10, pool_id: 1, bonded: 1, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 2 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 2, commission: 0 }, Event::Bonded { member: 20, pool_id: 1, bonded: 2, joined: false } ] ); @@ -5248,7 +5266,7 @@ mod reward_counter_precision { assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173, commission: 0 }] ); }) } @@ -5298,7 +5316,7 @@ mod reward_counter_precision { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 15937424600999999996 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 15937424600999999996, commission: 0 }] ); // now let a small member join with 10 DOTs. @@ -5314,7 +5332,7 @@ mod reward_counter_precision { vec![ Event::Bonded { member: 30, pool_id: 1, bonded: 100000000000, joined: true }, // quite small, but working fine. - Event::PaidOut { member: 30, pool_id: 1, payout: 38 } + Event::PaidOut { member: 30, pool_id: 1, payout: 38, commission: 0 } ] ); }) @@ -5394,7 +5412,7 @@ mod reward_counter_precision { bonded: 100000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997 } + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } ] ); @@ -5407,7 +5425,7 @@ mod reward_counter_precision { assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10000000 }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }] ); // earn some more rewards, this time 20 can also claim. @@ -5420,8 +5438,8 @@ mod reward_counter_precision { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10000000 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 1 } + Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 1, commission: 0 } ] ); }); @@ -5470,7 +5488,7 @@ mod reward_counter_precision { bonded: 100000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997 } + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } ] ); From e39769809a8a43ce0a9a332871ff3a77c4750e5b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 28 Oct 2022 14:03:47 +0200 Subject: [PATCH 050/221] fmt --- frame/nomination-pools/src/lib.rs | 12 +++++--- frame/nomination-pools/src/tests.rs | 46 +++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f3433d018c002..19741d6ff67c3 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -274,8 +274,8 @@ use frame_support::{ pallet_prelude::{MaxEncodedLen, *}, storage::bounded_btree_map::BoundedBTreeMap, traits::{ - Currency, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, - ExistenceRequirement, Get, tokens::WithdrawReasons, + tokens::WithdrawReasons, Currency, Defensive, DefensiveOption, DefensiveResult, + DefensiveSaturating, ExistenceRequirement, Get, }, transactional, CloneNoBound, DefaultNoBound, RuntimeDebugNoBound, }; @@ -1505,7 +1505,12 @@ pub mod pallet { /// A member has became bonded in a pool. Bonded { member: T::AccountId, pool_id: PoolId, bonded: BalanceOf, joined: bool }, /// A payout has been made to a member. - PaidOut { member: T::AccountId, pool_id: PoolId, payout: BalanceOf, commission: BalanceOf }, + PaidOut { + member: T::AccountId, + pool_id: PoolId, + payout: BalanceOf, + commission: BalanceOf, + }, /// A member has unbonded from their pool. /// /// - `balance` is the corresponding balance of the number of points that has been @@ -2653,7 +2658,6 @@ impl Pallet { if pool_commission != BalanceOf::::zero() { if let Some(p) = payee { - T::Currency::withdraw( &bonded_pool.reward_account(), pool_commission, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 901eb6a9560a3..93ff5b13b1322 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -1355,9 +1355,7 @@ mod claim_payout { // Then assert_eq!( pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 }, - ] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] ); }) } @@ -1748,8 +1746,18 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 30 + 100 / 2 + 60 / 3, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 100 / 2 + 60 / 3, commission: 0 }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 30 + 100 / 2 + 60 / 3, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 100 / 2 + 60 / 3, + commission: 0 + }, Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3, commission: 0 }, ] ); @@ -1986,7 +1994,12 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 30, pool_id: 1, payout: 10 + 20 + 20, commission: 0 }] + vec![Event::PaidOut { + member: 30, + pool_id: 1, + payout: 10 + 20 + 20, + commission: 0 + }] ); }); } @@ -4961,7 +4974,12 @@ mod bond_extra { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: claimable_reward, commission: 0 }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: claimable_reward, + commission: 0 + }, Event::Bonded { member: 10, pool_id: 1, @@ -5316,7 +5334,12 @@ mod reward_counter_precision { assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 15937424600999999996, commission: 0 }] + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 15937424600999999996, + commission: 0 + }] ); // now let a small member join with 10 DOTs. @@ -5425,7 +5448,12 @@ mod reward_counter_precision { assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }] + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 10000000, + commission: 0 + }] ); // earn some more rewards, this time 20 can also claim. From 4f9eaee519b83f0171ec15b1d317afd1fef6cfe3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 28 Oct 2022 14:17:11 +0200 Subject: [PATCH 051/221] mv comment, rn closure --- frame/nomination-pools/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 19741d6ff67c3..59fd36f3e3969 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2637,10 +2637,7 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // If a non-zero commission has been applied to the pool, deduct the share from - // `pending_rewards` and send that amount to the pool `depositor`. - // Defensive: The commission payee is also checked for existence. - let get_commission = |b: &BondedPool| -> (BalanceOf, Option) { + let get_commission_and_payee = |b: &BondedPool| -> (BalanceOf, Option) { if let Some(c) = &b.commission { let commission_percent = c.percentage(); if commission_percent > Perbill::zero() { @@ -2653,9 +2650,12 @@ impl Pallet { (Zero::zero(), None) }; - let (pool_commission, payee) = get_commission(&bonded_pool); + let (pool_commission, payee) = get_commission_and_payee(&bonded_pool); pending_rewards = pending_rewards.saturating_sub(pool_commission); + // If a non-zero commission has been applied to the pool, deduct the share from + // `reward_pool` and send the amount to the commission `payee`. + // Defensive: The commission payee is also checked for existence. if pool_commission != BalanceOf::::zero() { if let Some(p) = payee { T::Currency::withdraw( From 49619e57330f6c7ae7d0880783980a06b2b66f94 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 29 Oct 2022 11:45:51 +0200 Subject: [PATCH 052/221] percentage() -> as_percent() --- frame/nomination-pools/src/lib.rs | 6 +++--- frame/nomination-pools/src/tests.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 59fd36f3e3969..1cc33ccb6f9c0 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -569,7 +569,7 @@ impl Default for Commission { impl Commission { /// Get the current commission percentage of this pool. /// Returns zero if commission has not yet been configured. - fn percentage(&self) -> Perbill { + fn as_percent(&self) -> Perbill { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } @@ -588,7 +588,7 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(self.percentage()) > t.change_rate.max_increase + (*to).saturating_sub(self.as_percent()) > t.change_rate.max_increase } else { return false } @@ -2639,7 +2639,7 @@ impl Pallet { let get_commission_and_payee = |b: &BondedPool| -> (BalanceOf, Option) { if let Some(c) = &b.commission { - let commission_percent = c.percentage(); + let commission_percent = c.as_percent(); if commission_percent > Perbill::zero() { let payee = b.commission_payee().map(|p| p.clone()).or(None); if payee.is_some() { diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 93ff5b13b1322..7d0b22557694d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -258,7 +258,7 @@ mod bonded_pool { let commission = BondedPool::::get(1).unwrap().commission.unwrap(); - assert_eq!(commission.percentage(), Perbill::from_percent(50)); + assert_eq!(commission.as_percent(), Perbill::from_percent(50)); // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), From dd977b2b03ad26a68c57802fcc55f0f91505c91a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 12:22:41 +0000 Subject: [PATCH 053/221] simplifying expressions --- frame/nomination-pools/src/lib.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 88a380ae87dd5..421062e2ca5f8 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -286,7 +286,7 @@ use sp_runtime::{ AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero, }, - FixedPointNumber, FixedPointOperand, Perbill, + FixedPointNumber, Perbill, }; use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; @@ -1292,7 +1292,7 @@ pub mod pallet { use super::*; use frame_support::traits::StorageVersion; use frame_system::{ensure_signed, pallet_prelude::*}; - use sp_runtime::{traits::CheckedAdd, Perbill}; + use sp_runtime::Perbill; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); @@ -2172,21 +2172,19 @@ pub mod pallet { ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); let final_payee = payee - .map(|p| p) - .or(bonded_pool.commission_payee().map(|p| p.clone())) + .or(bonded_pool.commission_payee().cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; let commission = &bonded_pool.commission; ensure!( - commission.as_ref().map(|c| !c.throttling(&new_commission)).unwrap_or(true), + commission.as_ref().map_or(true, |c| !c.throttling(&new_commission)), Error::::CommissionChangeThrottled ); ensure!( commission .as_ref() - .map(|c| c.max.map(|m| new_commission <= m).unwrap_or(true)) - .unwrap_or(true), + .map_or(true, |c| c.max.map(|m| new_commission <= m).unwrap_or(true)), Error::::CommissionExceedsMaximum ); @@ -2219,11 +2217,16 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - if let Some(c) = &bonded_pool.commission { - if let Some(existing_max) = c.max { - ensure!(existing_max > max_commission, Error::::MaxCommissionRestricted); - } - } + ensure!( + &bonded_pool + .commission + .as_ref() + .map(|c| c.max) + .flatten() + .map_or(true, |m| m > max_commission), + Error::::MaxCommissionRestricted + ); + bonded_pool.set_max_commission(max_commission.clone()); bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); From d395db08782491bffe20190ba970da4c05c35e6a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 12:45:32 +0000 Subject: [PATCH 054/221] Option -> Commission --- frame/nomination-pools/src/lib.rs | 123 +++++++++--------------- frame/nomination-pools/src/migration.rs | 2 +- frame/nomination-pools/src/tests.rs | 52 +++++----- 3 files changed, 72 insertions(+), 105 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 421062e2ca5f8..02fdd3aedc537 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -623,8 +623,8 @@ pub struct CommissionThrottle { #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct BondedPoolInner { - /// The commission rate, if any, of the pool. - pub commission: Option>, + /// The commission rate of the pool. + pub commission: Commission, /// Count of members that belong to the pool. pub member_counter: u32, /// Total points of all the members in the pool who are actively bonded. @@ -667,7 +667,7 @@ impl BondedPool { Self { id, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: Zero::zero(), points: Zero::zero(), roles, @@ -693,10 +693,7 @@ impl BondedPool { /// Gets the current commission payee of this pool as a reference. fn commission_payee(&self) -> Option<&T::AccountId> { - self.commission - .as_ref() - .and_then(|c| c.current.as_ref().map(|(_, p)| p)) - .or(None) + self.commission.current.as_ref().map(|(_, p)| p).or(None) } /// Consume self and put into storage. @@ -787,58 +784,41 @@ impl BondedPool { /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. fn set_commission_current(&mut self, commission: &Perbill, payee: T::AccountId) { - self.commission = self - .commission - .take() - .map_or(Some(Commission::default()), |c| Some(c)) - .map(|c| Commission { - current: if commission > &Perbill::zero() { - Some((*commission, payee)) - } else { - None - }, - throttle: c - .throttle - .map(|t| CommissionThrottle { - previous_set_at: Some(>::block_number()), - ..t - }) - .or(None), - ..c - }); + self.commission = Commission { + current: if commission > &Perbill::zero() { Some((*commission, payee)) } else { None }, + throttle: self + .commission + .throttle + .as_ref() + .map(|t| CommissionThrottle { + previous_set_at: Some(>::block_number()), + ..*t + }) + .or(None), + ..self.commission + }; } /// Set the pool's maximum commission. fn set_max_commission(&mut self, max_commission: Perbill) { - self.commission = self + self.commission.max = Some(max_commission); + self.commission.current = self .commission - .take() - .map_or(Some(Commission::default()), |c| Some(c)) - .map(|c| Commission { - max: Some(max_commission), - // if the pool's current commission is higher than the updated maximum - // commission, decrease it to the new maximum commission. - current: c - .current - .map(|x| if x.0 > max_commission { (max_commission, x.1) } else { x }) - .or(None), - ..c - }); + .current + .as_ref() + .map(|x| if x.0 > max_commission { (max_commission, x.1.clone()) } else { x.clone() }) + .or(None); } /// Set the pool's commission throttle settings. fn set_commission_throttle(&mut self, change_rate: CommissionThrottlePrefs) { - self.commission = - self.commission - .take() - .map_or(Some(Commission::default()), |c| Some(c)) - .map(|c| Commission { - throttle: c.throttle.map_or( - Some(CommissionThrottle { change_rate, previous_set_at: None }), - |t| Some(CommissionThrottle { change_rate, ..t }), - ), - ..c - }); + self.commission.throttle = self + .commission + .throttle + .as_ref() + .map_or(Some(CommissionThrottle { change_rate, previous_set_at: None }), |t| { + Some(CommissionThrottle { change_rate, ..*t }) + }); } fn is_root(&self, who: &T::AccountId) -> bool { @@ -2177,14 +2157,9 @@ pub mod pallet { let commission = &bonded_pool.commission; + ensure!(!commission.throttling(&new_commission), Error::::CommissionChangeThrottled); ensure!( - commission.as_ref().map_or(true, |c| !c.throttling(&new_commission)), - Error::::CommissionChangeThrottled - ); - ensure!( - commission - .as_ref() - .map_or(true, |c| c.max.map(|m| new_commission <= m).unwrap_or(true)), + commission.max.map(|m| new_commission <= m).unwrap_or(true), Error::::CommissionExceedsMaximum ); @@ -2218,12 +2193,7 @@ pub mod pallet { ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); ensure!( - &bonded_pool - .commission - .as_ref() - .map(|c| c.max) - .flatten() - .map_or(true, |m| m > max_commission), + &bonded_pool.commission.max.map_or(true, |m| m > max_commission), Error::::MaxCommissionRestricted ); @@ -2251,15 +2221,14 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - if let Some(c) = &bonded_pool.commission { - if let Some(throttle) = &c.throttle { - ensure!( - !(prefs.max_increase > throttle.change_rate.max_increase || - prefs.min_delay < throttle.change_rate.min_delay), - Error::::CommissionThrottleNotAllowed - ); - } + if let Some(throttle) = &bonded_pool.commission.throttle { + ensure!( + !(prefs.max_increase > throttle.change_rate.max_increase || + prefs.min_delay < throttle.change_rate.min_delay), + Error::::CommissionThrottleNotAllowed + ); } + bonded_pool.set_commission_throttle(prefs); bonded_pool.put(); Ok(()) @@ -2587,13 +2556,11 @@ impl Pallet { reward_pool.register_claimed_reward(pending_rewards); let get_commission_and_payee = |b: &BondedPool| -> (BalanceOf, Option) { - if let Some(c) = &b.commission { - let commission_percent = c.as_percent(); - if commission_percent > Perbill::zero() { - let payee = b.commission_payee().map(|p| p.clone()).or(None); - if payee.is_some() { - return (commission_percent * pending_rewards, payee) - } + let commission_percent = &b.commission.as_percent(); + if commission_percent > &Perbill::zero() { + let payee = b.commission_payee().map(|p| p.clone()).or(None); + if payee.is_some() { + return (*commission_percent * pending_rewards, payee) } } (Zero::zero(), None) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 4067a4b896e01..89eed4cac62d1 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -53,7 +53,7 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: self.member_counter, points: self.points, state: self.state, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index ba0661cdf3ca6..001dc757f72ed 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -55,7 +55,7 @@ fn test_setup_works() { BondedPool:: { id: last_pool, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 10, roles: DEFAULT_ROLES, @@ -99,7 +99,7 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -155,7 +155,7 @@ mod bonded_pool { let mut bonded_pool = BondedPool:: { id: 123123, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -204,7 +204,7 @@ mod bonded_pool { let pool = BondedPool:: { id: 123, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -256,7 +256,7 @@ mod bonded_pool { Some(900) )); - let commission = BondedPool::::get(1).unwrap().commission.unwrap(); + let commission = BondedPool::::get(1).unwrap().commission; assert_eq!(commission.as_percent(), Perbill::from_percent(50)); // Commission change events triggered successfully @@ -321,7 +321,7 @@ mod bonded_pool { )); assert_eq!( BondedPool::::get(1).unwrap().commission, - Some(Commission { + Commission { current: None, max: None, throttle: Some(CommissionThrottle { @@ -331,7 +331,7 @@ mod bonded_pool { }, previous_set_at: None, }) - }) + } ); // We now try to increase commission to 5% (5% increase): this should be throttled. @@ -355,7 +355,7 @@ mod bonded_pool { )); assert_eq!( BondedPool::::get(1).unwrap().commission, - Some(Commission { + Commission { current: Some((Perbill::from_percent(1), 900)), max: None, throttle: Some(CommissionThrottle { @@ -365,7 +365,7 @@ mod bonded_pool { }, previous_set_at: Some(1_u64), }) - }) + } ); // Attempt to increase the commission an additional 1% (now 2%) again immediately. @@ -481,7 +481,7 @@ mod bonded_pool { Perbill::from_percent(90) )); assert_eq!( - BondedPools::::get(1).unwrap().commission.unwrap().max, + BondedPools::::get(1).unwrap().commission.max, Some(Perbill::from_percent(90)) ); @@ -510,11 +510,11 @@ mod bonded_pool { )); assert_eq!( BondedPools::::get(1).unwrap().commission, - Some(Commission { + Commission { current: Some((Perbill::from_percent(50), 900)), max: Some(Perbill::from_percent(50)), throttle: None - }) + } ); // Commission change events triggered successfully @@ -579,7 +579,7 @@ mod bonded_pool { } )); assert_eq!( - BondedPools::::get(1).unwrap().commission.unwrap().throttle, + BondedPools::::get(1).unwrap().commission.throttle, Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { max_increase: Perbill::from_percent(5), @@ -837,7 +837,7 @@ mod join { let bonded = |points, member_counter| BondedPool:: { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter, points, roles: DEFAULT_ROLES, @@ -921,7 +921,7 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -991,7 +991,7 @@ mod join { BondedPool:: { id: 123, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 100, roles: DEFAULT_ROLES, @@ -2844,7 +2844,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 0, roles: DEFAULT_ROLES, @@ -2881,7 +2881,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 3, points: 560, roles: DEFAULT_ROLES, @@ -2922,7 +2922,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 3, points: 10, roles: DEFAULT_ROLES, @@ -2966,7 +2966,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 0, roles: DEFAULT_ROLES, @@ -3095,7 +3095,7 @@ mod unbond { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 3, points: 10, // Only 10 points because 200 + 100 was unbonded roles: DEFAULT_ROLES, @@ -3246,7 +3246,7 @@ mod unbond { BondedPool:: { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 10, roles: DEFAULT_ROLES, @@ -3959,7 +3959,7 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 3, points: 10, roles: DEFAULT_ROLES, @@ -4040,7 +4040,7 @@ mod withdraw_unbonded { BondedPool { id: 1, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 2, points: 10, roles: DEFAULT_ROLES, @@ -4594,7 +4594,7 @@ mod create { BondedPool { id: 2, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), points: StakingMock::minimum_nominator_bond(), member_counter: 1, roles: PoolRoles { @@ -4659,7 +4659,7 @@ mod create { BondedPool:: { id: 2, inner: BondedPoolInner { - commission: None, + commission: Commission::default(), member_counter: 1, points: 10, roles: DEFAULT_ROLES, From 7637e46976c2e3ebd5e2428e1e78d68121b2044d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 12:52:40 +0000 Subject: [PATCH 055/221] add maybe_update_max --- frame/nomination-pools/src/lib.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 02fdd3aedc537..5ede7e046bd04 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -593,6 +593,21 @@ impl Commission { return false } } + + /// Set the pool's maximum commission. + fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { + if let Some(old) = self.max.as_mut() { + if new_max > *old { + return Err("Error".into()) + } + *old = new_max; + // ensure current is also less then the new maximum. + let _ = self.current.as_mut().map(|(x, _)| *x = (*x).min(new_max)); + } else { + self.max = Some(new_max) + }; + Ok(()) + } } /// Pool commission throttle preferences. @@ -799,17 +814,6 @@ impl BondedPool { }; } - /// Set the pool's maximum commission. - fn set_max_commission(&mut self, max_commission: Perbill) { - self.commission.max = Some(max_commission); - self.commission.current = self - .commission - .current - .as_ref() - .map(|x| if x.0 > max_commission { (max_commission, x.1.clone()) } else { x.clone() }) - .or(None); - } - /// Set the pool's commission throttle settings. fn set_commission_throttle(&mut self, change_rate: CommissionThrottlePrefs) { self.commission.throttle = self @@ -2197,7 +2201,7 @@ pub mod pallet { Error::::MaxCommissionRestricted ); - bonded_pool.set_max_commission(max_commission.clone()); + bonded_pool.commission.maybe_update_max(max_commission.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) From 309672928c0cff89320e435b68dd7eeb35bb8e33 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 12:56:05 +0000 Subject: [PATCH 056/221] Err --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 5ede7e046bd04..51ef5dfa39e29 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -598,7 +598,7 @@ impl Commission { fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { - return Err("Error".into()) + return Err(Error::::CommissionChangeThrottled.into()) } *old = new_max; // ensure current is also less then the new maximum. From b4c35f34d6871e7bdacf81fb92af7c514d1e4333 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 12:56:59 +0000 Subject: [PATCH 057/221] tests pass --- frame/nomination-pools/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 51ef5dfa39e29..a41e85a4a0556 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -598,7 +598,7 @@ impl Commission { fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { - return Err(Error::::CommissionChangeThrottled.into()) + return Err(Error::::MaxCommissionRestricted.into()) } *old = new_max; // ensure current is also less then the new maximum. @@ -2196,11 +2196,6 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - ensure!( - &bonded_pool.commission.max.map_or(true, |m| m > max_commission), - Error::::MaxCommissionRestricted - ); - bonded_pool.commission.maybe_update_max(max_commission.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); From 36612b5ad93373d0bfe40155e457ce6838e376a0 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 13:20:06 +0000 Subject: [PATCH 058/221] add update_current --- frame/nomination-pools/src/lib.rs | 55 ++++++++++++++++--------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a41e85a4a0556..88a9a72c8c2f7 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -594,6 +594,26 @@ impl Commission { } } + /// Set the pool's commission. + /// + /// Update commission accordingly based on `commission` and `payee`. + /// + /// If throttle is present, record the current block as the previously updated commission. + /// + /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. + fn update_current(&mut self, commission: &Perbill, payee: T::AccountId) { + self.current = + if commission > &Perbill::zero() { Some((*commission, payee)) } else { None }; + + let _ = self + .throttle + .as_mut() + .map(|t| { + t.register_change(>::block_number()); + }) + .or(None); + } + /// Set the pool's maximum commission. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { @@ -633,6 +653,12 @@ pub struct CommissionThrottle { previous_set_at: Option, } +impl CommissionThrottle { + fn register_change(&mut self, now: T::BlockNumber) { + self.previous_set_at = Some(now); + } +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] @@ -789,31 +815,6 @@ impl BondedPool { .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } - /// Set the pool's commission. - /// - /// Update commission accordingly based on `commission` and `payee`. If Commission does not yet - /// exist, it is bootstrapped with default() and populated with the supplied `commission` and - /// `payee` values. Otherwise, the existing commission is updated. - /// - /// If throttle is present, record the current block as the previously updated commission. - /// - /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. - fn set_commission_current(&mut self, commission: &Perbill, payee: T::AccountId) { - self.commission = Commission { - current: if commission > &Perbill::zero() { Some((*commission, payee)) } else { None }, - throttle: self - .commission - .throttle - .as_ref() - .map(|t| CommissionThrottle { - previous_set_at: Some(>::block_number()), - ..*t - }) - .or(None), - ..self.commission - }; - } - /// Set the pool's commission throttle settings. fn set_commission_throttle(&mut self, change_rate: CommissionThrottlePrefs) { self.commission.throttle = self @@ -2159,7 +2160,7 @@ pub mod pallet { .or(bonded_pool.commission_payee().cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; - let commission = &bonded_pool.commission; + let commission = &mut bonded_pool.commission; ensure!(!commission.throttling(&new_commission), Error::::CommissionChangeThrottled); ensure!( @@ -2167,7 +2168,7 @@ pub mod pallet { Error::::CommissionExceedsMaximum ); - bonded_pool.set_commission_current(&new_commission, final_payee.clone()); + commission.update_current(&new_commission, final_payee.clone()); bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, From e5b86514434e41746f32675299472a5def060deb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 7 Nov 2022 13:21:15 +0000 Subject: [PATCH 059/221] mv payee() --- frame/nomination-pools/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 88a9a72c8c2f7..3e151597eebe7 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -573,6 +573,11 @@ impl Commission { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } + /// Gets the current commission payee of this pool as a reference. + fn payee(&self) -> Option<&T::AccountId> { + self.current.as_ref().map(|(_, p)| p).or(None) + } + /// Returns true if a commission percentage updating to `to` would exhaust the throttle limit. /// A commission update will be throttled (disallowed) if: /// 1. not enough blocks have passed since the previous commission update took place, and @@ -732,11 +737,6 @@ impl BondedPool { Pallet::::create_reward_account(self.id) } - /// Gets the current commission payee of this pool as a reference. - fn commission_payee(&self) -> Option<&T::AccountId> { - self.commission.current.as_ref().map(|(_, p)| p).or(None) - } - /// Consume self and put into storage. fn put(self) { BondedPools::::insert(self.id, BondedPoolInner { ..self.inner }); @@ -2157,7 +2157,7 @@ pub mod pallet { ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); let final_payee = payee - .or(bonded_pool.commission_payee().cloned()) + .or(bonded_pool.commission.payee().cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; let commission = &mut bonded_pool.commission; @@ -2558,7 +2558,7 @@ impl Pallet { let get_commission_and_payee = |b: &BondedPool| -> (BalanceOf, Option) { let commission_percent = &b.commission.as_percent(); if commission_percent > &Perbill::zero() { - let payee = b.commission_payee().map(|p| p.clone()).or(None); + let payee = b.commission.payee().map(|p| p.clone()).or(None); if payee.is_some() { return (*commission_percent * pending_rewards, payee) } From c5fe182639e08ce8ee629176efae51a4300a90bf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 12:06:37 +0000 Subject: [PATCH 060/221] add arbitrary points values --- frame/nomination-pools/src/tests.rs | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 001dc757f72ed..7ad1aec89f9c0 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -1343,6 +1343,21 @@ mod claim_payout { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] ); + // The pool earns 17 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] + ); + // The pool earns 50 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); assert_ok!(Pools::do_reward_payout( @@ -1357,6 +1372,21 @@ mod claim_payout { pool_events_since_last_call(), vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] ); + + // The pool earns 10439 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] + ); }) } From 37485b34d8d1165bcd6b530c17fb675ae76b5387 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 12:09:22 +0000 Subject: [PATCH 061/221] migration: add commission to BondedPoolInner --- frame/nomination-pools/src/migration.rs | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 89eed4cac62d1..08121b58ba95b 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -451,3 +451,81 @@ pub mod v3 { } } } + +pub mod v4 { + use super::*; + + #[derive(Decode)] + pub struct OldBondedPoolInner { + pub points: BalanceOf, + pub state: PoolState, + pub member_counter: u32, + pub roles: PoolRoles, + } + + impl OldBondedPoolInner { + fn migrate_to_v4(self) -> BondedPoolInner { + BondedPoolInner { + commission: Commission::default(), + member_counter: self.member_counter, + points: self.points, + state: self.state, + roles: self.roles, + } + } + } + + /// This migration removes stale bonded-pool metadata, if any. + pub struct MigrateToV4(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV4 { + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + log!( + info, + "Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); + + if current == 4 && onchain == 3 { + let mut translated = 0u64; + BondedPools::::translate::, _>(|_key, old_value| { + translated.saturating_inc(); + Some(old_value.migrate_to_v4()) + }); + + current.put::>(); + log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); + T::DbWeight::get().reads_writes(translated + 1, translated + 1) + } else { + log!(info, "Migration did not executed. This probably should be removed"); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + ensure!( + Pallet::::current_storage_version() > Pallet::::on_chain_storage_version(), + "the on_chain version is equal or more than the current one" + ); + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), &'static str> { + ensure!( + BondedPools::::iter().all(|(_, inner)| inner.contains_key("commission")), + "not all BondedPools have a `commission` field" + ); + ensure!( + BondedPools::::iter().all(|(_, inner)| inner.commission.is_none()), + "a commission value has been incorrectly set" + ); + ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); + Ok(()) + } + } +} From 882f16786c7b67638b56f0606d9771f61fdcfced Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 12:28:51 +0000 Subject: [PATCH 062/221] add commission to BondedPoolInner --- frame/nomination-pools/benchmarking/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9b063539152b7..3bedbe3a2cf83 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -509,15 +509,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), state_toggler: Some(depositor.clone()), }, + state: PoolState::Open, } ); assert_eq!( @@ -548,15 +549,16 @@ frame_benchmarking::benchmarks! { assert_eq!( new_pool, BondedPoolInner { - points: min_create_bond, - state: PoolState::Open, + commission: Commission::default(), member_counter: 1, + points: min_create_bond, roles: PoolRoles { depositor: depositor.clone(), root: Some(depositor.clone()), nominator: Some(depositor.clone()), state_toggler: Some(depositor.clone()), - } + }, + state: PoolState::Open, } ); assert_eq!( @@ -658,3 +660,4 @@ frame_benchmarking::benchmarks! { crate::mock::Runtime ); } + From f813564f269237d0816ce46498e91c77538a1b94 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 12:48:35 +0000 Subject: [PATCH 063/221] add commision benchmarks --- .../nomination-pools/benchmarking/src/lib.rs | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 3bedbe3a2cf83..a76605abf0b8b 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -654,10 +654,62 @@ frame_benchmarking::benchmarks! { assert!(T::Staking::nominations(Pools::::create_bonded_account(1)).is_none()); } + set_commission { + // Create a pool + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + // set a max commission + Pools::::set_max_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1, Perbill::from_percent(50)); + // set a commission throttle + Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1, CommissionThrottlePrefs { + max_increase: Perbill::from_percent(20), + min_delay: 1000_u64, + }); + }:_(RuntimeOrigin::Signed(depositor.clone(), 1, Perbill::from_percent(100), depositor)) + verify { + assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { + current: Some(Perbill::from_percent(100), depositor), + max: None, + throttle: None, + }); + } + + set_max_commission { + // Create a pool + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + // Set a commission that will update when max commission is set. + Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), Perbill::from_percent(100), depositor); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1, Perbill::from_percent(50)) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: Some(Perbill::from_percent(50), depositor), + max: Some(Perbill::from_percent(50)), + throttle: None, + }); + } + + set_commission_throttle { + // Create a pool + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1, CommissionThrottlePrefs { + max_increase: Perbill::from_percent(50), + min_delay: 1000_u64, + }) + verify { + assert_eq!( + BondedPools::::get(1).unwrap().commission, Commission { + current: None, + max: None, + throttle: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(50), + min_delay: 1000_u64, + }, + }); + } + impl_benchmark_test_suite!( Pallet, crate::mock::new_test_ext(), crate::mock::Runtime ); } - From f6463317f7cb50bfc4b8a698d6735c9a77bebc29 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 12:50:24 +0000 Subject: [PATCH 064/221] set_max_commission -> set_commission_max --- frame/nomination-pools/benchmarking/src/lib.rs | 4 ++-- frame/nomination-pools/src/lib.rs | 2 +- frame/nomination-pools/src/tests.rs | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index a76605abf0b8b..87b706104ef20 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -658,7 +658,7 @@ frame_benchmarking::benchmarks! { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); // set a max commission - Pools::::set_max_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1, Perbill::from_percent(50)); + Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1, Perbill::from_percent(50)); // set a commission throttle Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1, CommissionThrottlePrefs { max_increase: Perbill::from_percent(20), @@ -673,7 +673,7 @@ frame_benchmarking::benchmarks! { }); } - set_max_commission { + set_commission_max { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); // Set a commission that will update when max commission is set. diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3e151597eebe7..bcbb859872b6d 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2188,7 +2188,7 @@ pub mod pallet { /// current commission is higher than the supplied maximum. #[pallet::weight(0)] #[transactional] - pub fn set_max_commission( + pub fn set_commission_max( origin: OriginFor, pool_id: PoolId, max_commission: Perbill, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 7ad1aec89f9c0..6cf142d05b7a8 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -409,7 +409,7 @@ mod bonded_pool { ); // We will now set a max commission to the current 2% - assert_ok!(Pools::set_max_commission( + assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, Perbill::from_percent(2) @@ -457,11 +457,11 @@ mod bonded_pool { } #[test] - fn set_max_commission_works_with_error_tests() { + fn set_commission_max_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { // Provided pool does not exist assert_noop!( - Pools::set_max_commission( + Pools::set_commission_max( RuntimeOrigin::signed(900), 9999, Perbill::from_percent(1) @@ -470,12 +470,12 @@ mod bonded_pool { ); // Sender does not have permission to set commission assert_noop!( - Pools::set_max_commission(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), Error::::DoesNotHavePermission ); // Set a max commission commission pool 1 to 90% - assert_ok!(Pools::set_max_commission( + assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, Perbill::from_percent(90) @@ -487,7 +487,7 @@ mod bonded_pool { // We attempt to increase the max commission to 100%, but increasing is disallowed. assert_noop!( - Pools::set_max_commission( + Pools::set_commission_max( RuntimeOrigin::signed(900), 1, Perbill::from_percent(100) @@ -503,7 +503,7 @@ mod bonded_pool { Perbill::from_percent(75), Some(900), )); - assert_ok!(Pools::set_max_commission( + assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, Perbill::from_percent(50) From 2a55aba02fa00d52f650fdc50f75de88829fa0a4 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 15:47:54 +0000 Subject: [PATCH 065/221] benchmarks build --- .../nomination-pools/benchmarking/src/lib.rs | 73 +++++++++++++------ frame/nomination-pools/src/lib.rs | 33 +++++---- 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 87b706104ef20..1d173b539ccc3 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -28,11 +28,15 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ - BalanceOf, BondExtra, BondedPoolInner, BondedPools, ConfigOp, MaxPoolMembers, - MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, - PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, + BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionThrottle, + CommissionThrottlePrefs, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, + MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, + SubPoolsStorage, +}; +use sp_runtime::{ + traits::{Bounded, StaticLookup, Zero}, + Perbill, }; -use sp_runtime::traits::{Bounded, StaticLookup, Zero}; use sp_staking::{EraIndex, StakingInterface}; // `frame_benchmarking::benchmarks!` macro needs this use pallet_nomination_pools::Call; @@ -133,13 +137,28 @@ impl ListScenario { // Create accounts with the origin weight let (pool_creator1, pool_origin1) = create_pool_account::(USER_SEED + 1, origin_weight); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator1.clone()).into(), + 1, + Perbill::from_percent(50), + Some(pool_creator1.clone()), + ) + .unwrap(); + T::Staking::nominate( &pool_origin1, // NOTE: these don't really need to be validators. vec![account("random_validator", 0, USER_SEED)], )?; - let (_, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); + let (pool_creator2, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator2.clone()).into(), + 1, + Perbill::from_percent(50), + Some(pool_creator2), + ) + .unwrap(); T::Staking::nominate( &pool_origin2, vec![account("random_validator", 0, USER_SEED)].clone(), @@ -155,7 +174,15 @@ impl ListScenario { dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?; // Create an account with the worst case destination weight - let (_, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); + let (pool_creator3, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator3.clone()).into(), + 1, + Perbill::from_percent(50), + Some(pool_creator3), + ) + .unwrap(); + T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?; let weight_of = pallet_staking::Pallet::::weight_of_fn(); @@ -658,16 +685,17 @@ frame_benchmarking::benchmarks! { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); // set a max commission - Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1, Perbill::from_percent(50)); + Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); // set a commission throttle - Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1, CommissionThrottlePrefs { + Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionThrottlePrefs { max_increase: Perbill::from_percent(20), - min_delay: 1000_u64, - }); - }:_(RuntimeOrigin::Signed(depositor.clone(), 1, Perbill::from_percent(100), depositor)) + min_delay: 1000u32.into(), + }).unwrap(); + + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(100), Some(depositor.clone())) verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { - current: Some(Perbill::from_percent(100), depositor), + current: Some((Perbill::from_percent(100), depositor)), max: None, throttle: None, }); @@ -677,12 +705,12 @@ frame_benchmarking::benchmarks! { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); // Set a commission that will update when max commission is set. - Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), Perbill::from_percent(100), depositor); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1, Perbill::from_percent(50)) + Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(100), Some(depositor.clone())).unwrap(); + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50)) verify { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { - current: Some(Perbill::from_percent(50), depositor), + current: Some((Perbill::from_percent(50), depositor)), max: Some(Perbill::from_percent(50)), throttle: None, }); @@ -691,19 +719,22 @@ frame_benchmarking::benchmarks! { set_commission_throttle { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1, CommissionThrottlePrefs { + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionThrottlePrefs { max_increase: Perbill::from_percent(50), - min_delay: 1000_u64, + min_delay: 1000u32.into(), }) verify { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { current: None, max: None, - throttle: CommissionThrottlePrefs { - max_increase: Perbill::from_percent(50), - min_delay: 1000_u64, - }, + throttle: Some(CommissionThrottle { + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), + }, + previous_set_at: Some(1u32.into()), + }), }); } diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index bcbb859872b6d..c036d5b1fe623 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -551,13 +551,13 @@ pub struct PoolRoles { #[scale_info(skip_type_params(T))] pub struct Commission { /// The active commission rate of the pool along with the account commission is paid to. - current: Option<(Perbill, T::AccountId)>, + pub current: Option<(Perbill, T::AccountId)>, /// An optional maximum commission that can be set by the pool root. Once set, this value /// cannot be updated. - max: Option, + pub max: Option, /// Configiration around how often the commission can be updated, and metadata around the /// previous round of updates. - throttle: Option>, + pub throttle: Option>, } impl Default for Commission { @@ -635,18 +635,6 @@ impl Commission { } } -/// Pool commission throttle preferences. -/// -/// Throttle prefs need to be passed and configured together. This struct is used in -/// the `set_commission_throttle` call as well as in CommissionThrottle. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] -pub struct CommissionThrottlePrefs { - /// The maximum amount the commission can be updated by. - pub max_increase: Perbill, - /// How often an update can take place. - pub min_delay: BlockNumber, -} - /// The pool root is able to set a commission throttle for their pool. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] @@ -655,7 +643,7 @@ pub struct CommissionThrottle { /// The change rate dictates how often and by how much commission can be updated. pub change_rate: CommissionThrottlePrefs, /// The block the previous commission update took place. - previous_set_at: Option, + pub previous_set_at: Option, } impl CommissionThrottle { @@ -664,6 +652,19 @@ impl CommissionThrottle { } } + +/// Pool commission throttle preferences. +/// +/// Throttle prefs need to be passed and configured together. This struct is used in +/// the `set_commission_throttle` call as well as in CommissionThrottle. +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] +pub struct CommissionThrottlePrefs { + /// The maximum amount the commission can be updated by. + pub max_increase: Perbill, + /// How often an update can take place. + pub min_delay: BlockNumber, +} + /// Pool permissions and state #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Clone)] #[codec(mel_bound(T: Config))] From 1ea87c697e51d7afd0f3220ff93a4e2862b05ca7 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:01:35 +0000 Subject: [PATCH 066/221] maybe_update_current --- frame/nomination-pools/src/lib.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c036d5b1fe623..f5b4fcc4ef33c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -606,7 +606,17 @@ impl Commission { /// If throttle is present, record the current block as the previously updated commission. /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. - fn update_current(&mut self, commission: &Perbill, payee: T::AccountId) { + fn maybe_update_current( + &mut self, + commission: &Perbill, + payee: T::AccountId, + ) -> DispatchResult { + ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!( + self.max.map(|m| commission <= &m).unwrap_or(true), + Error::::CommissionExceedsMaximum + ); + self.current = if commission > &Perbill::zero() { Some((*commission, payee)) } else { None }; @@ -617,6 +627,7 @@ impl Commission { t.register_change(>::block_number()); }) .or(None); + Ok(()) } /// Set the pool's maximum commission. @@ -652,7 +663,6 @@ impl CommissionThrottle { } } - /// Pool commission throttle preferences. /// /// Throttle prefs need to be passed and configured together. This struct is used in @@ -2163,13 +2173,7 @@ pub mod pallet { let commission = &mut bonded_pool.commission; - ensure!(!commission.throttling(&new_commission), Error::::CommissionChangeThrottled); - ensure!( - commission.max.map(|m| new_commission <= m).unwrap_or(true), - Error::::CommissionExceedsMaximum - ); - - commission.update_current(&new_commission, final_payee.clone()); + commission.maybe_update_current(&new_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, From 6bca9cc22aee21ff92c0228853a4df51e6185290 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:05:37 +0000 Subject: [PATCH 067/221] maybe_update_throttle --- frame/nomination-pools/src/lib.rs | 43 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f5b4fcc4ef33c..54f0403e8d80e 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -644,6 +644,24 @@ impl Commission { }; Ok(()) } + + /// Set the pool's commission throttle settings. + fn maybe_update_throttle(&mut self, change_rate: CommissionThrottlePrefs) -> DispatchResult { + if let Some(throttle) = &self.throttle { + ensure!( + !(change_rate.max_increase > throttle.change_rate.max_increase || + change_rate.min_delay < throttle.change_rate.min_delay), + Error::::CommissionThrottleNotAllowed + ); + } + self.throttle = self + .throttle + .as_ref() + .map_or(Some(CommissionThrottle { change_rate, previous_set_at: None }), |t| { + Some(CommissionThrottle { change_rate, ..*t }) + }); + Ok(()) + } } /// The pool root is able to set a commission throttle for their pool. @@ -826,17 +844,6 @@ impl BondedPool { .saturating_sub(T::Staking::active_stake(&account).unwrap_or_default()) } - /// Set the pool's commission throttle settings. - fn set_commission_throttle(&mut self, change_rate: CommissionThrottlePrefs) { - self.commission.throttle = self - .commission - .throttle - .as_ref() - .map_or(Some(CommissionThrottle { change_rate, previous_set_at: None }), |t| { - Some(CommissionThrottle { change_rate, ..*t }) - }); - } - fn is_root(&self, who: &T::AccountId) -> bool { self.roles.root.as_ref().map_or(false, |root| root == who) } @@ -2171,9 +2178,7 @@ pub mod pallet { .or(bonded_pool.commission.payee().cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; - let commission = &mut bonded_pool.commission; - - commission.maybe_update_current(&new_commission, final_payee.clone())?; + bonded_pool.commission.maybe_update_current(&new_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, @@ -2226,15 +2231,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - if let Some(throttle) = &bonded_pool.commission.throttle { - ensure!( - !(prefs.max_increase > throttle.change_rate.max_increase || - prefs.min_delay < throttle.change_rate.min_delay), - Error::::CommissionThrottleNotAllowed - ); - } - - bonded_pool.set_commission_throttle(prefs); + bonded_pool.commission.maybe_update_throttle(prefs)?; bonded_pool.put(); Ok(()) } From 1e65a80fa149edb429c08ddd174f3e7df8d405d8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:10:55 +0000 Subject: [PATCH 068/221] fmt --- frame/nomination-pools/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 54f0403e8d80e..a2049963e1687 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -646,11 +646,14 @@ impl Commission { } /// Set the pool's commission throttle settings. - fn maybe_update_throttle(&mut self, change_rate: CommissionThrottlePrefs) -> DispatchResult { - if let Some(throttle) = &self.throttle { + fn maybe_update_throttle( + &mut self, + change_rate: CommissionThrottlePrefs, + ) -> DispatchResult { + if let Some(t) = &self.throttle { ensure!( - !(change_rate.max_increase > throttle.change_rate.max_increase || - change_rate.min_delay < throttle.change_rate.min_delay), + !(change_rate.max_increase > t.change_rate.max_increase || + change_rate.min_delay < t.change_rate.min_delay), Error::::CommissionThrottleNotAllowed ); } @@ -2178,7 +2181,9 @@ pub mod pallet { .or(bonded_pool.commission.payee().cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; - bonded_pool.commission.maybe_update_current(&new_commission, final_payee.clone())?; + bonded_pool + .commission + .maybe_update_current(&new_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, From 2c2112699b0f5599239d767259ea79ef7c190241 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:18:02 +0000 Subject: [PATCH 069/221] add throttle.restricted() --- frame/nomination-pools/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a2049963e1687..ca394d993560d 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -651,11 +651,7 @@ impl Commission { change_rate: CommissionThrottlePrefs, ) -> DispatchResult { if let Some(t) = &self.throttle { - ensure!( - !(change_rate.max_increase > t.change_rate.max_increase || - change_rate.min_delay < t.change_rate.min_delay), - Error::::CommissionThrottleNotAllowed - ); + ensure!(!t.restricted(&change_rate), Error::::CommissionThrottleNotAllowed); } self.throttle = self .throttle @@ -679,6 +675,11 @@ pub struct CommissionThrottle { } impl CommissionThrottle { + fn restricted(&self, change_rate: &CommissionThrottlePrefs) -> bool { + change_rate.max_increase > self.change_rate.max_increase || + change_rate.min_delay < self.change_rate.min_delay + } + fn register_change(&mut self, now: T::BlockNumber) { self.previous_set_at = Some(now); } From d821d6d42aa3e5a610df3eb1cdecd8ea04614b75 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:34:51 +0000 Subject: [PATCH 070/221] mv get_commission_and_payee to Commission --- frame/nomination-pools/src/lib.rs | 43 ++++++++++++++++------------- frame/nomination-pools/src/tests.rs | 2 +- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index ca394d993560d..664306b35aebb 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -569,7 +569,7 @@ impl Default for Commission { impl Commission { /// Get the current commission percentage of this pool. /// Returns zero if commission has not yet been configured. - fn as_percent(&self) -> Perbill { + fn percent(&self) -> Perbill { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } @@ -593,7 +593,7 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(self.as_percent()) > t.change_rate.max_increase + (*to).saturating_sub(self.percent()) > t.change_rate.max_increase } else { return false } @@ -661,6 +661,21 @@ impl Commission { }); Ok(()) } + + /// Gets the current commission (if any) and payee to be paid. + fn get_commission_and_payee( + &self, + pending_rewards: &BalanceOf, + ) -> (BalanceOf, Option) { + let commission_percent = &self.percent(); + if commission_percent > &Perbill::zero() { + let payee = self.payee().map(|p| p.clone()).or(None); + if payee.is_some() { + return (*commission_percent * *pending_rewards, payee) + } + } + (Zero::zero(), None) + } } /// The pool root is able to set a commission throttle for their pool. @@ -2563,32 +2578,22 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - let get_commission_and_payee = |b: &BondedPool| -> (BalanceOf, Option) { - let commission_percent = &b.commission.as_percent(); - if commission_percent > &Perbill::zero() { - let payee = b.commission.payee().map(|p| p.clone()).or(None); - if payee.is_some() { - return (*commission_percent * pending_rewards, payee) - } - } - (Zero::zero(), None) - }; - - let (pool_commission, payee) = get_commission_and_payee(&bonded_pool); - pending_rewards = pending_rewards.saturating_sub(pool_commission); + let (pool_commission, payee) = + &bonded_pool.commission.get_commission_and_payee(&pending_rewards); + pending_rewards = pending_rewards.saturating_sub(*pool_commission); // If a non-zero commission has been applied to the pool, deduct the share from // `reward_pool` and send the amount to the commission `payee`. // Defensive: The commission payee is also checked for existence. - if pool_commission != BalanceOf::::zero() { + if pool_commission != &BalanceOf::::zero() { if let Some(p) = payee { T::Currency::withdraw( &bonded_pool.reward_account(), - pool_commission, + *pool_commission, WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; - T::Currency::deposit_creating(&p, pool_commission); + T::Currency::deposit_creating(&p, *pool_commission); } } @@ -2604,7 +2609,7 @@ impl Pallet { member: member_account.clone(), pool_id: member.pool_id, payout: pending_rewards, - commission: pool_commission, + commission: *pool_commission, }); Ok(pending_rewards) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 6cf142d05b7a8..43367caec0f66 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -258,7 +258,7 @@ mod bonded_pool { let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.as_percent(), Perbill::from_percent(50)); + assert_eq!(commission.percent(), Perbill::from_percent(50)); // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), From 757e11262c9bef68a18ef4df9f43aaed4bb3592a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:36:50 +0000 Subject: [PATCH 071/221] rm dereference --- frame/nomination-pools/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 664306b35aebb..7544db0dbdbb5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -667,11 +667,11 @@ impl Commission { &self, pending_rewards: &BalanceOf, ) -> (BalanceOf, Option) { - let commission_percent = &self.percent(); - if commission_percent > &Perbill::zero() { + let commission_percent = self.percent(); + if commission_percent > Perbill::zero() { let payee = self.payee().map(|p| p.clone()).or(None); if payee.is_some() { - return (*commission_percent * *pending_rewards, payee) + return (commission_percent * *pending_rewards, payee) } } (Zero::zero(), None) From 32cc16fd99e5500d9426705da08936f74b72be7a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 16:51:38 +0000 Subject: [PATCH 072/221] commission_set() + comments --- frame/nomination-pools/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 7544db0dbdbb5..d77145b67c22b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -573,6 +573,11 @@ impl Commission { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } + /// check if the current commission is a non-zero amount + fn commission_set(&self) -> bool { + self.percent() > Perbill::zero() + } + /// Gets the current commission payee of this pool as a reference. fn payee(&self) -> Option<&T::AccountId> { self.current.as_ref().map(|(_, p)| p).or(None) @@ -667,14 +672,13 @@ impl Commission { &self, pending_rewards: &BalanceOf, ) -> (BalanceOf, Option) { - let commission_percent = self.percent(); - if commission_percent > Perbill::zero() { - let payee = self.payee().map(|p| p.clone()).or(None); - if payee.is_some() { - return (commission_percent * *pending_rewards, payee) - } + if self.commission_set() { + self.payee().map_or((Zero::zero(), None), |p| { + (self.percent() * *pending_rewards, Some(p.clone())) + }) + } else { + (Zero::zero(), None) } - (Zero::zero(), None) } } @@ -2578,8 +2582,12 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); + // Gets the commission percentage and payee to be paid if commission has + // been set. Otherwise, a zero commission and payee of `None` is returned. let (pool_commission, payee) = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); + + // Deduct any outstanding commission from the reward being claimed. pending_rewards = pending_rewards.saturating_sub(*pool_commission); // If a non-zero commission has been applied to the pool, deduct the share from From 0d0c7812da65856455f9b5b3a5aede16e90a446d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 17:08:02 +0000 Subject: [PATCH 073/221] fix pool id --- frame/nomination-pools/benchmarking/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 1d173b539ccc3..692e134996600 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -154,7 +154,7 @@ impl ListScenario { let (pool_creator2, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); Pools::::set_commission( RuntimeOrigin::Signed(pool_creator2.clone()).into(), - 1, + 2, Perbill::from_percent(50), Some(pool_creator2), ) @@ -177,7 +177,7 @@ impl ListScenario { let (pool_creator3, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); Pools::::set_commission( RuntimeOrigin::Signed(pool_creator3.clone()).into(), - 1, + 3, Perbill::from_percent(50), Some(pool_creator3), ) From a40f5554bc31f85ee3db292d2509ced487adbff3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 17:42:52 +0000 Subject: [PATCH 074/221] throttle after commission set --- frame/nomination-pools/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d77145b67c22b..0de3b10166305 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -598,7 +598,8 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(self.percent()) > t.change_rate.max_increase + (*to).saturating_sub(self.percent()) > t.change_rate.max_increase && + self.current.is_some() } else { return false } @@ -622,8 +623,7 @@ impl Commission { Error::::CommissionExceedsMaximum ); - self.current = - if commission > &Perbill::zero() { Some((*commission, payee)) } else { None }; + self.current = Some((*commission, payee)); let _ = self .throttle From 47169658a30e047feb54cbdeb8d4fa863999f25c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 17:43:07 +0000 Subject: [PATCH 075/221] amend set_commission_handles_errors --- frame/nomination-pools/src/tests.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 43367caec0f66..8e6712bf44aa9 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -334,36 +334,47 @@ mod bonded_pool { } ); - // We now try to increase commission to 5% (5% increase): this should be throttled. + // Set the initial commission to 5%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(5), + Some(900) + )); + + // We now try to increase commission to 10% (5% increase): this should be throttled. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), + Perbill::from_percent(10), Some(900) ), Error::::CommissionChangeThrottled ); + // Run 2 blocks into the future. + run_blocks(2); + // We now try to increase commission by 1%, and provide an initial payee. // This should work, and set the `previous_set_at` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(1), + Perbill::from_percent(6), Some(900) )); assert_eq!( BondedPool::::get(1).unwrap().commission, Commission { - current: Some((Perbill::from_percent(1), 900)), + current: Some((Perbill::from_percent(6), 900)), max: None, throttle: Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }, - previous_set_at: Some(1_u64), + previous_set_at: Some(3_u64), }) } ); @@ -439,7 +450,12 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(1), + commission: Perbill::from_percent(5), + payee: 900 + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(6), payee: 900 }, Event::PoolCommissionUpdated { From 5060cb6ff83c896a0600e5c193e3223e2c3a50db Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 17:56:02 +0000 Subject: [PATCH 076/221] benchmarks work --- .../nomination-pools/benchmarking/src/lib.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 692e134996600..9f1e6c59a2472 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -289,9 +289,10 @@ frame_benchmarking::benchmarks! { }: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::Rewards) verify { + // commission of 50% deducted here. assert!( T::Staking::active_stake(&scenario.origin1).unwrap() >= - scenario.dest_weight + scenario.dest_weight / 2u32.into() ); } @@ -689,15 +690,21 @@ frame_benchmarking::benchmarks! { // set a commission throttle Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionThrottlePrefs { max_increase: Perbill::from_percent(20), - min_delay: 1000u32.into(), + min_delay: 0u32.into(), }).unwrap(); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(100), Some(depositor.clone())) + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(40), Some(depositor.clone())) verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { - current: Some((Perbill::from_percent(100), depositor)), - max: None, - throttle: None, + current: Some((Perbill::from_percent(40), depositor)), + max: Some(Perbill::from_percent(50)), + throttle: Some(CommissionThrottle { + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(20), + min_delay: 0u32.into() + }, + previous_set_at: Some(1u32.into()) + }), }); } @@ -705,7 +712,7 @@ frame_benchmarking::benchmarks! { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); // Set a commission that will update when max commission is set. - Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(100), Some(depositor.clone())).unwrap(); + Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50), Some(depositor.clone())).unwrap(); }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50)) verify { assert_eq!( @@ -733,7 +740,7 @@ frame_benchmarking::benchmarks! { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), }, - previous_set_at: Some(1u32.into()), + previous_set_at: None }), }); } From da6ab34a0046cc836b9818938abfb1a3fb835ed7 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 8 Nov 2022 17:56:30 +0000 Subject: [PATCH 077/221] run benchmarks + add weights to commission calls --- frame/nomination-pools/src/lib.rs | 6 +- frame/nomination-pools/src/weights.rs | 358 ++++++++++++++++---------- 2 files changed, 221 insertions(+), 143 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 0de3b10166305..9736150929dff 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2185,7 +2185,7 @@ pub mod pallet { /// If the max commission has _not yet_ been set, then the commission is not /// restricted. /// A `payee` must already be present before commission can be set. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::set_commission())] #[transactional] pub fn set_commission( origin: OriginFor, @@ -2221,7 +2221,7 @@ pub mod pallet { /// /// This call also updates the pool's current commission to the new maximum if the /// current commission is higher than the supplied maximum. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::set_commission_max())] #[transactional] pub fn set_commission_max( origin: OriginFor, @@ -2245,7 +2245,7 @@ pub mod pallet { /// throttle configuration is given. /// /// If a throttle configuration does not yet exist, the values are set. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::set_commission_throttle())] #[transactional] pub fn set_commission_throttle( origin: OriginFor, diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index e20394ca668b9..660c7fc9d2316 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,41 +1,32 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-06-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2022-11-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Rosss-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: -// target/production/substrate +// target/release/substrate // benchmark // pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_nomination_pools -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --heap-pages=4096 -// --output=./frame/nomination-pools/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --execution +// wasm +// --wasm-execution +// compiled +// --dev +// --pallet +// pallet-nomination-pools +// --extrinsic +// * +// --steps +// 50 +// --repeat +// 20 +// --output +// frame/nomination-pools/src/weights.rs +// --template +// .maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -61,6 +52,9 @@ pub trait WeightInfo { fn set_configs() -> Weight; fn update_roles() -> Weight; fn chill() -> Weight; + fn set_commission() -> Weight; + fn set_commission_max() -> Weight; + fn set_commission_throttle() -> Weight; } /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. @@ -69,18 +63,19 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MinJoinBond (r:1 w:0) // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:2 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) // Storage: NominationPools MaxPoolMembers (r:1 w:0) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - Weight::from_ref_time(123_947_000 as u64) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(151_000_000 as u64) .saturating_add(T::DbWeight::get().reads(17 as u64)) .saturating_add(T::DbWeight::get().writes(12 as u64)) } @@ -88,13 +83,14 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:2) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - Weight::from_ref_time(118_236_000 as u64) + // Minimum execution time: 140_000 nanoseconds. + Weight::from_ref_time(142_000_000 as u64) .saturating_add(T::DbWeight::get().reads(14 as u64)) .saturating_add(T::DbWeight::get().writes(12 as u64)) } @@ -102,13 +98,14 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - Weight::from_ref_time(132_475_000 as u64) + // Minimum execution time: 170_000 nanoseconds. + Weight::from_ref_time(175_000_000 as u64) .saturating_add(T::DbWeight::get().reads(14 as u64)) .saturating_add(T::DbWeight::get().writes(13 as u64)) } @@ -117,63 +114,69 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - Weight::from_ref_time(50_299_000 as u64) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(55_000_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: System Account (r:2 w:1) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Nominators (r:1 w:0) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - Weight::from_ref_time(121_254_000 as u64) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(147_000_000 as u64) .saturating_add(T::DbWeight::get().reads(18 as u64)) .saturating_add(T::DbWeight::get().writes(13 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - Weight::from_ref_time(41_928_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(52_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(4 as u64)) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(54_406_927 as u64) + // Standard Error: 1_104 + .saturating_add(Weight::from_ref_time(14_722 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools SubPoolsStorage (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - Weight::from_ref_time(81_611_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(56_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(8 as u64)) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(99_241_507 as u64) + // Standard Error: 2_139 + .saturating_add(Weight::from_ref_time(1_948 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(9 as u64)) .saturating_add(T::DbWeight::get().writes(7 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:1) + // Storage: Staking Ledger (r:1 w:1) // Storage: Staking SlashingSpans (r:1 w:0) // Storage: Staking Validators (r:1 w:0) // Storage: Staking Nominators (r:1 w:0) @@ -185,29 +188,32 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: NominationPools CounterForRewardPools (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - Weight::from_ref_time(139_849_000 as u64) - .saturating_add(T::DbWeight::get().reads(19 as u64)) - .saturating_add(T::DbWeight::get().writes(16 as u64)) + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(152_086_647 as u64) + // Standard Error: 2_975 + .saturating_add(Weight::from_ref_time(633 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(20 as u64)) + .saturating_add(T::DbWeight::get().writes(17 as u64)) } + // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: NominationPools MinCreateBond (r:1 w:0) // Storage: NominationPools MinJoinBond (r:1 w:0) // Storage: NominationPools MaxPools (r:1 w:0) // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) // Storage: NominationPools MaxPoolMembers (r:1 w:0) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) // Storage: System Account (r:2 w:2) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:1) + // Storage: Staking Ledger (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking HistoryDepth (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: NominationPools CounterForRewardPools (r:1 w:1) @@ -216,36 +222,40 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - Weight::from_ref_time(126_246_000 as u64) - .saturating_add(T::DbWeight::get().reads(22 as u64)) + // Minimum execution time: 130_000 nanoseconds. + Weight::from_ref_time(132_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(21 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: Staking Nominators (r:1 w:1) // Storage: Staking MaxNominatorsCount (r:1 w:0) // Storage: Staking Validators (r:2 w:0) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: BagsList ListNodes (r:1 w:1) - // Storage: BagsList ListBags (r:1 w:1) - // Storage: BagsList CounterForListNodes (r:1 w:1) + // Storage: VoterList ListNodes (r:1 w:1) + // Storage: VoterList ListBags (r:1 w:1) + // Storage: VoterList CounterForListNodes (r:1 w:1) // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - Weight::from_ref_time(48_829_000 as u64) - // Standard Error: 10_000 - .saturating_add(Weight::from_ref_time(2_204_000 as u64).saturating_mul(n as u64)) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(58_146_514 as u64) + // Standard Error: 5_979 + .saturating_add(Weight::from_ref_time(1_361_282 as u64).saturating_mul(n as u64)) .saturating_add(T::DbWeight::get().reads(12 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) .saturating_add(T::DbWeight::get().writes(5 as u64)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - Weight::from_ref_time(26_761_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(37_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) @@ -253,9 +263,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - Weight::from_ref_time(14_519_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(n as u64)) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_098_252 as u64) + // Standard Error: 231 + .saturating_add(Weight::from_ref_time(654 as u64).saturating_mul(n as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -265,28 +276,53 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - Weight::from_ref_time(6_173_000 as u64) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000 as u64) .saturating_add(T::DbWeight::get().writes(5 as u64)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - Weight::from_ref_time(22_261_000 as u64) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) // Storage: Staking Validators (r:1 w:0) // Storage: Staking Nominators (r:1 w:1) // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: BagsList ListNodes (r:1 w:1) - // Storage: BagsList ListBags (r:1 w:1) - // Storage: BagsList CounterForListNodes (r:1 w:1) + // Storage: VoterList ListNodes (r:1 w:1) + // Storage: VoterList ListBags (r:1 w:1) + // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - Weight::from_ref_time(47_959_000 as u64) - .saturating_add(T::DbWeight::get().reads(8 as u64)) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(62_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(9 as u64)) .saturating_add(T::DbWeight::get().writes(5 as u64)) } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission() -> Weight { + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(24_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_max() -> Weight { + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_throttle() -> Weight { + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_000_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } } // For backwards compatibility and tests @@ -294,18 +330,19 @@ impl WeightInfo for () { // Storage: NominationPools MinJoinBond (r:1 w:0) // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:2 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) // Storage: NominationPools MaxPoolMembers (r:1 w:0) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) - // Storage: Staking Bonded (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - Weight::from_ref_time(123_947_000 as u64) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(151_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(17 as u64)) .saturating_add(RocksDbWeight::get().writes(12 as u64)) } @@ -313,13 +350,14 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:2) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - Weight::from_ref_time(118_236_000 as u64) + // Minimum execution time: 140_000 nanoseconds. + Weight::from_ref_time(142_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(14 as u64)) .saturating_add(RocksDbWeight::get().writes(12 as u64)) } @@ -327,13 +365,14 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - Weight::from_ref_time(132_475_000 as u64) + // Minimum execution time: 170_000 nanoseconds. + Weight::from_ref_time(175_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(14 as u64)) .saturating_add(RocksDbWeight::get().writes(13 as u64)) } @@ -342,63 +381,69 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - Weight::from_ref_time(50_299_000 as u64) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(55_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) + // Storage: Staking Ledger (r:1 w:1) // Storage: System Account (r:2 w:1) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Nominators (r:1 w:0) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: Balances Locks (r:1 w:1) - // Storage: BagsList ListNodes (r:3 w:3) - // Storage: Staking Bonded (r:1 w:0) - // Storage: BagsList ListBags (r:2 w:2) + // Storage: VoterList ListNodes (r:3 w:3) + // Storage: VoterList ListBags (r:2 w:2) // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - Weight::from_ref_time(121_254_000 as u64) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(147_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(18 as u64)) .saturating_add(RocksDbWeight::get().writes(13 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - Weight::from_ref_time(41_928_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(52_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(54_406_927 as u64) + // Standard Error: 1_104 + .saturating_add(Weight::from_ref_time(14_722 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools SubPoolsStorage (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - Weight::from_ref_time(81_611_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(56_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(8 as u64)) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(99_241_507 as u64) + // Standard Error: 2_139 + .saturating_add(Weight::from_ref_time(1_948 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(9 as u64)) .saturating_add(RocksDbWeight::get().writes(7 as u64)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools SubPoolsStorage (r:1 w:1) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:1) + // Storage: Staking Ledger (r:1 w:1) // Storage: Staking SlashingSpans (r:1 w:0) // Storage: Staking Validators (r:1 w:0) // Storage: Staking Nominators (r:1 w:0) @@ -410,29 +455,32 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: NominationPools CounterForRewardPools (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) + // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - Weight::from_ref_time(139_849_000 as u64) - .saturating_add(RocksDbWeight::get().reads(19 as u64)) - .saturating_add(RocksDbWeight::get().writes(16 as u64)) + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(152_086_647 as u64) + // Standard Error: 2_975 + .saturating_add(Weight::from_ref_time(633 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(20 as u64)) + .saturating_add(RocksDbWeight::get().writes(17 as u64)) } + // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: NominationPools MinCreateBond (r:1 w:0) // Storage: NominationPools MinJoinBond (r:1 w:0) // Storage: NominationPools MaxPools (r:1 w:0) // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: NominationPools PoolMembers (r:1 w:1) - // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:1 w:0) // Storage: NominationPools MaxPoolMembers (r:1 w:0) // Storage: NominationPools CounterForPoolMembers (r:1 w:1) // Storage: System Account (r:2 w:2) - // Storage: Staking Ledger (r:1 w:1) // Storage: Staking Bonded (r:1 w:1) + // Storage: Staking Ledger (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking HistoryDepth (r:1 w:0) // Storage: Balances Locks (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: NominationPools CounterForRewardPools (r:1 w:1) @@ -441,36 +489,40 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - Weight::from_ref_time(126_246_000 as u64) - .saturating_add(RocksDbWeight::get().reads(22 as u64)) + // Minimum execution time: 130_000 nanoseconds. + Weight::from_ref_time(132_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(21 as u64)) .saturating_add(RocksDbWeight::get().writes(15 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) // Storage: Staking MinNominatorBond (r:1 w:0) // Storage: Staking Nominators (r:1 w:1) // Storage: Staking MaxNominatorsCount (r:1 w:0) // Storage: Staking Validators (r:2 w:0) // Storage: Staking CurrentEra (r:1 w:0) - // Storage: Staking Bonded (r:1 w:0) - // Storage: BagsList ListNodes (r:1 w:1) - // Storage: BagsList ListBags (r:1 w:1) - // Storage: BagsList CounterForListNodes (r:1 w:1) + // Storage: VoterList ListNodes (r:1 w:1) + // Storage: VoterList ListBags (r:1 w:1) + // Storage: VoterList CounterForListNodes (r:1 w:1) // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - Weight::from_ref_time(48_829_000 as u64) - // Standard Error: 10_000 - .saturating_add(Weight::from_ref_time(2_204_000 as u64).saturating_mul(n as u64)) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(58_146_514 as u64) + // Standard Error: 5_979 + .saturating_add(Weight::from_ref_time(1_361_282 as u64).saturating_mul(n as u64)) .saturating_add(RocksDbWeight::get().reads(12 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) .saturating_add(RocksDbWeight::get().writes(5 as u64)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - Weight::from_ref_time(26_761_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(37_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) @@ -478,9 +530,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - Weight::from_ref_time(14_519_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(n as u64)) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_098_252 as u64) + // Standard Error: 231 + .saturating_add(Weight::from_ref_time(654 as u64).saturating_mul(n as u64)) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } @@ -490,26 +543,51 @@ impl WeightInfo for () { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - Weight::from_ref_time(6_173_000 as u64) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000 as u64) .saturating_add(RocksDbWeight::get().writes(5 as u64)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - Weight::from_ref_time(22_261_000 as u64) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: NominationPools BondedPools (r:1 w:0) + // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) // Storage: Staking Validators (r:1 w:0) // Storage: Staking Nominators (r:1 w:1) // Storage: Staking CounterForNominators (r:1 w:1) - // Storage: BagsList ListNodes (r:1 w:1) - // Storage: BagsList ListBags (r:1 w:1) - // Storage: BagsList CounterForListNodes (r:1 w:1) + // Storage: VoterList ListNodes (r:1 w:1) + // Storage: VoterList ListBags (r:1 w:1) + // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - Weight::from_ref_time(47_959_000 as u64) - .saturating_add(RocksDbWeight::get().reads(8 as u64)) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(62_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(9 as u64)) .saturating_add(RocksDbWeight::get().writes(5 as u64)) } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission() -> Weight { + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(24_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_max() -> Weight { + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: NominationPools BondedPools (r:1 w:1) + fn set_commission_throttle() -> Weight { + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_000_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } } From 86bef40025e5d8e862f65396303b55e8a1d9129c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 14:19:39 +0000 Subject: [PATCH 078/221] merge master --- Cargo.lock | 205 +++++++++++++++++++++++++++++------------------------ 1 file changed, 111 insertions(+), 94 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d65411de0125c..061dc3c9be180 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,7 +42,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", "opaque-debug 0.3.0", @@ -258,7 +258,7 @@ checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" dependencies = [ "async-io", "blocking", - "cfg-if", + "cfg-if 1.0.0", "event-listener", "futures-lite", "libc", @@ -393,7 +393,7 @@ checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object 0.27.1", @@ -623,7 +623,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", - "cfg-if", + "cfg-if 1.0.0", "constant_time_eq", ] @@ -842,6 +842,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -860,7 +866,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cipher", "cpufeatures", "zeroize", @@ -934,7 +940,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1078,7 +1084,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44919ecaf6f99e8e737bc239408931c9a01e9a6c74814fee8242dd2506b65390" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "glob", ] @@ -1261,7 +1267,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1308,7 +1314,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", ] @@ -1318,7 +1324,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] @@ -1329,7 +1335,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", "memoffset", @@ -1342,7 +1348,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] @@ -1656,7 +1662,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "dirs-sys-next", ] @@ -1695,7 +1701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" dependencies = [ "byteorder", - "quick-error", + "quick-error 1.2.3", ] [[package]] @@ -2033,7 +2039,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "winapi", @@ -2080,7 +2086,7 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crc32fast", "libc", "libz-sys", @@ -2279,7 +2285,7 @@ version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "parity-scale-codec", "scale-info", "serde", @@ -2661,7 +2667,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", @@ -2674,7 +2680,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.10.0+wasi-snapshot-preview1", ] @@ -2794,16 +2800,16 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "handlebars" -version = "4.3.5" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e4ab33f1213cdc25b5fa45c76881240cfe79284cf2b395e8b9e312a30a2fd" +checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b" dependencies = [ "log", "pest", "pest_derive", + "quick-error 2.0.0", "serde", "serde_json", - "thiserror", ] [[package]] @@ -2946,9 +2952,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] name = "httpdate" @@ -2964,9 +2970,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.22" +version = "0.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" dependencies = [ "bytes", "futures-channel", @@ -2977,7 +2983,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.4", + "itoa 0.4.8", "pin-project-lite 0.2.6", "socket2", "tokio", @@ -3092,7 +3098,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -3323,7 +3329,7 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3636d281d46c3b64182eb3a0a42b7b483191a2ecc3f05301fa67403f7c9bc949" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "ecdsa", "elliptic-curve", "sha2 0.10.2", @@ -3527,7 +3533,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "winapi", ] @@ -4018,7 +4024,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "value-bag", ] @@ -4090,6 +4096,12 @@ dependencies = [ "libc", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "match_cfg" version = "0.1.0" @@ -4226,7 +4238,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2be9a9090bc1cac2930688fa9478092a64c6a92ddc6ae0692d46b37d9cab709" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "downcast", "fragile", "lazy_static", @@ -4241,7 +4253,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d702a0530a0141cf4ed147cf5ec7be6f2c187d4e37fcbefc39cf34116bfe8f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "proc-macro2", "quote", "syn", @@ -4445,7 +4457,7 @@ checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "memoffset", ] @@ -4457,7 +4469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 1.0.0", "libc", ] @@ -6429,7 +6441,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "hashbrown 0.12.3", "impl-trait-for-tuples", "parity-util-mem-derive", @@ -6489,7 +6501,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall", @@ -6503,7 +6515,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", @@ -6548,19 +6560,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.4.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.4.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" dependencies = [ "pest", "pest_generator", @@ -6568,9 +6579,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.4.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", @@ -6581,13 +6592,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.4.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ - "once_cell", + "maplit", "pest", - "sha1", + "sha-1 0.8.2", ] [[package]] @@ -6690,15 +6701,14 @@ dependencies = [ [[package]] name = "polling" -version = "2.4.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" +checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" dependencies = [ - "autocfg", - "cfg-if", + "cfg-if 0.1.10", "libc", "log", - "wepoll-ffi", + "wepoll-sys", "winapi", ] @@ -6719,7 +6729,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "opaque-debug 0.3.0", "universal-hash", @@ -6835,7 +6845,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fnv", "lazy_static", "memchr", @@ -6967,6 +6977,12 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-error" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" + [[package]] name = "quickcheck" version = "1.0.3" @@ -7278,7 +7294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error", + "quick-error 1.2.3", ] [[package]] @@ -7463,7 +7479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error", + "quick-error 1.2.3", "tempfile", ] @@ -8028,7 +8044,7 @@ dependencies = [ name = "sc-executor-wasmtime" version = "0.10.0-dev" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "log", "once_cell", @@ -8831,7 +8847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9" dependencies = [ "bitvec", - "cfg-if", + "cfg-if 1.0.0", "derive_more", "parity-scale-codec", "scale-info-derive", @@ -9062,6 +9078,18 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha-1" version = "0.9.4" @@ -9069,23 +9097,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpuid-bool", "digest 0.9.0", "opaque-debug 0.3.0", ] -[[package]] -name = "sha1" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.3", -] - [[package]] name = "sha2" version = "0.8.2" @@ -9105,7 +9122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", @@ -9117,7 +9134,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.3", ] @@ -9252,7 +9269,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1", + "sha-1 0.9.4", ] [[package]] @@ -10382,7 +10399,7 @@ version = "2.0.0" dependencies = [ "beefy-merkle-tree", "beefy-primitives", - "cfg-if", + "cfg-if 1.0.0", "frame-support", "frame-system", "frame-system-rpc-runtime-api", @@ -10571,7 +10588,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand", "libc", "redox_syscall", @@ -10818,7 +10835,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "log", "pin-project-lite 0.2.6", "tracing-attributes", @@ -10961,7 +10978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" dependencies = [ "async-trait", - "cfg-if", + "cfg-if 1.0.0", "data-encoding", "enum-as-inner", "futures-channel", @@ -10984,7 +11001,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "futures-util", "ipconfig", "lazy_static", @@ -11058,7 +11075,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "digest 0.10.3", "rand 0.8.5", "static_assertions", @@ -11273,7 +11290,7 @@ version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -11298,7 +11315,7 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -11404,7 +11421,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f727a39e7161f7438ddb8eafe571b67c576a8c2fb459f666d9053b5bba4afdea" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "indexmap", "js-sys", "loupe", @@ -11524,7 +11541,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd7fdc60e252a795c849b3f78a81a134783051407e7e279c10b7019139ef8dc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "enum-iterator", "enumset", "leb128", @@ -11549,7 +11566,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcff0cd2c01a8de6009fd863b14ea883132a468a24f2d2ee59dc34453d3a31b5" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "enum-iterator", "enumset", "leb128", @@ -11596,7 +11613,7 @@ checksum = "afdc46158517c2769f9938bc222a7d41b3bb330824196279d8aa2d667cd40641" dependencies = [ "backtrace", "cc", - "cfg-if", + "cfg-if 1.0.0", "enum-iterator", "indexmap", "libc", @@ -11667,7 +11684,7 @@ checksum = "8a10dc9784d8c3a33c970e3939180424955f08af2e7f20368ec02685a0e8f065" dependencies = [ "anyhow", "bincode", - "cfg-if", + "cfg-if 1.0.0", "indexmap", "libc", "log", @@ -11693,7 +11710,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee4dbdc6daf68528cad1275ac91e3f51848ce9824385facc94c759f529decdf8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -11765,7 +11782,7 @@ dependencies = [ "addr2line", "anyhow", "bincode", - "cfg-if", + "cfg-if 1.0.0", "cpp_demangle", "gimli 0.26.1", "log", @@ -11800,7 +11817,7 @@ checksum = "ae79e0515160bd5abee5df50a16c4eb8db9f71b530fc988ae1d9ce34dcb8dd01" dependencies = [ "anyhow", "cc", - "cfg-if", + "cfg-if 1.0.0", "indexmap", "libc", "log", @@ -11877,10 +11894,10 @@ dependencies = [ ] [[package]] -name = "wepoll-ffi" -version = "0.1.2" +name = "wepoll-sys" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" dependencies = [ "cc", ] From c801b8e4f16319e4bfaa116cab507f0d2c4df5cf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 14:51:21 +0000 Subject: [PATCH 079/221] + test: do_not_throttle_initial_commission_set --- frame/nomination-pools/src/tests.rs | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 8e6712bf44aa9..5a7c2b45e36b3 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -557,6 +557,68 @@ mod bonded_pool { }); } + #[test] + fn do_not_throttle_initial_commission_set() { + ExtBuilder::default().build_and_execute(|| { + // Set a commission throttle for pool 1 + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 5_u64 + } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.throttle, + Some(CommissionThrottle { + change_rate: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(5), + min_delay: 5_u64 + }, + previous_set_at: None + }) + ); + + // add commission that is beyond the throttle boundaries + // setting on same block as throttle update (`min_delay` of 5 blocks should not fail tx) + // `None` -> 50% commission ( `max_increase` of 5% should not fail tx) + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(25), + Some(900) + )); + + // now throttle should take effect. + + // attempt 1% commission increase on same block + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(26), + Some(900) + ), + Error::::CommissionChangeThrottled + ); + + // move to a block beyond `min_delay` + run_blocks(5); + + // attempt 26% commission increase + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(51), + Some(900) + ), + Error::::CommissionChangeThrottled + ); + }) + } + #[test] fn set_commission_throttle_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { From d02c6713032cc0642b53ac394654cfc7a9924d7b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:18:08 +0000 Subject: [PATCH 080/221] bring comments up to date --- frame/nomination-pools/src/lib.rs | 66 ++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 9736150929dff..cf47eba16c070 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -540,23 +540,28 @@ pub struct PoolRoles { /// Pool commission. /// -/// The pool depositor can set a commission upon a pool creation, from which the pool owner can -/// update thereafter. The `max` commission value can only be decreased after the initial value is -/// set, as to prevent the commission from repeatedly increasing. +/// The pool `root` can set commission configuration after pool creation. By default, all commission +/// values are `None`. Pool `root`s can set `max` and `throttle` configurations before setting an +/// initial commission `current` value - the commission percentage and payee of commission. /// -/// A commission throttle is also optional, allowing the pool to set strict limits to how much +/// The `max` commission value can only be decreased after the initial value is +/// set, to prevent commission from repeatedly increasing. +/// +/// An optional commission throttle allows the pool to set strict limits to how much /// commission can change in each update, and how often updates can take place. +/// If a `throttle` is set *before* a commission `current` is set, the initial commission `current` +/// value will *not* be subject to throttling. Subsequent commission updates will be. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct Commission { - /// The active commission rate of the pool along with the account commission is paid to. + /// Optional commission rate of the pool along with the account commission is paid to. pub current: Option<(Perbill, T::AccountId)>, - /// An optional maximum commission that can be set by the pool root. Once set, this value - /// cannot be updated. + /// Optional maximum commission that can be set by the pool `root`. Once set, this value + /// can only be updated to a decreased value. pub max: Option, - /// Configiration around how often the commission can be updated, and metadata around the - /// previous round of updates. + /// Optional configiration around how often commission can be updated, and when the last + /// commission update took place. pub throttle: Option>, } @@ -584,9 +589,12 @@ impl Commission { } /// Returns true if a commission percentage updating to `to` would exhaust the throttle limit. + /// /// A commission update will be throttled (disallowed) if: /// 1. not enough blocks have passed since the previous commission update took place, and /// 2. the new commission is larger than the maximum allowed increase. + /// + /// Throttlnig is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { let throttle = self.throttle.as_ref().map(|t| t).or(None); if let Some(t) = throttle { @@ -606,11 +614,8 @@ impl Commission { } /// Set the pool's commission. - /// /// Update commission accordingly based on `commission` and `payee`. - /// /// If throttle is present, record the current block as the previously updated commission. - /// /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. fn maybe_update_current( &mut self, @@ -636,13 +641,17 @@ impl Commission { } /// Set the pool's maximum commission. + /// The pool's maximum commission can be set to any value initially, and only smaller values + /// thereafter. If larger values are attempted, this function will return a dispatch error. + /// + /// If `current.0` is larger than an updated max commission value, then `current.0` will also be + /// updated to the new maximum. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { return Err(Error::::MaxCommissionRestricted.into()) } *old = new_max; - // ensure current is also less then the new maximum. let _ = self.current.as_mut().map(|(x, _)| *x = (*x).min(new_max)); } else { self.max = Some(new_max) @@ -651,6 +660,9 @@ impl Commission { } /// Set the pool's commission throttle settings. + /// Once a throttle configuration has been set, only more restrictive values can be set + /// thereafter. These restrictions translate to increased `min_delay` values and decreased + /// `max_increase` values. fn maybe_update_throttle( &mut self, change_rate: CommissionThrottlePrefs, @@ -668,6 +680,8 @@ impl Commission { } /// Gets the current commission (if any) and payee to be paid. + /// A zero commission along with a `None` payee is returned in the event a commission has not + /// been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -705,6 +719,8 @@ impl CommissionThrottle { } /// Pool commission throttle preferences. +/// A commission throttle consists of 2 values; (1) the maximum allowed commission change, and (2) +/// the minimum amount of blocks that must elapse before commission updates are allowed again. /// /// Throttle prefs need to be passed and configured together. This struct is used in /// the `set_commission_throttle` call as well as in CommissionThrottle. @@ -2178,13 +2194,15 @@ pub mod pallet { /// Set the commission of a pool. /// - /// The dispatch origin of this call must be signed by the root role of the pool. + /// The dispatch origin of this call must be signed by the `root` role of the pool. /// If the pool has a max commission set, the commission supplied must be less or - /// equal to the max. + /// equal to that value. /// - /// If the max commission has _not yet_ been set, then the commission is not - /// restricted. - /// A `payee` must already be present before commission can be set. + /// If the max commission has _not yet_ been set, then the commission range is not + /// bounded. + /// A `payee` must be provided if commission has not yet been set (still `None`). Once + /// commission has been set, the `payee` can be omitted in further calls. If a `payee` + /// update is desired, the commission must still be passed into the call. #[pallet::weight(T::WeightInfo::set_commission())] #[transactional] pub fn set_commission( @@ -2215,12 +2233,12 @@ pub mod pallet { /// Set the maximum commission of a pool. /// - /// The dispatch origin of this call must be signed by the root role of the pool. + /// The dispatch origin of this call must be signed by the `root` role of the pool. /// If a maximum commission already exists prior to this call, then the updated - /// max commission must be lower, otherwise this call will fail. + /// max commission must be *lower*, otherwise this call will fail. /// /// This call also updates the pool's current commission to the new maximum if the - /// current commission is higher than the supplied maximum. + /// current commission is higher than the maximum supplied. #[pallet::weight(T::WeightInfo::set_commission_max())] #[transactional] pub fn set_commission_max( @@ -2240,11 +2258,11 @@ pub mod pallet { /// Set the commission throttle for a pool. /// - /// The dispatch origin of this call must be signed by the root role of the pool. - /// If a throttle is already present, this call will only succeed if a more restrictive + /// The dispatch origin of this call must be signed by the `root` role of the pool. + /// If a throttle is already present, this call will only succeed if a more *restrictive* /// throttle configuration is given. /// - /// If a throttle configuration does not yet exist, the values are set. + /// If a throttle configuration does not yet exist, the provided values are set. #[pallet::weight(T::WeightInfo::set_commission_throttle())] #[transactional] pub fn set_commission_throttle( From 8fe6af1b99d05fda3a8d88a13682a6655b514d56 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:24:13 +0000 Subject: [PATCH 081/221] simplify get_commission_and_payee --- frame/nomination-pools/src/lib.rs | 20 ++++++-------------- frame/nomination-pools/src/tests.rs | 2 +- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cf47eba16c070..12767bc7f9bd7 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -574,15 +574,10 @@ impl Default for Commission { impl Commission { /// Get the current commission percentage of this pool. /// Returns zero if commission has not yet been configured. - fn percent(&self) -> Perbill { + fn commission_or_zero(&self) -> Perbill { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } - /// check if the current commission is a non-zero amount - fn commission_set(&self) -> bool { - self.percent() > Perbill::zero() - } - /// Gets the current commission payee of this pool as a reference. fn payee(&self) -> Option<&T::AccountId> { self.current.as_ref().map(|(_, p)| p).or(None) @@ -606,7 +601,7 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(self.percent()) > t.change_rate.max_increase && + (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase && self.current.is_some() } else { return false @@ -686,13 +681,10 @@ impl Commission { &self, pending_rewards: &BalanceOf, ) -> (BalanceOf, Option) { - if self.commission_set() { - self.payee().map_or((Zero::zero(), None), |p| { - (self.percent() * *pending_rewards, Some(p.clone())) - }) - } else { - (Zero::zero(), None) - } + self.current + .as_ref() + .map(|(commission, payee)| (*commission * *pending_rewards, Some(payee.clone()))) + .unwrap_or((Zero::zero(), None)) } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5a7c2b45e36b3..e13fb5aa6fad4 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -258,7 +258,7 @@ mod bonded_pool { let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.percent(), Perbill::from_percent(50)); + assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), From 00ec90e3c4318ca4b1f1965f114ac7afdbd268c9 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:36:36 +0000 Subject: [PATCH 082/221] use transfer() --- frame/nomination-pools/src/lib.rs | 5 ++--- frame/nomination-pools/src/tests.rs | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 12767bc7f9bd7..da8ce94dfc58c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2605,13 +2605,12 @@ impl Pallet { // Defensive: The commission payee is also checked for existence. if pool_commission != &BalanceOf::::zero() { if let Some(p) = payee { - T::Currency::withdraw( + T::Currency::transfer( &bonded_pool.reward_account(), + &p, *pool_commission, - WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; - T::Currency::deposit_creating(&p, *pool_commission); } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index e13fb5aa6fad4..d409bf7fb4047 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -1384,6 +1384,9 @@ mod claim_payout { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + // Set a commission pool 1 to 50%, with a payee set to `2` assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), From de7b57ab50f280a462b69e94a6d91ee91210632b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:38:43 +0000 Subject: [PATCH 083/221] tidy up --- frame/nomination-pools/src/lib.rs | 4 ++-- frame/nomination-pools/src/tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index da8ce94dfc58c..3da030e2d8154 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -274,8 +274,8 @@ use frame_support::{ pallet_prelude::{MaxEncodedLen, *}, storage::bounded_btree_map::BoundedBTreeMap, traits::{ - tokens::WithdrawReasons, Currency, Defensive, DefensiveOption, DefensiveResult, - DefensiveSaturating, ExistenceRequirement, Get, + Currency, Defensive, DefensiveOption, DefensiveResult, DefensiveSaturating, + ExistenceRequirement, Get, }, transactional, CloneNoBound, DefaultNoBound, RuntimeDebugNoBound, }; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index d409bf7fb4047..5fd4052ca02e8 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -1384,8 +1384,8 @@ mod claim_payout { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit - let _ = Balances::deposit_creating(&2, 5); + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 50%, with a payee set to `2` assert_ok!(Pools::set_commission( From 610d294d7c13cb5bb5e6ef6922db5671639e1cef Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:45:03 +0000 Subject: [PATCH 084/221] rm payee() --- frame/nomination-pools/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3da030e2d8154..e5c6a295537a9 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -578,11 +578,6 @@ impl Commission { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } - /// Gets the current commission payee of this pool as a reference. - fn payee(&self) -> Option<&T::AccountId> { - self.current.as_ref().map(|(_, p)| p).or(None) - } - /// Returns true if a commission percentage updating to `to` would exhaust the throttle limit. /// /// A commission update will be throttled (disallowed) if: @@ -2208,7 +2203,7 @@ pub mod pallet { ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); let final_payee = payee - .or(bonded_pool.commission.payee().cloned()) + .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; bonded_pool From 2de069350fd31657b2ec17a2577c802df22ec75d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:48:04 +0000 Subject: [PATCH 085/221] fixes --- frame/nomination-pools/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e5c6a295537a9..a74e1fe331bcb 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -584,7 +584,7 @@ impl Commission { /// 1. not enough blocks have passed since the previous commission update took place, and /// 2. the new commission is larger than the maximum allowed increase. /// - /// Throttlnig is not applied to commission updates if `current` is still `None`. + /// Throttling is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { let throttle = self.throttle.as_ref().map(|t| t).or(None); if let Some(t) = throttle { @@ -623,10 +623,7 @@ impl Commission { let _ = self .throttle .as_mut() - .map(|t| { - t.register_change(>::block_number()); - }) - .or(None); + .map(|t| t.register_change(>::block_number())); Ok(()) } From 635d23cead95599d697b23dc86a6b0f8d4850b78 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 9 Nov 2022 15:59:45 +0000 Subject: [PATCH 086/221] migration fixes --- frame/nomination-pools/src/migration.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 08121b58ba95b..7a4efe027edb2 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -517,11 +517,9 @@ pub mod v4 { #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), &'static str> { ensure!( - BondedPools::::iter().all(|(_, inner)| inner.contains_key("commission")), - "not all BondedPools have a `commission` field" - ); - ensure!( - BondedPools::::iter().all(|(_, inner)| inner.commission.is_none()), + BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && + inner.commission.max.is_none() && + inner.commission.throttle.is_none()), "a commission value has been incorrectly set" ); ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); From 4d412d7937f815b0b5c92a260d90792a8dbe3eb9 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 12:00:01 +0000 Subject: [PATCH 087/221] fixes + refactor get_commission_and_payee --- frame/nomination-pools/src/lib.rs | 30 +++++++++++++---------------- frame/nomination-pools/src/tests.rs | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2d9ba68a50cfa..0baf47affba11 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -716,11 +716,11 @@ impl Commission { fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, - ) -> (BalanceOf, Option) { + ) -> Option<(BalanceOf, T::AccountId)> { self.current .as_ref() - .map(|(commission, payee)| (*commission * *pending_rewards, Some(payee.clone()))) - .unwrap_or((Zero::zero(), None)) + .map(|(commission, payee)| (*commission * *pending_rewards, payee.clone())) + .or(None) } } @@ -2210,7 +2210,6 @@ pub mod pallet { /// commission has been set, the `payee` can be omitted in further calls. If a `payee` /// update is desired, the commission must still be passed into the call. #[pallet::weight(T::WeightInfo::set_commission())] - #[transactional] pub fn set_commission( origin: OriginFor, pool_id: PoolId, @@ -2246,7 +2245,6 @@ pub mod pallet { /// This call also updates the pool's current commission to the new maximum if the /// current commission is higher than the maximum supplied. #[pallet::weight(T::WeightInfo::set_commission_max())] - #[transactional] pub fn set_commission_max( origin: OriginFor, pool_id: PoolId, @@ -2270,7 +2268,6 @@ pub mod pallet { /// /// If a throttle configuration does not yet exist, the provided values are set. #[pallet::weight(T::WeightInfo::set_commission_throttle())] - #[transactional] pub fn set_commission_throttle( origin: OriginFor, pool_id: PoolId, @@ -2605,20 +2602,19 @@ impl Pallet { // Gets the commission percentage and payee to be paid if commission has // been set. Otherwise, a zero commission and payee of `None` is returned. - let (pool_commission, payee) = - &bonded_pool.commission.get_commission_and_payee(&pending_rewards); + let commission_current = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); - // Deduct any outstanding commission from the reward being claimed. - pending_rewards = pending_rewards.saturating_sub(*pool_commission); + if let Some((pool_commission, payee)) = commission_current { + // Deduct any outstanding commission from the reward being claimed. + pending_rewards = pending_rewards.saturating_sub(*pool_commission); - // If a non-zero commission has been applied to the pool, deduct the share from - // `reward_pool` and send the amount to the commission `payee`. - // Defensive: The commission payee is also checked for existence. - if pool_commission != &BalanceOf::::zero() { - if let Some(p) = payee { + // If a non-zero commission has been applied to the pool, deduct the share from + // `reward_pool` and send the amount to the commission `payee`. + // Defensive: The commission payee is also checked for existence. + if pool_commission != &BalanceOf::::zero() { T::Currency::transfer( &bonded_pool.reward_account(), - &p, + &payee, *pool_commission, ExistenceRequirement::KeepAlive, )?; @@ -2639,7 +2635,7 @@ impl Pallet { member: member_account.clone(), pool_id: member.pool_id, payout: pending_rewards, - commission: *pool_commission, + commission: commission_current.as_ref().map(|(c, _)| *c).unwrap_or(Zero::zero()), }); Ok(pending_rewards) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 2f6d51433071d..f41bd6ef13ea8 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5468,8 +5468,18 @@ mod reward_counter_precision { pool_events_since_last_call(), vec![ Event::Bonded { member: 20, pool_id: 1, bonded: 5000000000000, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7333333333333333333 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3666666666666666666 } + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 7333333333333333333, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 3666666666666666666, + commission: 0 + } ] ); }) From 4c89cf1b28d1a0bbc9bf988b6ba0183dff86004c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 12:07:21 +0000 Subject: [PATCH 088/221] revert Cargo.lock --- Cargo.lock | 205 ++++++++++++++++++++++++----------------------------- 1 file changed, 94 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4b4f190d2cbc..a13768a64a4ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,7 +42,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "opaque-debug 0.3.0", @@ -258,7 +258,7 @@ checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" dependencies = [ "async-io", "blocking", - "cfg-if 1.0.0", + "cfg-if", "event-listener", "futures-lite", "libc", @@ -393,7 +393,7 @@ checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object 0.27.1", @@ -623,7 +623,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.2", "cc", - "cfg-if 1.0.0", + "cfg-if", "constant_time_eq", ] @@ -842,12 +842,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -866,7 +860,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cipher", "cpufeatures", "zeroize", @@ -940,7 +934,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1084,7 +1078,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44919ecaf6f99e8e737bc239408931c9a01e9a6c74814fee8242dd2506b65390" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "glob", ] @@ -1267,7 +1261,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1314,7 +1308,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] @@ -1324,7 +1318,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] @@ -1335,7 +1329,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", @@ -1348,7 +1342,7 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "lazy_static", ] @@ -1662,7 +1656,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "dirs-sys-next", ] @@ -1701,7 +1695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" dependencies = [ "byteorder", - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -2039,7 +2033,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "winapi", @@ -2086,7 +2080,7 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crc32fast", "libc", "libz-sys", @@ -2285,7 +2279,7 @@ version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df6bb8542ef006ef0de09a5c4420787d79823c0ed7924225822362fd2bf2ff2d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "parity-scale-codec", "scale-info", "serde", @@ -2667,7 +2661,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", @@ -2680,7 +2674,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.10.0+wasi-snapshot-preview1", ] @@ -2800,16 +2794,16 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "handlebars" -version = "4.2.2" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b" +checksum = "433e4ab33f1213cdc25b5fa45c76881240cfe79284cf2b395e8b9e312a30a2fd" dependencies = [ "log", "pest", "pest_derive", - "quick-error 2.0.0", "serde", "serde_json", + "thiserror", ] [[package]] @@ -2953,9 +2947,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -2971,9 +2965,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.16" +version = "0.14.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" dependencies = [ "bytes", "futures-channel", @@ -2984,7 +2978,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 0.4.8", + "itoa 1.0.4", "pin-project-lite 0.2.6", "socket2", "tokio", @@ -3099,7 +3093,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -3330,7 +3324,7 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3636d281d46c3b64182eb3a0a42b7b483191a2ecc3f05301fa67403f7c9bc949" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ecdsa", "elliptic-curve", "sha2 0.10.2", @@ -3534,7 +3528,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] @@ -4025,7 +4019,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "value-bag", ] @@ -4097,12 +4091,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "match_cfg" version = "0.1.0" @@ -4229,7 +4217,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2be9a9090bc1cac2930688fa9478092a64c6a92ddc6ae0692d46b37d9cab709" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "downcast", "fragile", "lazy_static", @@ -4244,7 +4232,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86d702a0530a0141cf4ed147cf5ec7be6f2c187d4e37fcbefc39cf34116bfe8f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "proc-macro2", "quote", "syn", @@ -4448,7 +4436,7 @@ checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" dependencies = [ "bitflags", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "memoffset", ] @@ -4460,7 +4448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", ] @@ -6447,7 +6435,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "hashbrown 0.12.3", "impl-trait-for-tuples", "parity-util-mem-derive", @@ -6507,7 +6495,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "instant", "libc", "redox_syscall", @@ -6521,7 +6509,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -6566,18 +6554,19 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ + "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.1.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +checksum = "60b75706b9642ebcb34dab3bc7750f811609a0eb1dd8b88c2d15bf628c1c65b2" dependencies = [ "pest", "pest_generator", @@ -6585,9 +6574,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +checksum = "f4f9272122f5979a6511a749af9db9bfc810393f63119970d7085fed1c4ea0db" dependencies = [ "pest", "pest_meta", @@ -6598,13 +6587,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +checksum = "4c8717927f9b79515e565a64fe46c38b8cd0427e64c40680b14a7365ab09ac8d" dependencies = [ - "maplit", + "once_cell", "pest", - "sha-1 0.8.2", + "sha1", ] [[package]] @@ -6707,14 +6696,15 @@ dependencies = [ [[package]] name = "polling" -version = "2.0.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2" dependencies = [ - "cfg-if 0.1.10", + "autocfg", + "cfg-if", "libc", "log", - "wepoll-sys", + "wepoll-ffi", "winapi", ] @@ -6735,7 +6725,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "opaque-debug 0.3.0", "universal-hash", @@ -6851,7 +6841,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f64969ffd5dd8f39bd57a68ac53c163a095ed9d0fb707146da1b27025a3504" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fnv", "lazy_static", "memchr", @@ -6983,12 +6973,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" - [[package]] name = "quickcheck" version = "1.0.3" @@ -7300,7 +7284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -7485,7 +7469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", ] @@ -8050,7 +8034,7 @@ dependencies = [ name = "sc-executor-wasmtime" version = "0.10.0-dev" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "once_cell", @@ -8853,7 +8837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9" dependencies = [ "bitvec", - "cfg-if 1.0.0", + "cfg-if", "derive_more", "parity-scale-codec", "scale-info-derive", @@ -9084,18 +9068,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - [[package]] name = "sha-1" version = "0.9.4" @@ -9103,12 +9075,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpuid-bool", "digest 0.9.0", "opaque-debug 0.3.0", ] +[[package]] +name = "sha1" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006769ba83e921b3085caa8334186b00cf92b4cb1a6cf4632fbccc8eff5c7549" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.3", +] + [[package]] name = "sha2" version = "0.8.2" @@ -9128,7 +9111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.9.0", "opaque-debug 0.3.0", @@ -9140,7 +9123,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest 0.10.3", ] @@ -9275,7 +9258,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "sha-1 0.9.4", + "sha-1", ] [[package]] @@ -10405,7 +10388,7 @@ version = "2.0.0" dependencies = [ "beefy-merkle-tree", "beefy-primitives", - "cfg-if 1.0.0", + "cfg-if", "frame-support", "frame-system", "frame-system-rpc-runtime-api", @@ -10594,7 +10577,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", "libc", "redox_syscall", @@ -10841,7 +10824,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "log", "pin-project-lite 0.2.6", "tracing-attributes", @@ -10984,7 +10967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" dependencies = [ "async-trait", - "cfg-if 1.0.0", + "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", @@ -11007,7 +10990,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "futures-util", "ipconfig", "lazy_static", @@ -11081,7 +11064,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "digest 0.10.3", "rand 0.8.5", "static_assertions", @@ -11296,7 +11279,7 @@ version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -11321,7 +11304,7 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3de431a2910c86679c34283a33f66f4e4abd7e0aec27b6669060148872aadf94" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -11427,7 +11410,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f727a39e7161f7438ddb8eafe571b67c576a8c2fb459f666d9053b5bba4afdea" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "indexmap", "js-sys", "loupe", @@ -11547,7 +11530,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd7fdc60e252a795c849b3f78a81a134783051407e7e279c10b7019139ef8dc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "enum-iterator", "enumset", "leb128", @@ -11572,7 +11555,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcff0cd2c01a8de6009fd863b14ea883132a468a24f2d2ee59dc34453d3a31b5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "enum-iterator", "enumset", "leb128", @@ -11619,7 +11602,7 @@ checksum = "afdc46158517c2769f9938bc222a7d41b3bb330824196279d8aa2d667cd40641" dependencies = [ "backtrace", "cc", - "cfg-if 1.0.0", + "cfg-if", "enum-iterator", "indexmap", "libc", @@ -11690,7 +11673,7 @@ checksum = "8a10dc9784d8c3a33c970e3939180424955f08af2e7f20368ec02685a0e8f065" dependencies = [ "anyhow", "bincode", - "cfg-if 1.0.0", + "cfg-if", "indexmap", "libc", "log", @@ -11716,7 +11699,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee4dbdc6daf68528cad1275ac91e3f51848ce9824385facc94c759f529decdf8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -11788,7 +11771,7 @@ dependencies = [ "addr2line", "anyhow", "bincode", - "cfg-if 1.0.0", + "cfg-if", "cpp_demangle", "gimli 0.26.1", "log", @@ -11823,7 +11806,7 @@ checksum = "ae79e0515160bd5abee5df50a16c4eb8db9f71b530fc988ae1d9ce34dcb8dd01" dependencies = [ "anyhow", "cc", - "cfg-if 1.0.0", + "cfg-if", "indexmap", "libc", "log", @@ -11900,10 +11883,10 @@ dependencies = [ ] [[package]] -name = "wepoll-sys" -version = "3.0.1" +name = "wepoll-ffi" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" dependencies = [ "cc", ] From dba9edd063f557d4c9cf4cdbbb7e9428e2562099 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 12:16:47 +0000 Subject: [PATCH 089/221] add licence --- frame/nomination-pools/src/weights.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 660c7fc9d2316..344cfac72eb5d 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,3 +1,19 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Autogenerated weights for pallet_nomination_pools //! From 7954f762244932c31612b252bac4fa79c7f319cd Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 12:42:33 +0000 Subject: [PATCH 090/221] add comment --- frame/nomination-pools/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 0baf47affba11..8f258a38c05e0 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -664,6 +664,7 @@ impl Commission { self.current = Some((*commission, payee)); + // update `throttle.previous_set_at` if a throttle has been configured. let _ = self .throttle .as_mut() From 1937108cb3758c49a8551d55dcd10753cf97083a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 14:31:48 +0000 Subject: [PATCH 091/221] bump STORAGE_VERSION --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 8f258a38c05e0..d8a52490cc3f8 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1360,7 +1360,7 @@ pub mod pallet { use sp_runtime::Perbill; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(4); #[pallet::pallet] #[pallet::generate_store(pub(crate) trait Store)] From 9d0ea15335024547e498bbf08925e7fc487a788b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 10 Nov 2022 20:48:39 +0000 Subject: [PATCH 092/221] remove comment formatting --- frame/nomination-pools/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d8a52490cc3f8..2f8f2dd3d8b0c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -593,8 +593,8 @@ pub struct PoolRoles { /// /// An optional commission throttle allows the pool to set strict limits to how much /// commission can change in each update, and how often updates can take place. -/// If a `throttle` is set *before* a commission `current` is set, the initial commission `current` -/// value will *not* be subject to throttling. Subsequent commission updates will be. +/// If a `throttle` is set before a commission `current` is set, the initial commission `current` +/// value will not be subject to throttling. Subsequent commission updates will be. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] @@ -2205,7 +2205,7 @@ pub mod pallet { /// If the pool has a max commission set, the commission supplied must be less or /// equal to that value. /// - /// If the max commission has _not yet_ been set, then the commission range is not + /// If the max commission has not yet been set, then the commission range is not /// bounded. /// A `payee` must be provided if commission has not yet been set (still `None`). Once /// commission has been set, the `payee` can be omitted in further calls. If a `payee` @@ -2241,7 +2241,7 @@ pub mod pallet { /// /// The dispatch origin of this call must be signed by the `root` role of the pool. /// If a maximum commission already exists prior to this call, then the updated - /// max commission must be *lower*, otherwise this call will fail. + /// max commission must be lower, otherwise this call will fail. /// /// This call also updates the pool's current commission to the new maximum if the /// current commission is higher than the maximum supplied. @@ -2264,7 +2264,7 @@ pub mod pallet { /// Set the commission throttle for a pool. /// /// The dispatch origin of this call must be signed by the `root` role of the pool. - /// If a throttle is already present, this call will only succeed if a more *restrictive* + /// If a throttle is already present, this call will only succeed if a more restrictive /// throttle configuration is given. /// /// If a throttle configuration does not yet exist, the provided values are set. From 1a0fd00783fb4290424752114853cdc5cc4519e1 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 12:00:54 +0000 Subject: [PATCH 093/221] create_pool_account supports commission --- .../nomination-pools/benchmarking/src/lib.rs | 76 +++++++++---------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9f1e6c59a2472..78eceb27d43e9 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -71,6 +71,7 @@ fn create_funded_user_with_balance( fn create_pool_account( n: u32, balance: BalanceOf, + commission: Option, ) -> (T::AccountId, T::AccountId) { let ed = CurrencyOf::::minimum_balance(); let pool_creator: T::AccountId = @@ -86,6 +87,20 @@ fn create_pool_account( ) .unwrap(); + if let Some(c) = commission { + let _ = pallet_nomination_pools::BondedPools::::iter() + .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator) + .map(|(pool_id, _)| { + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator.clone()).into(), + pool_id, + c, + Some(pool_creator.clone()), + ) + .expect("pool commission has been set"); + }); + } + let pool_account = pallet_nomination_pools::BondedPools::::iter() .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator) .map(|(pool_id, _)| Pools::::create_bonded_account(pool_id)) @@ -136,14 +151,8 @@ impl ListScenario { sp_std::mem::forget(i); // Create accounts with the origin weight - let (pool_creator1, pool_origin1) = create_pool_account::(USER_SEED + 1, origin_weight); - Pools::::set_commission( - RuntimeOrigin::Signed(pool_creator1.clone()).into(), - 1, - Perbill::from_percent(50), - Some(pool_creator1.clone()), - ) - .unwrap(); + let (pool_creator1, pool_origin1) = + create_pool_account::(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50))); T::Staking::nominate( &pool_origin1, @@ -151,14 +160,9 @@ impl ListScenario { vec![account("random_validator", 0, USER_SEED)], )?; - let (pool_creator2, pool_origin2) = create_pool_account::(USER_SEED + 2, origin_weight); - Pools::::set_commission( - RuntimeOrigin::Signed(pool_creator2.clone()).into(), - 2, - Perbill::from_percent(50), - Some(pool_creator2), - ) - .unwrap(); + let (_, pool_origin2) = + create_pool_account::(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50))); + T::Staking::nominate( &pool_origin2, vec![account("random_validator", 0, USER_SEED)].clone(), @@ -174,14 +178,8 @@ impl ListScenario { dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?; // Create an account with the worst case destination weight - let (pool_creator3, pool_dest1) = create_pool_account::(USER_SEED + 3, dest_weight); - Pools::::set_commission( - RuntimeOrigin::Signed(pool_creator3.clone()).into(), - 3, - Perbill::from_percent(50), - Some(pool_creator3), - ) - .unwrap(); + let (_, pool_dest1) = + create_pool_account::(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50))); T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?; @@ -299,7 +297,7 @@ frame_benchmarking::benchmarks! { claim_payout { let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let ed = CurrencyOf::::minimum_balance(); - let (depositor, pool_account) = create_pool_account::(0, origin_weight); + let (depositor, pool_account) = create_pool_account::(0, origin_weight, None); let reward_account = Pools::::create_reward_account(1); // Send funds to the reward account of the pool @@ -359,7 +357,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -401,7 +399,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Add a new member let min_join_bond = MinJoinBond::::get().max(CurrencyOf::::minimum_balance()); @@ -447,7 +445,7 @@ frame_benchmarking::benchmarks! { let s in 0 .. MAX_SPANS; let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); let depositor_lookup = T::Lookup::unlookup(depositor.clone()); // We set the pool to the destroying state so the depositor can leave @@ -560,7 +558,7 @@ frame_benchmarking::benchmarks! { // Create a pool let min_create_bond = Pools::::depositor_min_bond() * 2u32.into(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); // Create some accounts to nominate. For the sake of benchmarking they don't need to be // actual validators @@ -598,7 +596,7 @@ frame_benchmarking::benchmarks! { set_state { // Create a pool let min_create_bond = Pools::::depositor_min_bond(); - let (depositor, pool_account) = create_pool_account::(0, min_create_bond); + let (depositor, pool_account) = create_pool_account::(0, min_create_bond, None); BondedPools::::mutate(&1, |maybe_pool| { // Force the pool into an invalid state maybe_pool.as_mut().map(|mut pool| pool.points = min_create_bond * 10u32.into()); @@ -615,7 +613,7 @@ frame_benchmarking::benchmarks! { let n in 1 .. ::MaxMetadataLen::get(); // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Create metadata of the max possible size let metadata: Vec = (0..n).map(|_| 42).collect(); @@ -644,7 +642,7 @@ frame_benchmarking::benchmarks! { update_roles { let first_id = pallet_nomination_pools::LastPoolId::::get() + 1; - let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (root, _) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED); }:_( RuntimeOrigin::Signed(root.clone()), @@ -666,7 +664,7 @@ frame_benchmarking::benchmarks! { chill { // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // Nominate with the pool. let validators: Vec<_> = (0..T::MaxNominations::get()) @@ -683,8 +681,8 @@ frame_benchmarking::benchmarks! { } set_commission { - // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + // Create a pool - do not set a commission yet. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // set a max commission Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); // set a commission throttle @@ -709,10 +707,8 @@ frame_benchmarking::benchmarks! { } set_commission_max { - // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); - // Set a commission that will update when max commission is set. - Pools::::set_commission(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50), Some(depositor.clone())).unwrap(); + // Create a pool, setting a commission that will update when max commission is set. + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50))); }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50)) verify { assert_eq!( @@ -725,7 +721,7 @@ frame_benchmarking::benchmarks! { set_commission_throttle { // Create a pool - let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into()); + let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionThrottlePrefs { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), From c892dd094a8416c3da085a4b5ceef70ea378c44e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 12:21:26 +0000 Subject: [PATCH 094/221] derive DefaultNoBound --- frame/nomination-pools/src/lib.rs | 25 +++++++++++++------------ frame/nomination-pools/src/tests.rs | 16 ++++++++-------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2f8f2dd3d8b0c..865e4abbada93 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -609,6 +609,7 @@ pub struct Commission { pub throttle: Option>, } +#[derive(DefaultNoBound)] impl Default for Commission { fn default() -> Self { Self { current: None, max: None, throttle: None } @@ -647,10 +648,10 @@ impl Commission { } } - /// Set the pool's commission. - /// Update commission accordingly based on `commission` and `payee`. - /// If throttle is present, record the current block as the previously updated commission. - /// If the supplied commission is zero, `None` will be inserted and `payee` will be ignored. + /// Set the pool's commission. Update commission accordingly based on + /// `commission` and `payee`. If throttle is present, record the current block + /// as the previously updated commission. If the supplied commission is zero, + /// `None` will be inserted and `payee` will be ignored. fn maybe_update_current( &mut self, commission: &Perbill, @@ -672,12 +673,12 @@ impl Commission { Ok(()) } - /// Set the pool's maximum commission. - /// The pool's maximum commission can be set to any value initially, and only smaller values - /// thereafter. If larger values are attempted, this function will return a dispatch error. + /// Set the pool's maximum commission. The pool's maximum commission can be + /// set to any value initially, and only smaller values thereafter. If larger + /// values are attempted, this function will return a dispatch error. /// - /// If `current.0` is larger than an updated max commission value, then `current.0` will also be - /// updated to the new maximum. + /// If `current.0` is larger than an updated max commission value, then + /// `current.0` will also be updated to the new maximum. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { @@ -691,9 +692,9 @@ impl Commission { Ok(()) } - /// Set the pool's commission throttle settings. - /// Once a throttle configuration has been set, only more restrictive values can be set - /// thereafter. These restrictions translate to increased `min_delay` values and decreased + /// Set the pool's commission throttle settings. Once a throttle configuration + /// has been set, only more restrictive values can be set thereafter. These + /// restrictions translate to increased `min_delay` values and decreased /// `max_increase` values. fn maybe_update_throttle( &mut self, diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index f41bd6ef13ea8..e83cc77e69f9d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -3,17 +3,17 @@ // Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. use super::*; use crate::{mock::*, Event}; From 1a90b84559ce64e187d738ea4ed53875fca20618 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 12:24:59 +0000 Subject: [PATCH 095/221] format comments better --- frame/nomination-pools/src/lib.rs | 83 +++++++++++++++++-------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 865e4abbada93..32b0a3c9044c4 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -618,6 +618,7 @@ impl Default for Commission { impl Commission { /// Get the current commission percentage of this pool. + /// /// Returns zero if commission has not yet been configured. fn commission_or_zero(&self) -> Perbill { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) @@ -648,10 +649,12 @@ impl Commission { } } - /// Set the pool's commission. Update commission accordingly based on - /// `commission` and `payee`. If throttle is present, record the current block - /// as the previously updated commission. If the supplied commission is zero, - /// `None` will be inserted and `payee` will be ignored. + /// Set the pool's commission. + /// + /// Update commission accordingly based on `commission` and `payee`. If + /// throttle is present, record the current block as the previously updated + /// commission. If the supplied commission is zero, `None` will be inserted + /// and `payee` will be ignored. fn maybe_update_current( &mut self, commission: &Perbill, @@ -673,9 +676,11 @@ impl Commission { Ok(()) } - /// Set the pool's maximum commission. The pool's maximum commission can be - /// set to any value initially, and only smaller values thereafter. If larger - /// values are attempted, this function will return a dispatch error. + /// Set the pool's maximum commission. + /// + /// The pool's maximum commission can be set to any value initially, and only + /// smaller values thereafter. If larger values are attempted, this function + /// will return a dispatch error. /// /// If `current.0` is larger than an updated max commission value, then /// `current.0` will also be updated to the new maximum. @@ -692,10 +697,11 @@ impl Commission { Ok(()) } - /// Set the pool's commission throttle settings. Once a throttle configuration - /// has been set, only more restrictive values can be set thereafter. These - /// restrictions translate to increased `min_delay` values and decreased - /// `max_increase` values. + /// Set the pool's commission throttle settings. + /// + /// Once a throttle configuration has been set, only more restrictive values + /// can be set thereafter. These restrictions translate to increased + /// `min_delay` values and decreased `max_increase` values. fn maybe_update_throttle( &mut self, change_rate: CommissionThrottlePrefs, @@ -713,8 +719,9 @@ impl Commission { } /// Gets the current commission (if any) and payee to be paid. - /// A zero commission along with a `None` payee is returned in the event a commission has not - /// been configured to the pool. + /// + /// A zero commission along with a `None` payee is returned in the event a + /// commission has not been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -749,11 +756,13 @@ impl CommissionThrottle { } /// Pool commission throttle preferences. -/// A commission throttle consists of 2 values; (1) the maximum allowed commission change, and (2) -/// the minimum amount of blocks that must elapse before commission updates are allowed again. /// -/// Throttle prefs need to be passed and configured together. This struct is used in -/// the `set_commission_throttle` call as well as in CommissionThrottle. +/// A commission throttle consists of 2 values; (1) the maximum allowed +/// commission change, and (2) the minimum amount of blocks that must elapse +/// before commission updates are allowed again. +/// +/// Throttle prefs need to be passed and configured together. This struct is +/// used in the `set_commission_throttle` call as well as in CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionThrottlePrefs { /// The maximum amount the commission can be updated by. @@ -781,8 +790,8 @@ pub struct BondedPoolInner { /// A wrapper for bonded pools, with utility functions. /// -/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account + id of the pool, -/// for easier access. +/// The main purpose of this is to wrap a [`BondedPoolInner`], with the account +/// + id of the pool, for easier access. #[derive(RuntimeDebugNoBound)] #[cfg_attr(feature = "std", derive(Clone, PartialEq))] pub struct BondedPool { @@ -2202,15 +2211,15 @@ pub mod pallet { /// Set the commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. - /// If the pool has a max commission set, the commission supplied must be less or - /// equal to that value. + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If the pool has a max commission set, the commission supplied + /// must be less or equal to that value. /// - /// If the max commission has not yet been set, then the commission range is not - /// bounded. - /// A `payee` must be provided if commission has not yet been set (still `None`). Once - /// commission has been set, the `payee` can be omitted in further calls. If a `payee` - /// update is desired, the commission must still be passed into the call. + /// If the max commission has not yet been set, then the commission range is + /// not bounded. A `payee` must be provided if commission has not yet been + /// set (still `None`). Once commission has been set, the `payee` can be + /// omitted in further calls. If a `payee` update is desired, the commission + /// must still be passed into the call. #[pallet::weight(T::WeightInfo::set_commission())] pub fn set_commission( origin: OriginFor, @@ -2240,12 +2249,13 @@ pub mod pallet { /// Set the maximum commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. - /// If a maximum commission already exists prior to this call, then the updated - /// max commission must be lower, otherwise this call will fail. + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If a maximum commission already exists prior to this call, + /// then the updated max commission must be lower, otherwise this call will + /// fail. /// - /// This call also updates the pool's current commission to the new maximum if the - /// current commission is higher than the maximum supplied. + /// This call also updates the pool's current commission to the new maximum + /// if the current commission is higher than the maximum supplied. #[pallet::weight(T::WeightInfo::set_commission_max())] pub fn set_commission_max( origin: OriginFor, @@ -2264,11 +2274,12 @@ pub mod pallet { /// Set the commission throttle for a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. - /// If a throttle is already present, this call will only succeed if a more restrictive - /// throttle configuration is given. + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If a throttle is already present, this call will only succeed + /// if a more restrictive throttle configuration is given. /// - /// If a throttle configuration does not yet exist, the provided values are set. + /// If a throttle configuration does not yet exist, the provided values are + /// set. #[pallet::weight(T::WeightInfo::set_commission_throttle())] pub fn set_commission_throttle( origin: OriginFor, From 8286109851c8e063a3bc517cc8a4120b0acb1c3a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 12:27:47 +0000 Subject: [PATCH 096/221] fixes --- frame/nomination-pools/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 32b0a3c9044c4..524227fd9faf5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -609,7 +609,6 @@ pub struct Commission { pub throttle: Option>, } -#[derive(DefaultNoBound)] impl Default for Commission { fn default() -> Self { Self { current: None, max: None, throttle: None } @@ -632,8 +631,7 @@ impl Commission { /// /// Throttling is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { - let throttle = self.throttle.as_ref().map(|t| t).or(None); - if let Some(t) = throttle { + if let Some(t) = self.throttle.as_ref() { // check for `min_delay` throttling if t.previous_set_at.map_or(false, |p| { >::block_number().saturating_sub(p) < From a358c1dc07d44b05a91062295c591b8e77315cae Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 12:35:17 +0000 Subject: [PATCH 097/221] DefaultNoBound --- frame/nomination-pools/src/lib.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 524227fd9faf5..6316023eece43 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -595,7 +595,9 @@ pub struct PoolRoles { /// commission can change in each update, and how often updates can take place. /// If a `throttle` is set before a commission `current` is set, the initial commission `current` /// value will not be subject to throttling. Subsequent commission updates will be. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] +#[derive( + Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, +)] #[codec(mel_bound(T: Config))] #[scale_info(skip_type_params(T))] pub struct Commission { @@ -609,12 +611,6 @@ pub struct Commission { pub throttle: Option>, } -impl Default for Commission { - fn default() -> Self { - Self { current: None, max: None, throttle: None } - } -} - impl Commission { /// Get the current commission percentage of this pool. /// From be296039a6626d1604e6ee22914f961c1e218c28 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 13:32:28 +0000 Subject: [PATCH 098/221] migration note --- frame/nomination-pools/src/migration.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 7a4efe027edb2..3cb3e59e90c57 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -52,10 +52,12 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { + // TODO: I don't think this is correct, commission shouldn't be included in + // this migration. BondedPoolInner { + points: self.points, commission: Commission::default(), member_counter: self.member_counter, - points: self.points, state: self.state, roles: self.roles.migrate_to_v1(), } From bc91daf6196e0bf9436b6f4c70c9a58f883c5bfd Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 14 Nov 2022 13:32:43 +0000 Subject: [PATCH 099/221] feedback fixes --- frame/nomination-pools/src/lib.rs | 50 ++++++++++++++----------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6316023eece43..e0296d775c4b5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -636,11 +636,10 @@ impl Commission { return true } // check for `max_increase` throttling - (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase && + return (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase && self.current.is_some() - } else { - return false } + false } /// Set the pool's commission. @@ -655,10 +654,7 @@ impl Commission { payee: T::AccountId, ) -> DispatchResult { ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); - ensure!( - self.max.map(|m| commission <= &m).unwrap_or(true), - Error::::CommissionExceedsMaximum - ); + ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); self.current = Some((*commission, payee)); @@ -700,15 +696,16 @@ impl Commission { &mut self, change_rate: CommissionThrottlePrefs, ) -> DispatchResult { - if let Some(t) = &self.throttle { - ensure!(!t.restricted(&change_rate), Error::::CommissionThrottleNotAllowed); - } - self.throttle = self - .throttle - .as_ref() - .map_or(Some(CommissionThrottle { change_rate, previous_set_at: None }), |t| { - Some(CommissionThrottle { change_rate, ..*t }) - }); + ensure!( + &self.throttle.as_ref().map_or(true, |t| t.less_restrictive(&change_rate)), + Error::::CommissionThrottleNotAllowed + ); + + self.throttle = Some(CommissionThrottle { + change_rate, + previous_set_at: self.throttle.as_ref().map_or(None, |t| t.previous_set_at), + }); + Ok(()) } @@ -739,9 +736,10 @@ pub struct CommissionThrottle { } impl CommissionThrottle { - fn restricted(&self, change_rate: &CommissionThrottlePrefs) -> bool { - change_rate.max_increase > self.change_rate.max_increase || - change_rate.min_delay < self.change_rate.min_delay + /// Returns `true` if `change_rate` is less restrictive than `self`. + fn less_restrictive(&self, change_rate: &CommissionThrottlePrefs) -> bool { + change_rate.max_increase <= self.change_rate.max_increase && + change_rate.min_delay >= self.change_rate.min_delay } fn register_change(&mut self, now: T::BlockNumber) { @@ -759,7 +757,7 @@ impl CommissionThrottle { /// used in the `set_commission_throttle` call as well as in CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionThrottlePrefs { - /// The maximum amount the commission can be updated by. + /// The maximum amount the commission can be updated by per `min_delay` period. pub max_increase: Perbill, /// How often an update can take place. pub min_delay: BlockNumber, @@ -2608,16 +2606,14 @@ impl Pallet { reward_pool.register_claimed_reward(pending_rewards); // Gets the commission percentage and payee to be paid if commission has - // been set. Otherwise, a zero commission and payee of `None` is returned. - let commission_current = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); + // been set. Otherwise, `None` is returned. + let maybe_commission = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); - if let Some((pool_commission, payee)) = commission_current { + if let Some((pool_commission, payee)) = maybe_commission { // Deduct any outstanding commission from the reward being claimed. pending_rewards = pending_rewards.saturating_sub(*pool_commission); - // If a non-zero commission has been applied to the pool, deduct the share from - // `reward_pool` and send the amount to the commission `payee`. - // Defensive: The commission payee is also checked for existence. + // Send any non-zero `pool_commission` to the commission `payee`. if pool_commission != &BalanceOf::::zero() { T::Currency::transfer( &bonded_pool.reward_account(), @@ -2642,7 +2638,7 @@ impl Pallet { member: member_account.clone(), pool_id: member.pool_id, payout: pending_rewards, - commission: commission_current.as_ref().map(|(c, _)| *c).unwrap_or(Zero::zero()), + commission: maybe_commission.as_ref().map(|(c, _)| *c).unwrap_or(Zero::zero()), }); Ok(pending_rewards) From 37caa0325ba45ba4a77b43780ec4cb6f82a0b4f5 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Dec 2022 14:34:56 +0000 Subject: [PATCH 100/221] Update frame/nomination-pools/src/lib.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gonçalo Pestana --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e0296d775c4b5..3a6f600c13094 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -606,7 +606,7 @@ pub struct Commission { /// Optional maximum commission that can be set by the pool `root`. Once set, this value /// can only be updated to a decreased value. pub max: Option, - /// Optional configiration around how often commission can be updated, and when the last + /// Optional configuration around how often commission can be updated, and when the last /// commission update took place. pub throttle: Option>, } From ba0a7575e66711fa4774a1e448ae88e04b4436dd Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Dec 2022 21:55:03 +0700 Subject: [PATCH 101/221] + PoolCommissionThrottleUpdated event --- frame/nomination-pools/src/lib.rs | 12 ++++++++++-- frame/nomination-pools/src/tests.rs | 7 +++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index e0296d775c4b5..9320df89b90f1 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1476,8 +1476,9 @@ pub mod pallet { pub type BondedPools = CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner>; - /// Reward pools. This is where there rewards for each pool accumulate. When a members payout - /// is claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. + /// Reward pools. This is where there rewards for each pool accumulate. When a + /// members payout is claimed, the balance comes out fo the reward pool. Keyed + /// by the bonded pools account. #[pallet::storage] pub type RewardPools = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool>; @@ -1610,6 +1611,11 @@ pub mod pallet { PoolCommissionUpdated { pool_id: PoolId, commission: Perbill, payee: T::AccountId }, /// A pool's maximum commission setting has been changed. PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, + /// A pool's commission throttle has been changed. + PoolCommissionThrottleUpdated { + pool_id: PoolId, + prefs: CommissionThrottlePrefs, + }, } #[pallet::error] @@ -2284,6 +2290,8 @@ pub mod pallet { bonded_pool.commission.maybe_update_throttle(prefs)?; bonded_pool.put(); + + Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, prefs }); Ok(()) } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index e83cc77e69f9d..ff2dfb83de76c 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -447,6 +447,13 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionThrottleUpdated { + pool_id: 1, + prefs: CommissionThrottlePrefs { + max_increase: Perbill::from_percent(1), + min_delay: 2 + } + }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(5), From d9e01ae956e4399fda575479421ffa0033c187de Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Dec 2022 22:01:19 +0700 Subject: [PATCH 102/221] amend comment --- frame/nomination-pools/src/migration.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 3cb3e59e90c57..64007a8b9fa2d 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -477,7 +477,8 @@ pub mod v4 { } } - /// This migration removes stale bonded-pool metadata, if any. + /// This migration adds a `commission` field to every `BondedPoolInner`, if + /// any. pub struct MigrateToV4(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for MigrateToV4 { fn on_runtime_upgrade() -> Weight { From 1f1e6274746e9c11412abdeab28cc4465e7298e7 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 7 Dec 2022 22:30:07 +0700 Subject: [PATCH 103/221] rm leaky abstraction --- frame/nomination-pools/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b45991cb2cc31..faea52aaf4660 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -636,8 +636,7 @@ impl Commission { return true } // check for `max_increase` throttling - return (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase && - self.current.is_some() + return (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase } false } @@ -653,7 +652,7 @@ impl Commission { commission: &Perbill, payee: T::AccountId, ) -> DispatchResult { - ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!(!(self.current.is_some() && self.throttling(&commission)), Error::::CommissionChangeThrottled); ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); self.current = Some((*commission, payee)); From 91be8278af2f658e945580aac32477ec8af92ce6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 8 Dec 2022 03:35:47 +0700 Subject: [PATCH 104/221] allow update payee only --- .../nomination-pools/benchmarking/src/lib.rs | 4 +- frame/nomination-pools/src/lib.rs | 17 +- frame/nomination-pools/src/tests.rs | 69 +++- frame/nomination-pools/src/weights.rs | 348 +++++++++--------- 4 files changed, 228 insertions(+), 210 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 78eceb27d43e9..23f8159a87ea9 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -94,7 +94,7 @@ fn create_pool_account( Pools::::set_commission( RuntimeOrigin::Signed(pool_creator.clone()).into(), pool_id, - c, + Some(c), Some(pool_creator.clone()), ) .expect("pool commission has been set"); @@ -691,7 +691,7 @@ frame_benchmarking::benchmarks! { min_delay: 0u32.into(), }).unwrap(); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(40), Some(depositor.clone())) + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(Perbill::from_percent(40)), Some(depositor.clone())) verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(40), depositor)), diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index faea52aaf4660..adf1b57977d28 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -652,7 +652,10 @@ impl Commission { commission: &Perbill, payee: T::AccountId, ) -> DispatchResult { - ensure!(!(self.current.is_some() && self.throttling(&commission)), Error::::CommissionChangeThrottled); + ensure!( + !(self.current.is_some() && self.throttling(&commission)), + Error::::CommissionChangeThrottled + ); ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); self.current = Some((*commission, payee)); @@ -1670,6 +1673,8 @@ pub mod pallet { Defensive(DefensiveError), /// Partial unbonding now allowed permissionlessly. PartialUnbondNotAllowedPermissionlessly, + /// No commission has been set. + NoCommissionSet, /// No account has been set to receive commission. NoCommissionPayeeSet, /// The pool's max commission cannot be set higher than the existing value. @@ -2221,24 +2226,28 @@ pub mod pallet { pub fn set_commission( origin: OriginFor, pool_id: PoolId, - new_commission: Perbill, + commission: Option, payee: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + let final_commission = commission + .or(bonded_pool.commission.current.as_ref().map(|(c, _)| c).cloned()) + .ok_or(Error::::NoCommissionSet)?; + let final_payee = payee .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; bonded_pool .commission - .maybe_update_current(&new_commission, final_payee.clone())?; + .maybe_update_current(&final_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, - commission: new_commission, + commission: final_commission, payee: final_payee, }); Ok(()) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index ff2dfb83de76c..1056c4e76dcae 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -251,13 +251,28 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(50), + Some(Perbill::from_percent(50)), Some(900) )); let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); + + // Set a new payee only + assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None, Some(901))); + let commission = BondedPool::::get(1).unwrap().commission; + assert_eq!(commission.current, Some((Perbill::from_percent(50), 901))); + + // Set a new commission only + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(25)), + None + )); + let commission = BondedPool::::get(1).unwrap().commission; + assert_eq!(commission.current, Some((Perbill::from_percent(25), 901))); + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), @@ -268,7 +283,17 @@ mod bonded_pool { pool_id: 1, commission: Perbill::from_percent(50), payee: 900 - } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(50), + payee: 901 + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(25), + payee: 901 + }, ] ); }); @@ -282,7 +307,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 9999, - Perbill::from_percent(1), + Some(Perbill::from_percent(1)), Some(900) ), Error::::PoolNotFound @@ -292,22 +317,26 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(1), 1, - Perbill::from_percent(5), + Some(Perbill::from_percent(5)), Some(900) ), Error::::DoesNotHavePermission ); - // No payee has been provided, and none is present. + // No payee has been set, and `None` is provided. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), + Some(Perbill::from_percent(5)), None ), Error::::NoCommissionPayeeSet ); - + // No commission has been set, and `None` is provided. + assert_noop!( + Pools::set_commission(RuntimeOrigin::signed(900), 1, None, Some(900)), + Error::::NoCommissionSet + ); // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -337,7 +366,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(5), + Some(Perbill::from_percent(5)), Some(900) )); @@ -346,7 +375,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(10), + Some(Perbill::from_percent(10)), Some(900) ), Error::::CommissionChangeThrottled @@ -360,7 +389,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(6), + Some(Perbill::from_percent(6)), Some(900) )); assert_eq!( @@ -385,7 +414,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(2), + Some(Perbill::from_percent(2)), Some(900) ), Error::::CommissionChangeThrottled @@ -399,7 +428,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(2), + Some(Perbill::from_percent(2)), None )); @@ -412,7 +441,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(4), + Some(Perbill::from_percent(4)), None, ), Error::::CommissionChangeThrottled @@ -435,7 +464,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(3), + Some(Perbill::from_percent(3)), None, ), Error::::CommissionExceedsMaximum @@ -522,7 +551,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(75), + Some(Perbill::from_percent(75)), Some(900), )); assert_ok!(Pools::set_commission_max( @@ -592,7 +621,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(25), + Some(Perbill::from_percent(25)), Some(900) )); @@ -603,7 +632,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(26), + Some(Perbill::from_percent(26)), Some(900) ), Error::::CommissionChangeThrottled @@ -617,7 +646,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(51), + Some(Perbill::from_percent(51)), Some(900) ), Error::::CommissionChangeThrottled @@ -1397,7 +1426,7 @@ mod claim_payout { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, - Perbill::from_percent(33), + Some(Perbill::from_percent(33)), Some(2) )); diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 344cfac72eb5d..9c57ccfe55308 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -1,25 +1,9 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `Rosss-MacBook-Pro-2.local`, CPU: `` +//! DATE: 2022-12-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `192.168.1.33`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -90,10 +74,10 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(151_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(17 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Minimum execution time: 139_000 nanoseconds. + Weight::from_ref_time(149_000_000) + .saturating_add(T::DbWeight::get().reads(17)) + .saturating_add(T::DbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -105,10 +89,10 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 140_000 nanoseconds. - Weight::from_ref_time(142_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(12 as u64)) + // Minimum execution time: 224_000 nanoseconds. + Weight::from_ref_time(239_000_000) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -120,20 +104,20 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 170_000 nanoseconds. - Weight::from_ref_time(175_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(14 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(174_000_000) + .saturating_add(T::DbWeight::get().reads(14)) + .saturating_add(T::DbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(55_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(4 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(53_000_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -150,10 +134,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(147_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(18 as u64)) - .saturating_add(T::DbWeight::get().writes(13 as u64)) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(138_000_000) + .saturating_add(T::DbWeight::get().reads(18)) + .saturating_add(T::DbWeight::get().writes(13)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -162,12 +146,12 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(54_406_927 as u64) - // Standard Error: 1_104 - .saturating_add(Weight::from_ref_time(14_722 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(5 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(53_679_159) + // Standard Error: 2_231 + .saturating_add(Weight::from_ref_time(22_068).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -180,12 +164,12 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(99_241_507 as u64) - // Standard Error: 2_139 - .saturating_add(Weight::from_ref_time(1_948 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(7 as u64)) + // Minimum execution time: 92_000 nanoseconds. + Weight::from_ref_time(94_427_540) + // Standard Error: 2_084 + .saturating_add(Weight::from_ref_time(24_363).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(7)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -208,13 +192,11 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(152_086_647 as u64) - // Standard Error: 2_975 - .saturating_add(Weight::from_ref_time(633 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(20 as u64)) - .saturating_add(T::DbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(146_560_808) + .saturating_add(T::DbWeight::get().reads(20)) + .saturating_add(T::DbWeight::get().writes(17)) } // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) @@ -238,10 +220,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 130_000 nanoseconds. - Weight::from_ref_time(132_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(21 as u64)) - .saturating_add(T::DbWeight::get().writes(15 as u64)) + // Minimum execution time: 126_000 nanoseconds. + Weight::from_ref_time(127_000_000) + .saturating_add(T::DbWeight::get().reads(21)) + .saturating_add(T::DbWeight::get().writes(15)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -257,34 +239,34 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_146_514 as u64) - // Standard Error: 5_979 - .saturating_add(Weight::from_ref_time(1_361_282 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(12 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 55_000 nanoseconds. + Weight::from_ref_time(55_544_997) + // Standard Error: 6_821 + .saturating_add(Weight::from_ref_time(1_349_501).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(37_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 33_000 nanoseconds. + Weight::from_ref_time(34_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_098_252 as u64) - // Standard Error: 231 - .saturating_add(Weight::from_ref_time(654 as u64).saturating_mul(n as u64)) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(12_856_680) + // Standard Error: 195 + .saturating_add(Weight::from_ref_time(463).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) } // Storage: NominationPools MinJoinBond (r:0 w:1) // Storage: NominationPools MaxPoolMembers (r:0 w:1) @@ -292,16 +274,16 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000 as u64) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(6_000_000) + .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -313,31 +295,31 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(9 as u64)) - .saturating_add(T::DbWeight::get().writes(5 as u64)) + // Minimum execution time: 56_000 nanoseconds. + Weight::from_ref_time(57_000_000) + .saturating_add(T::DbWeight::get().reads(9)) + .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(24_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 20_000 nanoseconds. + Weight::from_ref_time(21_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_000_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Minimum execution time: 20_000 nanoseconds. + Weight::from_ref_time(21_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } } @@ -357,10 +339,10 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(151_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(17 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Minimum execution time: 139_000 nanoseconds. + Weight::from_ref_time(149_000_000) + .saturating_add(RocksDbWeight::get().reads(17)) + .saturating_add(RocksDbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -372,10 +354,10 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 140_000 nanoseconds. - Weight::from_ref_time(142_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(12 as u64)) + // Minimum execution time: 224_000 nanoseconds. + Weight::from_ref_time(239_000_000) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(12)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -387,20 +369,20 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 170_000 nanoseconds. - Weight::from_ref_time(175_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(14 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(174_000_000) + .saturating_add(RocksDbWeight::get().reads(14)) + .saturating_add(RocksDbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(55_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(4 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(53_000_000) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) @@ -417,10 +399,10 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(147_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(18 as u64)) - .saturating_add(RocksDbWeight::get().writes(13 as u64)) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(138_000_000) + .saturating_add(RocksDbWeight::get().reads(18)) + .saturating_add(RocksDbWeight::get().writes(13)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -429,12 +411,12 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(54_406_927 as u64) - // Standard Error: 1_104 - .saturating_add(Weight::from_ref_time(14_722 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(5 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(53_679_159) + // Standard Error: 2_231 + .saturating_add(Weight::from_ref_time(22_068).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -447,12 +429,12 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(99_241_507 as u64) - // Standard Error: 2_139 - .saturating_add(Weight::from_ref_time(1_948 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(7 as u64)) + // Minimum execution time: 92_000 nanoseconds. + Weight::from_ref_time(94_427_540) + // Standard Error: 2_084 + .saturating_add(Weight::from_ref_time(24_363).saturating_mul(s.into())) + .saturating_add(RocksDbWeight::get().reads(9)) + .saturating_add(RocksDbWeight::get().writes(7)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: Staking CurrentEra (r:1 w:0) @@ -475,13 +457,11 @@ impl WeightInfo for () { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(152_086_647 as u64) - // Standard Error: 2_975 - .saturating_add(Weight::from_ref_time(633 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(20 as u64)) - .saturating_add(RocksDbWeight::get().writes(17 as u64)) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(146_560_808) + .saturating_add(RocksDbWeight::get().reads(20)) + .saturating_add(RocksDbWeight::get().writes(17)) } // Storage: NominationPools LastPoolId (r:1 w:1) // Storage: Staking MinNominatorBond (r:1 w:0) @@ -505,10 +485,10 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 130_000 nanoseconds. - Weight::from_ref_time(132_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(21 as u64)) - .saturating_add(RocksDbWeight::get().writes(15 as u64)) + // Minimum execution time: 126_000 nanoseconds. + Weight::from_ref_time(127_000_000) + .saturating_add(RocksDbWeight::get().reads(21)) + .saturating_add(RocksDbWeight::get().writes(15)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -524,34 +504,34 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_146_514 as u64) - // Standard Error: 5_979 - .saturating_add(Weight::from_ref_time(1_361_282 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(12 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 55_000 nanoseconds. + Weight::from_ref_time(55_544_997) + // Standard Error: 6_821 + .saturating_add(Weight::from_ref_time(1_349_501).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(12)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(37_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 33_000 nanoseconds. + Weight::from_ref_time(34_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_098_252 as u64) - // Standard Error: 231 - .saturating_add(Weight::from_ref_time(654 as u64).saturating_mul(n as u64)) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(12_856_680) + // Standard Error: 195 + .saturating_add(Weight::from_ref_time(463).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(2)) } // Storage: NominationPools MinJoinBond (r:0 w:1) // Storage: NominationPools MaxPoolMembers (r:0 w:1) @@ -559,16 +539,16 @@ impl WeightInfo for () { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000 as u64) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(6_000_000) + .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) @@ -580,30 +560,30 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(9 as u64)) - .saturating_add(RocksDbWeight::get().writes(5 as u64)) + // Minimum execution time: 56_000 nanoseconds. + Weight::from_ref_time(57_000_000) + .saturating_add(RocksDbWeight::get().reads(9)) + .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(24_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 20_000 nanoseconds. + Weight::from_ref_time(21_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_000_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Minimum execution time: 20_000 nanoseconds. + Weight::from_ref_time(21_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } } From 4e6982756eba1dccc6fac1547d33edacd3608f0a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 8 Dec 2022 03:47:15 +0700 Subject: [PATCH 105/221] change fn scope --- frame/nomination-pools/src/mock.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index f79d78d88c4a8..e0398f0904f31 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -354,14 +354,14 @@ parameter_types! { storage BalancesEvents: u32 = 0; } -/// Helper to tun a specified amount of blocks. -pub(crate) fn run_blocks(n: u64) { +/// Helper to run a specified amount of blocks. +pub fn run_blocks(n: u64) { let current_block = System::block_number(); run_to_block(n + current_block); } /// Helper to run to a specific block. -pub(crate) fn run_to_block(n: u64) { +pub fn run_to_block(n: u64) { let current_block = System::block_number(); assert!(n > current_block); while System::block_number() < n { From e820eeaf0ca179864d16cf8feaa4509e3c540a79 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 9 Dec 2022 02:46:58 +0700 Subject: [PATCH 106/221] fix set max commission logic --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index adf1b57977d28..6f460ed092579 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -682,10 +682,10 @@ impl Commission { return Err(Error::::MaxCommissionRestricted.into()) } *old = new_max; - let _ = self.current.as_mut().map(|(x, _)| *x = (*x).min(new_max)); } else { self.max = Some(new_max) }; + let _ = self.current.as_mut().map(|(x, _)| *x = (*x).min(new_max)); Ok(()) } From 5752dd97054f358867d7a801d25661838069e4ab Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 9 Dec 2022 02:47:13 +0700 Subject: [PATCH 107/221] + max_commission_after_current_commission_works --- frame/nomination-pools/src/tests.rs | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 1056c4e76dcae..a3f23d36cd9bb 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -592,6 +592,37 @@ mod bonded_pool { }); } + #[test] + fn max_commission_after_current_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // set pool commission to 50% first. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(50)), + Some(900), + )); + + // now set the max commission to something less than the current + // commission. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(25) + )); + + // the current commission should now be 25%. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(25), 900)), + max: Some(Perbill::from_percent(25)), + throttle: None + } + ); + }) + } + #[test] fn do_not_throttle_initial_commission_set() { ExtBuilder::default().build_and_execute(|| { From b651cd4e68d72750da8fde3c1c876bf6fd271375 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 10 Dec 2022 05:06:39 +0700 Subject: [PATCH 108/221] rm do_not_throttle_initial_commission_set --- frame/nomination-pools/src/lib.rs | 5 +- frame/nomination-pools/src/tests.rs | 91 +++++------------------------ 2 files changed, 16 insertions(+), 80 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6f460ed092579..52539e8b98cb9 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -652,10 +652,7 @@ impl Commission { commission: &Perbill, payee: T::AccountId, ) -> DispatchResult { - ensure!( - !(self.current.is_some() && self.throttling(&commission)), - Error::::CommissionChangeThrottled - ); + ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); self.current = Some((*commission, payee)); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a3f23d36cd9bb..d420d3dde23a5 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -337,6 +337,15 @@ mod bonded_pool { Pools::set_commission(RuntimeOrigin::signed(900), 1, None, Some(900)), Error::::NoCommissionSet ); + + // Set the initial commission to 5%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(5)), + Some(900) + )); + // Throttle test. We will throttle commission to be a +1% commission increase every 2 // blocks. assert_ok!(Pools::set_commission_throttle( @@ -350,7 +359,7 @@ mod bonded_pool { assert_eq!( BondedPool::::get(1).unwrap().commission, Commission { - current: None, + current: Some((Perbill::from_percent(5), 900)), max: None, throttle: Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { @@ -362,14 +371,6 @@ mod bonded_pool { } ); - // Set the initial commission to 5%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some(Perbill::from_percent(5)), - Some(900) - )); - // We now try to increase commission to 10% (5% increase): this should be throttled. assert_noop!( Pools::set_commission( @@ -476,6 +477,11 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(5), + payee: 900 + }, Event::PoolCommissionThrottleUpdated { pool_id: 1, prefs: CommissionThrottlePrefs { @@ -483,11 +489,6 @@ mod bonded_pool { min_delay: 2 } }, - Event::PoolCommissionUpdated { - pool_id: 1, - commission: Perbill::from_percent(5), - payee: 900 - }, Event::PoolCommissionUpdated { pool_id: 1, commission: Perbill::from_percent(6), @@ -623,68 +624,6 @@ mod bonded_pool { }) } - #[test] - fn do_not_throttle_initial_commission_set() { - ExtBuilder::default().build_and_execute(|| { - // Set a commission throttle for pool 1 - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionThrottlePrefs { - max_increase: Perbill::from_percent(5), - min_delay: 5_u64 - } - )); - assert_eq!( - BondedPools::::get(1).unwrap().commission.throttle, - Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { - max_increase: Perbill::from_percent(5), - min_delay: 5_u64 - }, - previous_set_at: None - }) - ); - - // add commission that is beyond the throttle boundaries - // setting on same block as throttle update (`min_delay` of 5 blocks should not fail tx) - // `None` -> 50% commission ( `max_increase` of 5% should not fail tx) - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some(Perbill::from_percent(25)), - Some(900) - )); - - // now throttle should take effect. - - // attempt 1% commission increase on same block - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some(Perbill::from_percent(26)), - Some(900) - ), - Error::::CommissionChangeThrottled - ); - - // move to a block beyond `min_delay` - run_blocks(5); - - // attempt 26% commission increase - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some(Perbill::from_percent(51)), - Some(900) - ), - Error::::CommissionChangeThrottled - ); - }) - } - #[test] fn set_commission_throttle_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { From 5f50791c69ab3d6a2b6b52fb8d0bc56c4132d07d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 10 Dec 2022 05:44:58 +0700 Subject: [PATCH 109/221] fix benchmark --- .../nomination-pools/benchmarking/src/lib.rs | 4 +- frame/nomination-pools/src/weights.rs | 186 +++++++++--------- 2 files changed, 95 insertions(+), 95 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 23f8159a87ea9..ae0b185556287 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -691,10 +691,10 @@ frame_benchmarking::benchmarks! { min_delay: 0u32.into(), }).unwrap(); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(Perbill::from_percent(40)), Some(depositor.clone())) + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(Perbill::from_percent(20)), Some(depositor.clone())) verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { - current: Some((Perbill::from_percent(40), depositor)), + current: Some((Perbill::from_percent(20), depositor)), max: Some(Perbill::from_percent(50)), throttle: Some(CommissionThrottle { change_rate: CommissionThrottlePrefs { diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 9c57ccfe55308..f492148d0c914 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `192.168.1.33`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(149_000_000) + // Minimum execution time: 174_000 nanoseconds. + Weight::from_ref_time(177_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 224_000 nanoseconds. - Weight::from_ref_time(239_000_000) + // Minimum execution time: 164_000 nanoseconds. + Weight::from_ref_time(175_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(174_000_000) + // Minimum execution time: 194_000 nanoseconds. + Weight::from_ref_time(202_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(53_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(64_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 137_000 nanoseconds. - Weight::from_ref_time(138_000_000) + // Minimum execution time: 163_000 nanoseconds. + Weight::from_ref_time(171_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -146,10 +146,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 50_000 nanoseconds. - Weight::from_ref_time(53_679_159) - // Standard Error: 2_231 - .saturating_add(Weight::from_ref_time(22_068).saturating_mul(s.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(62_502_532) + // Standard Error: 1_512 + .saturating_add(Weight::from_ref_time(17_113).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -164,10 +164,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 92_000 nanoseconds. - Weight::from_ref_time(94_427_540) - // Standard Error: 2_084 - .saturating_add(Weight::from_ref_time(24_363).saturating_mul(s.into())) + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_200_554) + // Standard Error: 2_177 + .saturating_add(Weight::from_ref_time(14_609).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -192,9 +192,11 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(146_560_808) + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + // Minimum execution time: 172_000 nanoseconds. + Weight::from_ref_time(182_121_177) + // Standard Error: 3_294 + .saturating_add(Weight::from_ref_time(339).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -220,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 126_000 nanoseconds. - Weight::from_ref_time(127_000_000) + // Minimum execution time: 159_000 nanoseconds. + Weight::from_ref_time(165_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -239,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 55_000 nanoseconds. - Weight::from_ref_time(55_544_997) - // Standard Error: 6_821 - .saturating_add(Weight::from_ref_time(1_349_501).saturating_mul(n.into())) + // Minimum execution time: 66_000 nanoseconds. + Weight::from_ref_time(67_815_257) + // Standard Error: 10_581 + .saturating_add(Weight::from_ref_time(1_548_222).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -251,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 33_000 nanoseconds. - Weight::from_ref_time(34_000_000) + // Minimum execution time: 41_000 nanoseconds. + Weight::from_ref_time(43_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -260,11 +262,9 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. - fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(12_856_680) - // Standard Error: 195 - .saturating_add(Weight::from_ref_time(463).saturating_mul(n.into())) + fn set_metadata(_n: u32, ) -> Weight { + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_770_961) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -274,14 +274,14 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 6_000 nanoseconds. - Weight::from_ref_time(6_000_000) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 26_000 nanoseconds. + Weight::from_ref_time(26_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -295,29 +295,29 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 56_000 nanoseconds. - Weight::from_ref_time(57_000_000) + // Minimum execution time: 68_000 nanoseconds. + Weight::from_ref_time(71_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(27_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 20_000 nanoseconds. - Weight::from_ref_time(21_000_000) + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(27_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 20_000 nanoseconds. - Weight::from_ref_time(21_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(26_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -339,8 +339,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(149_000_000) + // Minimum execution time: 174_000 nanoseconds. + Weight::from_ref_time(177_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -354,8 +354,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 224_000 nanoseconds. - Weight::from_ref_time(239_000_000) + // Minimum execution time: 164_000 nanoseconds. + Weight::from_ref_time(175_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -369,8 +369,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(174_000_000) + // Minimum execution time: 194_000 nanoseconds. + Weight::from_ref_time(202_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -379,8 +379,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(53_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(64_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -399,8 +399,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 137_000 nanoseconds. - Weight::from_ref_time(138_000_000) + // Minimum execution time: 163_000 nanoseconds. + Weight::from_ref_time(171_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -411,10 +411,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 50_000 nanoseconds. - Weight::from_ref_time(53_679_159) - // Standard Error: 2_231 - .saturating_add(Weight::from_ref_time(22_068).saturating_mul(s.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(62_502_532) + // Standard Error: 1_512 + .saturating_add(Weight::from_ref_time(17_113).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -429,10 +429,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 92_000 nanoseconds. - Weight::from_ref_time(94_427_540) - // Standard Error: 2_084 - .saturating_add(Weight::from_ref_time(24_363).saturating_mul(s.into())) + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_200_554) + // Standard Error: 2_177 + .saturating_add(Weight::from_ref_time(14_609).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -457,9 +457,11 @@ impl WeightInfo for () { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(146_560_808) + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + // Minimum execution time: 172_000 nanoseconds. + Weight::from_ref_time(182_121_177) + // Standard Error: 3_294 + .saturating_add(Weight::from_ref_time(339).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -485,8 +487,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 126_000 nanoseconds. - Weight::from_ref_time(127_000_000) + // Minimum execution time: 159_000 nanoseconds. + Weight::from_ref_time(165_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -504,10 +506,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 55_000 nanoseconds. - Weight::from_ref_time(55_544_997) - // Standard Error: 6_821 - .saturating_add(Weight::from_ref_time(1_349_501).saturating_mul(n.into())) + // Minimum execution time: 66_000 nanoseconds. + Weight::from_ref_time(67_815_257) + // Standard Error: 10_581 + .saturating_add(Weight::from_ref_time(1_548_222).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -516,8 +518,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 33_000 nanoseconds. - Weight::from_ref_time(34_000_000) + // Minimum execution time: 41_000 nanoseconds. + Weight::from_ref_time(43_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -525,11 +527,9 @@ impl WeightInfo for () { // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. - fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(12_856_680) - // Standard Error: 195 - .saturating_add(Weight::from_ref_time(463).saturating_mul(n.into())) + fn set_metadata(_n: u32, ) -> Weight { + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_770_961) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -539,14 +539,14 @@ impl WeightInfo for () { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 6_000 nanoseconds. - Weight::from_ref_time(6_000_000) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 26_000 nanoseconds. + Weight::from_ref_time(26_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -560,29 +560,29 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 56_000 nanoseconds. - Weight::from_ref_time(57_000_000) + // Minimum execution time: 68_000 nanoseconds. + Weight::from_ref_time(71_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(27_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 20_000 nanoseconds. - Weight::from_ref_time(21_000_000) + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(27_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 20_000 nanoseconds. - Weight::from_ref_time(21_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(26_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } From aa60ebc728e636ff20504ff3be213fe9f9212fdb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 22 Dec 2022 17:39:52 +0700 Subject: [PATCH 110/221] add note --- frame/nomination-pools/src/migration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 64007a8b9fa2d..caf0e5ab828db 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -52,8 +52,8 @@ pub mod v1 { impl OldBondedPoolInner { fn migrate_to_v1(self) -> BondedPoolInner { - // TODO: I don't think this is correct, commission shouldn't be included in - // this migration. + // Note: `commission` field not introduced to `BondedPoolInner` until + // migration 4. BondedPoolInner { points: self.points, commission: Commission::default(), From 27935012c09123ab689a64410308c64a06388516 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 12:45:21 +0700 Subject: [PATCH 111/221] + call_index macro to calls --- frame/nomination-pools/src/lib.rs | 187 +++++++++++++++--------------- 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2ce01c621e6f1..d6f34e22ecbd2 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2222,98 +2222,6 @@ pub mod pallet { Ok(()) } - /// Set the commission of a pool. - /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If the pool has a max commission set, the commission supplied - /// must be less or equal to that value. - /// - /// If the max commission has not yet been set, then the commission range is - /// not bounded. A `payee` must be provided if commission has not yet been - /// set (still `None`). Once commission has been set, the `payee` can be - /// omitted in further calls. If a `payee` update is desired, the commission - /// must still be passed into the call. - #[pallet::weight(T::WeightInfo::set_commission())] - pub fn set_commission( - origin: OriginFor, - pool_id: PoolId, - commission: Option, - payee: Option, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; - ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - - let final_commission = commission - .or(bonded_pool.commission.current.as_ref().map(|(c, _)| c).cloned()) - .ok_or(Error::::NoCommissionSet)?; - - let final_payee = payee - .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) - .ok_or(Error::::NoCommissionPayeeSet)?; - - bonded_pool - .commission - .maybe_update_current(&final_commission, final_payee.clone())?; - bonded_pool.put(); - Self::deposit_event(Event::::PoolCommissionUpdated { - pool_id, - commission: final_commission, - payee: final_payee, - }); - Ok(()) - } - - /// Set the maximum commission of a pool. - /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If a maximum commission already exists prior to this call, - /// then the updated max commission must be lower, otherwise this call will - /// fail. - /// - /// This call also updates the pool's current commission to the new maximum - /// if the current commission is higher than the maximum supplied. - #[pallet::weight(T::WeightInfo::set_commission_max())] - pub fn set_commission_max( - origin: OriginFor, - pool_id: PoolId, - max_commission: Perbill, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; - ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - - bonded_pool.commission.maybe_update_max(max_commission.clone())?; - bonded_pool.put(); - Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); - Ok(()) - } - - /// Set the commission throttle for a pool. - /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If a throttle is already present, this call will only succeed - /// if a more restrictive throttle configuration is given. - /// - /// If a throttle configuration does not yet exist, the provided values are - /// set. - #[pallet::weight(T::WeightInfo::set_commission_throttle())] - pub fn set_commission_throttle( - origin: OriginFor, - pool_id: PoolId, - prefs: CommissionThrottlePrefs, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; - ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - - bonded_pool.commission.maybe_update_throttle(prefs)?; - bonded_pool.put(); - - Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, prefs }); - Ok(()) - } - /// Update configurations for the nomination pools. The origin for this call must be /// Root. /// @@ -2422,6 +2330,101 @@ pub mod pallet { ensure!(bonded_pool.can_nominate(&who), Error::::NotNominator); T::Staking::chill(&bonded_pool.bonded_account()) } + + /// Set the commission of a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If the pool has a max commission set, the commission supplied + /// must be less or equal to that value. + /// + /// If the max commission has not yet been set, then the commission range is + /// not bounded. A `payee` must be provided if commission has not yet been + /// set (still `None`). Once commission has been set, the `payee` can be + /// omitted in further calls. If a `payee` update is desired, the commission + /// must still be passed into the call. + #[pallet::call_index(14)] + #[pallet::weight(T::WeightInfo::set_commission())] + pub fn set_commission( + origin: OriginFor, + pool_id: PoolId, + commission: Option, + payee: Option, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + let final_commission = commission + .or(bonded_pool.commission.current.as_ref().map(|(c, _)| c).cloned()) + .ok_or(Error::::NoCommissionSet)?; + + let final_payee = payee + .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) + .ok_or(Error::::NoCommissionPayeeSet)?; + + bonded_pool + .commission + .maybe_update_current(&final_commission, final_payee.clone())?; + bonded_pool.put(); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + commission: final_commission, + payee: final_payee, + }); + Ok(()) + } + + /// Set the maximum commission of a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If a maximum commission already exists prior to this call, + /// then the updated max commission must be lower, otherwise this call will + /// fail. + /// + /// This call also updates the pool's current commission to the new maximum + /// if the current commission is higher than the maximum supplied. + #[pallet::call_index(15)] + #[pallet::weight(T::WeightInfo::set_commission_max())] + pub fn set_commission_max( + origin: OriginFor, + pool_id: PoolId, + max_commission: Perbill, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.maybe_update_max(max_commission.clone())?; + bonded_pool.put(); + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); + Ok(()) + } + + /// Set the commission throttle for a pool. + /// + /// The dispatch origin of this call must be signed by the `root` role of + /// the pool. If a throttle is already present, this call will only succeed + /// if a more restrictive throttle configuration is given. + /// + /// If a throttle configuration does not yet exist, the provided values are + /// set. + #[pallet::call_index(16)] + #[pallet::weight(T::WeightInfo::set_commission_throttle())] + pub fn set_commission_throttle( + origin: OriginFor, + pool_id: PoolId, + prefs: CommissionThrottlePrefs, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; + ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); + + bonded_pool.commission.maybe_update_throttle(prefs)?; + bonded_pool.put(); + + Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, prefs }); + Ok(()) + } } #[pallet::hooks] From 6ad97d3661030b82854502591322bed2b9c24eb6 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 12:59:55 +0700 Subject: [PATCH 112/221] CommissionThrottlePrefs -> CommissionChangeRate --- .../nomination-pools/benchmarking/src/lib.rs | 10 +++---- frame/nomination-pools/src/lib.rs | 12 ++++----- frame/nomination-pools/src/tests.rs | 26 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index ae0b185556287..9596910e1d70a 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -29,7 +29,7 @@ use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionThrottle, - CommissionThrottlePrefs, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, + CommissionChangeRate, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, }; @@ -686,7 +686,7 @@ frame_benchmarking::benchmarks! { // set a max commission Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); // set a commission throttle - Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionThrottlePrefs { + Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate { max_increase: Perbill::from_percent(20), min_delay: 0u32.into(), }).unwrap(); @@ -697,7 +697,7 @@ frame_benchmarking::benchmarks! { current: Some((Perbill::from_percent(20), depositor)), max: Some(Perbill::from_percent(50)), throttle: Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(20), min_delay: 0u32.into() }, @@ -722,7 +722,7 @@ frame_benchmarking::benchmarks! { set_commission_throttle { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionThrottlePrefs { + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), }) @@ -732,7 +732,7 @@ frame_benchmarking::benchmarks! { current: None, max: None, throttle: Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), }, diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d6f34e22ecbd2..5dc68650629a6 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -693,7 +693,7 @@ impl Commission { /// `min_delay` values and decreased `max_increase` values. fn maybe_update_throttle( &mut self, - change_rate: CommissionThrottlePrefs, + change_rate: CommissionChangeRate, ) -> DispatchResult { ensure!( &self.throttle.as_ref().map_or(true, |t| t.less_restrictive(&change_rate)), @@ -729,14 +729,14 @@ impl Commission { #[scale_info(skip_type_params(T))] pub struct CommissionThrottle { /// The change rate dictates how often and by how much commission can be updated. - pub change_rate: CommissionThrottlePrefs, + pub change_rate: CommissionChangeRate, /// The block the previous commission update took place. pub previous_set_at: Option, } impl CommissionThrottle { /// Returns `true` if `change_rate` is less restrictive than `self`. - fn less_restrictive(&self, change_rate: &CommissionThrottlePrefs) -> bool { + fn less_restrictive(&self, change_rate: &CommissionChangeRate) -> bool { change_rate.max_increase <= self.change_rate.max_increase && change_rate.min_delay >= self.change_rate.min_delay } @@ -755,7 +755,7 @@ impl CommissionThrottle { /// Throttle prefs need to be passed and configured together. This struct is /// used in the `set_commission_throttle` call as well as in CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] -pub struct CommissionThrottlePrefs { +pub struct CommissionChangeRate { /// The maximum amount the commission can be updated by per `min_delay` period. pub max_increase: Perbill, /// How often an update can take place. @@ -1613,7 +1613,7 @@ pub mod pallet { /// A pool's commission throttle has been changed. PoolCommissionThrottleUpdated { pool_id: PoolId, - prefs: CommissionThrottlePrefs, + prefs: CommissionChangeRate, }, } @@ -2413,7 +2413,7 @@ pub mod pallet { pub fn set_commission_throttle( origin: OriginFor, pool_id: PoolId, - prefs: CommissionThrottlePrefs, + prefs: CommissionChangeRate, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index d420d3dde23a5..1bbb527e8e9e1 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -351,7 +351,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } @@ -362,7 +362,7 @@ mod bonded_pool { current: Some((Perbill::from_percent(5), 900)), max: None, throttle: Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }, @@ -399,7 +399,7 @@ mod bonded_pool { current: Some((Perbill::from_percent(6), 900)), max: None, throttle: Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }, @@ -484,7 +484,7 @@ mod bonded_pool { }, Event::PoolCommissionThrottleUpdated { pool_id: 1, - prefs: CommissionThrottlePrefs { + prefs: CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2 } @@ -632,7 +632,7 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 9999, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 1000_u64 } @@ -644,7 +644,7 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(1), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 1000_u64 } @@ -656,7 +656,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 1000_u64 } @@ -664,7 +664,7 @@ mod bonded_pool { assert_eq!( BondedPools::::get(1).unwrap().commission.throttle, Some(CommissionThrottle { - change_rate: CommissionThrottlePrefs { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 1000_u64 }, @@ -678,7 +678,7 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 500_u64 } @@ -692,7 +692,7 @@ mod bonded_pool { Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(10), min_delay: 1000_u64 } @@ -704,7 +704,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 2000_u64 } @@ -713,7 +713,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 2000_u64 } @@ -722,7 +722,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionThrottlePrefs { + CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 3000_u64 } From f4d25fe7efc99ccd44bd9f4fa1aafec858b75f3f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 22:23:42 +0700 Subject: [PATCH 113/221] consider min_delay durations in throttle --- bin/node/runtime/src/lib.rs | 8 + .../nomination-pools/benchmarking/src/lib.rs | 4 +- frame/nomination-pools/src/lib.rs | 61 +++++++- frame/nomination-pools/src/mock.rs | 8 + frame/nomination-pools/src/tests.rs | 133 +++++++++++++--- frame/nomination-pools/src/weights.rs | 142 +++++++++--------- .../nomination-pools/test-staking/src/mock.rs | 8 + 7 files changed, 260 insertions(+), 104 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e5776e3fd692c..11c4eda2babfb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -782,12 +782,20 @@ impl Convert for U256ToBalance { } } +pub struct BlockNumberToU32; +impl Convert for BlockNumberToU32 { + fn convert(n: BlockNumber) -> u32 { + n as u32 + } +} + impl pallet_nomination_pools::Config for Runtime { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; + type BlockNumberToU32 = BlockNumberToU32; type U256ToBalance = U256ToBalance; type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondPoolsWindow; diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 9596910e1d70a..38bd3d00d4f65 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -28,8 +28,8 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ - BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionThrottle, - CommissionChangeRate, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, + BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, + CommissionThrottle, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, }; diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 5dc68650629a6..765349bb06d7b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -628,15 +628,42 @@ impl Commission { /// Throttling is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { if let Some(t) = self.throttle.as_ref() { - // check for `min_delay` throttling - if t.previous_set_at.map_or(false, |p| { - >::block_number().saturating_sub(p) < - t.change_rate.min_delay - }) { + // factor previously updated block into whether user is throttled. + if t.previous_set_at.map_or( + // if no `previous_set_at` is set, throttled if the attempted increase + // in commission is greater than `max_increase`. + (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase, + |p| { + // `min_delay` blocks must have been surpassed since last update. + if >::block_number().saturating_sub(p) < + t.change_rate.min_delay + { + return true + } + // ensure the `max_increase` durations surpassed since the previous + // commission update allow the attempted commission increase. + // + // the attempted increase in commission relative to the current commission. + let attempted_increase = (*to).saturating_sub(self.commission_or_zero()); + + // the total durations passed since the last commission update. + let intervals_passed = self.intervals_since_block(&p, &t.change_rate.min_delay); + + // the maximum allowed increase, where the current `max_increase` + // Perbill is converted into a u32 by multiplying itself with 100_u32 + // and multiplied by durations passed, before being converted back + // into a Perbill. + let max_allowed_increase = Perbill::from_percent( + (t.change_rate.max_increase * 100_u32) * intervals_passed, + ); + + // throttled (true) if attempted increase is greater than the maximum + // allowed increase, as a percentage. + attempted_increase > max_allowed_increase + }, + ) { return true } - // check for `max_increase` throttling - return (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase } false } @@ -721,6 +748,23 @@ impl Commission { .map(|(commission, payee)| (*commission * *pending_rewards, payee.clone())) .or(None) } + + /// Calculate the amount of intervals that consume a period of blocks. + /// + /// The final number of intervals is returned. + pub fn intervals_since_block( + &self, + start_block: &T::BlockNumber, + interval: &T::BlockNumber, + ) -> u32 { + let blocks_passed = >::block_number().saturating_sub(*start_block); + + // ensure divsion by zero is not possible. + if blocks_passed == Zero::zero() { + return Zero::zero() + } + T::BlockNumberToU32::convert(blocks_passed.div(*interval)) + } } /// The pool root is able to set a commission throttle for their pool. @@ -1379,6 +1423,9 @@ pub mod pallet { /// The nominating balance. type Currency: Currency; + /// Convert the block number into u32. + type BlockNumberToU32: Convert; + /// The type that is used for reward counter. /// /// The arithmetic of the reward counter might saturate based on the size of the diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index e0398f0904f31..e38a3ccd666a8 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -212,6 +212,13 @@ impl Convert for U256ToBalance { } } +pub struct BlockNumberToU32; +impl Convert for BlockNumberToU32 { + fn convert(n: BlockNumber) -> u32 { + return n as u32 + } +} + parameter_types! { pub static PostUnbondingPoolsWindow: u32 = 2; pub static MaxMetadataLen: u32 = 2; @@ -224,6 +231,7 @@ impl pools::Config for Runtime { type Currency = Balances; type RewardCounter = RewardCounter; type BalanceToU256 = BalanceToU256; + type BlockNumberToU32 = BlockNumberToU32; type U256ToBalance = U256ToBalance; type Staking = StakingMock; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 1bbb527e8e9e1..c97b0bb4435a7 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -351,10 +351,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2_u64 - } + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } )); assert_eq!( BondedPool::::get(1).unwrap().commission, @@ -382,7 +379,7 @@ mod bonded_pool { Error::::CommissionChangeThrottled ); - // Run 2 blocks into the future. + // Run to block 3 run_blocks(2); // We now try to increase commission by 1%, and provide an initial payee. @@ -656,17 +653,14 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 1000_u64 - } + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } )); assert_eq!( BondedPools::::get(1).unwrap().commission.throttle, Some(CommissionThrottle { change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(5), - min_delay: 1000_u64 + min_delay: 10_u64 }, previous_set_at: None }) @@ -680,7 +674,7 @@ mod bonded_pool { 1, CommissionChangeRate { max_increase: Perbill::from_percent(5), - min_delay: 500_u64 + min_delay: 5_u64 } ), Error::::CommissionThrottleNotAllowed @@ -694,7 +688,7 @@ mod bonded_pool { 1, CommissionChangeRate { max_increase: Perbill::from_percent(10), - min_delay: 1000_u64 + min_delay: 10_u64 } ), Error::::CommissionThrottleNotAllowed @@ -704,31 +698,122 @@ mod bonded_pool { assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 2000_u64 - } + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } )); + // Successful more restrictive change of max_increase with the current min_delay assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 2000_u64 - } + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } )); + // Successful more restrictive change of both max_increase and min_delay assert_ok!(Pools::set_commission_throttle( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(3), - min_delay: 3000_u64 - } + CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } + )); + + // multi duration `min_delay` test. + // + // set the commission throttle change_rate of 1% per 3000 blocks. + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } + )); + // pre-requisite: set the commission to 1%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(1)), + Some(900), + )); + // Run 90 blocks into the future so we are eligible to update commission + // with 3 `min_delay` durations passed. + run_blocks(91); + + // we should not be able to increase the commission to 5%: 1% beyond the throttle limit. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(5)), + Some(900), + ), + Error::::CommissionChangeThrottled + ); + + // we should however be able to increase the commission to 4%: 1% + (3*1%). + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(4)), + Some(900), )); }); } + + #[test] + fn intervals_since_block_works() { + ExtBuilder::default().build_and_execute(|| { + // some duration to work with + let duration = 3_u64; + + // get commission to access `intervals_since_block` + let commission = BondedPool::::get(1).unwrap().commission; + + // run 5 durations in the future + let block_number = duration * 5; + run_to_block(block_number); + + // interval of 14 blocks should result in 4 durations + assert_eq!(commission.intervals_since_block(&1, &duration), 4_u32); + + // interval of 13 blocks should result in 4 durations + assert_eq!(commission.intervals_since_block(&2, &duration), 4_u32); + + // interval of 12 blocks should result in 4 durations + assert_eq!(commission.intervals_since_block(&3, &duration), 4_u32); + + // interval of 11 blocks should result in 3 durations + assert_eq!(commission.intervals_since_block(&4, &duration), 3_u32); + + // interval of 10 blocks should result in 3 durations + assert_eq!(commission.intervals_since_block(&5, &duration), 3_u32); + + // interval of 9 blocks should result in 3 durations + assert_eq!(commission.intervals_since_block(&6, &duration), 3_u32); + + // interval of 8 blocks should result in 2 durations + assert_eq!(commission.intervals_since_block(&7, &duration), 2_u32); + + // interval of 7 blocks should result in 2 duration + assert_eq!(commission.intervals_since_block(&8, &duration), 2_u32); + + // interval of 6 blocks should result in 2 durations + assert_eq!(commission.intervals_since_block(&9, &duration), 2_u32); + + // interval of 5 blocks should result in 1 duration + assert_eq!(commission.intervals_since_block(&10, &duration), 1_u32); + + // interval of 4 blocks should result in 1 duration + assert_eq!(commission.intervals_since_block(&11, &duration), 1_u32); + + // interval of 3 blocks should result in 1 duration + assert_eq!(commission.intervals_since_block(&12, &duration), 1_u32); + + // interval of 2 blocks should result in 0 durations + assert_eq!(commission.intervals_since_block(&13, &duration), 0_u32); + + // interval of 1 block should result in 0 durations + assert_eq!(commission.intervals_since_block(&14, &duration), 0_u32); + + // interval of 0 block should result in 0 durations + assert_eq!(commission.intervals_since_block(&15, &duration), 0_u32); + }) + } } mod reward_pool { diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index f492148d0c914..a94374fc3a83f 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-09, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `192.168.1.33`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 174_000 nanoseconds. - Weight::from_ref_time(177_000_000) + // Minimum execution time: 167_000 nanoseconds. + Weight::from_ref_time(170_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 164_000 nanoseconds. - Weight::from_ref_time(175_000_000) + // Minimum execution time: 163_000 nanoseconds. + Weight::from_ref_time(164_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 194_000 nanoseconds. - Weight::from_ref_time(202_000_000) + // Minimum execution time: 242_000 nanoseconds. + Weight::from_ref_time(247_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(64_000_000) + // Minimum execution time: 61_000 nanoseconds. + Weight::from_ref_time(63_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 163_000 nanoseconds. - Weight::from_ref_time(171_000_000) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(184_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -147,9 +147,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_502_532) - // Standard Error: 1_512 - .saturating_add(Weight::from_ref_time(17_113).saturating_mul(s.into())) + Weight::from_ref_time(62_118_829) + // Standard Error: 5_215 + .saturating_add(Weight::from_ref_time(62_099).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -164,10 +164,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 108_000 nanoseconds. - Weight::from_ref_time(115_200_554) - // Standard Error: 2_177 - .saturating_add(Weight::from_ref_time(14_609).saturating_mul(s.into())) + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(114_433_516) + // Standard Error: 3_429 + .saturating_add(Weight::from_ref_time(29_129).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -194,9 +194,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Minimum execution time: 172_000 nanoseconds. - Weight::from_ref_time(182_121_177) - // Standard Error: 3_294 - .saturating_add(Weight::from_ref_time(339).saturating_mul(s.into())) + Weight::from_ref_time(179_866_176) + // Standard Error: 3_909 + .saturating_add(Weight::from_ref_time(9_931).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -222,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 159_000 nanoseconds. - Weight::from_ref_time(165_000_000) + // Minimum execution time: 155_000 nanoseconds. + Weight::from_ref_time(158_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -241,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 66_000 nanoseconds. - Weight::from_ref_time(67_815_257) - // Standard Error: 10_581 - .saturating_add(Weight::from_ref_time(1_548_222).saturating_mul(n.into())) + // Minimum execution time: 69_000 nanoseconds. + Weight::from_ref_time(69_153_832) + // Standard Error: 14_591 + .saturating_add(Weight::from_ref_time(1_534_185).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -253,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 41_000 nanoseconds. - Weight::from_ref_time(43_000_000) + // Minimum execution time: 40_000 nanoseconds. + Weight::from_ref_time(41_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -264,7 +264,7 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 256]`. fn set_metadata(_n: u32, ) -> Weight { // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_770_961) + Weight::from_ref_time(15_899_205) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -274,14 +274,14 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. + // Minimum execution time: 8_000 nanoseconds. Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(26_000_000) + Weight::from_ref_time(27_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -295,22 +295,22 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 68_000 nanoseconds. + // Minimum execution time: 67_000 nanoseconds. Weight::from_ref_time(71_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 25_000 nanoseconds. + // Minimum execution time: 26_000 nanoseconds. Weight::from_ref_time(27_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 24_000 nanoseconds. - Weight::from_ref_time(27_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(25_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -339,8 +339,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 174_000 nanoseconds. - Weight::from_ref_time(177_000_000) + // Minimum execution time: 167_000 nanoseconds. + Weight::from_ref_time(170_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -354,8 +354,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 164_000 nanoseconds. - Weight::from_ref_time(175_000_000) + // Minimum execution time: 163_000 nanoseconds. + Weight::from_ref_time(164_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -369,8 +369,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 194_000 nanoseconds. - Weight::from_ref_time(202_000_000) + // Minimum execution time: 242_000 nanoseconds. + Weight::from_ref_time(247_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -379,8 +379,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(64_000_000) + // Minimum execution time: 61_000 nanoseconds. + Weight::from_ref_time(63_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -399,8 +399,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 163_000 nanoseconds. - Weight::from_ref_time(171_000_000) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(184_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -412,9 +412,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_502_532) - // Standard Error: 1_512 - .saturating_add(Weight::from_ref_time(17_113).saturating_mul(s.into())) + Weight::from_ref_time(62_118_829) + // Standard Error: 5_215 + .saturating_add(Weight::from_ref_time(62_099).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -429,10 +429,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 108_000 nanoseconds. - Weight::from_ref_time(115_200_554) - // Standard Error: 2_177 - .saturating_add(Weight::from_ref_time(14_609).saturating_mul(s.into())) + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(114_433_516) + // Standard Error: 3_429 + .saturating_add(Weight::from_ref_time(29_129).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -459,9 +459,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { // Minimum execution time: 172_000 nanoseconds. - Weight::from_ref_time(182_121_177) - // Standard Error: 3_294 - .saturating_add(Weight::from_ref_time(339).saturating_mul(s.into())) + Weight::from_ref_time(179_866_176) + // Standard Error: 3_909 + .saturating_add(Weight::from_ref_time(9_931).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -487,8 +487,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 159_000 nanoseconds. - Weight::from_ref_time(165_000_000) + // Minimum execution time: 155_000 nanoseconds. + Weight::from_ref_time(158_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -506,10 +506,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 66_000 nanoseconds. - Weight::from_ref_time(67_815_257) - // Standard Error: 10_581 - .saturating_add(Weight::from_ref_time(1_548_222).saturating_mul(n.into())) + // Minimum execution time: 69_000 nanoseconds. + Weight::from_ref_time(69_153_832) + // Standard Error: 14_591 + .saturating_add(Weight::from_ref_time(1_534_185).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -518,8 +518,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 41_000 nanoseconds. - Weight::from_ref_time(43_000_000) + // Minimum execution time: 40_000 nanoseconds. + Weight::from_ref_time(41_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -529,7 +529,7 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 256]`. fn set_metadata(_n: u32, ) -> Weight { // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_770_961) + Weight::from_ref_time(15_899_205) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -539,14 +539,14 @@ impl WeightInfo for () { // Storage: NominationPools MinCreateBond (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. + // Minimum execution time: 8_000 nanoseconds. Weight::from_ref_time(8_000_000) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(26_000_000) + Weight::from_ref_time(27_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -560,22 +560,22 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 68_000 nanoseconds. + // Minimum execution time: 67_000 nanoseconds. Weight::from_ref_time(71_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission() -> Weight { - // Minimum execution time: 25_000 nanoseconds. + // Minimum execution time: 26_000 nanoseconds. Weight::from_ref_time(27_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 24_000 nanoseconds. - Weight::from_ref_time(27_000_000) + // Minimum execution time: 25_000 nanoseconds. + Weight::from_ref_time(25_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 568dec7b3a340..c3792ec9207a7 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -162,6 +162,13 @@ impl Convert for U256ToBalance { } } +pub struct BlockNumberToU32; +impl Convert for BlockNumberToU32 { + fn convert(n: BlockNumber) -> u32 { + return n as u32 + } +} + parameter_types! { pub const PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -174,6 +181,7 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; + type BlockNumberToU32 = BlockNumberToU32; type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type MaxMetadataLen = ConstU32<256>; From eb3f62789d92af1128b66193b3bd871399647383 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 22:32:21 +0700 Subject: [PATCH 114/221] add struct to mock --- frame/nomination-pools/benchmarking/src/mock.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 6959aa9783ee5..529ccac50bec2 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -147,6 +147,13 @@ impl Convert for U256ToBalance { } } +pub struct BlockNumberToU32; +impl Convert for BlockNumberToU32 { + fn convert(n: BlockNumber) -> u32 { + return n as u32 + } +} + parameter_types! { pub static PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -158,6 +165,7 @@ impl pallet_nomination_pools::Config for Runtime { type WeightInfo = (); type Currency = Balances; type RewardCounter = FixedU128; + type BlockNumberToU32 = BlockNumberToU32; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; type Staking = Staking; From 03ef1c712e894b57f2d68a84c23edf0fad3ade73 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 22:44:29 +0700 Subject: [PATCH 115/221] clippy fix --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 765349bb06d7b..8a8b2517e0454 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2441,7 +2441,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_max(max_commission.clone())?; + bonded_pool.commission.maybe_update_max(max_commission)?; bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) From 111677b7e73b553b8598051bb6486e5040a00441 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 23:11:24 +0700 Subject: [PATCH 116/221] use saturated_into --- bin/node/runtime/src/lib.rs | 8 -------- frame/nomination-pools/benchmarking/src/mock.rs | 8 -------- frame/nomination-pools/src/lib.rs | 7 ++----- frame/nomination-pools/src/mock.rs | 8 -------- frame/nomination-pools/test-staking/src/mock.rs | 8 -------- 5 files changed, 2 insertions(+), 37 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 11c4eda2babfb..e5776e3fd692c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -782,20 +782,12 @@ impl Convert for U256ToBalance { } } -pub struct BlockNumberToU32; -impl Convert for BlockNumberToU32 { - fn convert(n: BlockNumber) -> u32 { - n as u32 - } -} - impl pallet_nomination_pools::Config for Runtime { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; - type BlockNumberToU32 = BlockNumberToU32; type U256ToBalance = U256ToBalance; type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondPoolsWindow; diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 529ccac50bec2..6959aa9783ee5 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -147,13 +147,6 @@ impl Convert for U256ToBalance { } } -pub struct BlockNumberToU32; -impl Convert for BlockNumberToU32 { - fn convert(n: BlockNumber) -> u32 { - return n as u32 - } -} - parameter_types! { pub static PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -165,7 +158,6 @@ impl pallet_nomination_pools::Config for Runtime { type WeightInfo = (); type Currency = Balances; type RewardCounter = FixedU128; - type BlockNumberToU32 = BlockNumberToU32; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; type Staking = Staking; diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 8a8b2517e0454..a2c167430ce60 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -334,7 +334,7 @@ use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, Zero, + AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, SaturatedConversion, StaticLookup, Zero, }, FixedPointNumber, Perbill, }; @@ -763,7 +763,7 @@ impl Commission { if blocks_passed == Zero::zero() { return Zero::zero() } - T::BlockNumberToU32::convert(blocks_passed.div(*interval)) + blocks_passed.div(*interval).saturated_into::() } } @@ -1423,9 +1423,6 @@ pub mod pallet { /// The nominating balance. type Currency: Currency; - /// Convert the block number into u32. - type BlockNumberToU32: Convert; - /// The type that is used for reward counter. /// /// The arithmetic of the reward counter might saturate based on the size of the diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index e38a3ccd666a8..e0398f0904f31 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -212,13 +212,6 @@ impl Convert for U256ToBalance { } } -pub struct BlockNumberToU32; -impl Convert for BlockNumberToU32 { - fn convert(n: BlockNumber) -> u32 { - return n as u32 - } -} - parameter_types! { pub static PostUnbondingPoolsWindow: u32 = 2; pub static MaxMetadataLen: u32 = 2; @@ -231,7 +224,6 @@ impl pools::Config for Runtime { type Currency = Balances; type RewardCounter = RewardCounter; type BalanceToU256 = BalanceToU256; - type BlockNumberToU32 = BlockNumberToU32; type U256ToBalance = U256ToBalance; type Staking = StakingMock; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index c3792ec9207a7..568dec7b3a340 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -162,13 +162,6 @@ impl Convert for U256ToBalance { } } -pub struct BlockNumberToU32; -impl Convert for BlockNumberToU32 { - fn convert(n: BlockNumber) -> u32 { - return n as u32 - } -} - parameter_types! { pub const PostUnbondingPoolsWindow: u32 = 10; pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls"); @@ -181,7 +174,6 @@ impl pallet_nomination_pools::Config for Runtime { type RewardCounter = FixedU128; type BalanceToU256 = BalanceToU256; type U256ToBalance = U256ToBalance; - type BlockNumberToU32 = BlockNumberToU32; type Staking = Staking; type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow; type MaxMetadataLen = ConstU32<256>; From 9a05061db3c106270a88084252a86caf549b332a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Fri, 23 Dec 2022 23:11:45 +0700 Subject: [PATCH 117/221] fmt --- frame/nomination-pools/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a2c167430ce60..ee651ba63c584 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -334,7 +334,8 @@ use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, CheckedAdd, CheckedSub, Convert, Saturating, SaturatedConversion, StaticLookup, Zero, + AccountIdConversion, CheckedAdd, CheckedSub, Convert, SaturatedConversion, Saturating, + StaticLookup, Zero, }, FixedPointNumber, Perbill, }; From 0f250aa250e7452edeecdbd5ddcf3c0e465d7489 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 08:09:44 +0700 Subject: [PATCH 118/221] + GlobalMaxCommission storage --- frame/nomination-pools/benchmarking/src/lib.rs | 8 +++++--- frame/nomination-pools/fuzzer/src/call.rs | 7 ++++--- frame/nomination-pools/src/lib.rs | 12 ++++++++++++ frame/nomination-pools/src/migration.rs | 3 +++ frame/nomination-pools/src/mock.rs | 14 +++++++++++++- frame/nomination-pools/src/tests.rs | 5 +++++ 6 files changed, 42 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 38bd3d00d4f65..ebde9bcb8ecf7 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -29,9 +29,9 @@ use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, - CommissionThrottle, ConfigOp, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, - MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, - SubPoolsStorage, + CommissionThrottle, ConfigOp, GlobalMaxCommission, MaxPoolMembers, MaxPoolMembersPerPool, + MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, + PoolState, RewardPools, SubPoolsStorage, }; use sp_runtime::{ traits::{Bounded, StaticLookup, Zero}, @@ -632,12 +632,14 @@ frame_benchmarking::benchmarks! { ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX) + ConfigOp::Set(Perbill::max_value()) ) verify { assert_eq!(MinJoinBond::::get(), BalanceOf::::max_value()); assert_eq!(MinCreateBond::::get(), BalanceOf::::max_value()); assert_eq!(MaxPools::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembers::::get(), Some(u32::MAX)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(u32::MAX)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::max_value())); } update_roles { diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index b07903609e8ab..307a647aebcb8 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -33,11 +33,11 @@ use pallet_nomination_pools::{ mock::*, pallet as pools, pallet::{BondedPools, Call as PoolsCall, Event as PoolsEvents, PoolMembers}, - BondExtra, BondedPool, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, - MinCreateBond, MinJoinBond, PoolId, + BondExtra, BondedPool, GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, + MaxPools, MinCreateBond, MinJoinBond, PoolId, }; use rand::{seq::SliceRandom, Rng}; -use sp_runtime::{assert_eq_error_rate, Perquintill}; +use sp_runtime::{assert_eq_error_rate, Perbill, Perquintill}; const ERA: BlockNumber = 1000; const MAX_ED_MULTIPLE: Balance = 10_000; @@ -224,6 +224,7 @@ fn main() { MaxPoolMembers::::set(Some(10_000)); MaxPoolMembersPerPool::::set(Some(1000)); MaxPools::::set(Some(1_000)); + GlobalMaxCommission::::set(Some(Perbill::from_percent(50))); MinCreateBond::::set(10 * ExistentialDeposit::get()); MinJoinBond::::set(5 * ExistentialDeposit::get()); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index ee651ba63c584..b18076a6b9781 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1509,6 +1509,10 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; + /// Maximum commission that can be set for all pools. + #[pallet::storage] + pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; + /// Active members. #[pallet::storage] pub type PoolMembers = @@ -1555,6 +1559,7 @@ pub mod pallet { pub max_pools: Option, pub max_members_per_pool: Option, pub max_members: Option, + pub global_max_commission: Option, } #[cfg(feature = "std")] @@ -1566,6 +1571,7 @@ pub mod pallet { max_pools: Some(16), max_members_per_pool: Some(32), max_members: Some(16 * 32), + global_max_commission: None, } } } @@ -1584,6 +1590,9 @@ pub mod pallet { if let Some(max_members) = self.max_members { MaxPoolMembers::::put(max_members); } + if let Some(global_max_commission) = self.global_max_commission { + GlobalMaxCommission::::put(global_max_commission); + } } } @@ -2277,6 +2286,7 @@ pub mod pallet { /// * `max_pools` - Set [`MaxPools`]. /// * `max_members` - Set [`MaxPoolMembers`]. /// * `max_members_per_pool` - Set [`MaxPoolMembersPerPool`]. + /// * `global_max_commission` - Set [`GlobalMaxCommission`]. #[pallet::call_index(11)] #[pallet::weight(T::WeightInfo::set_configs())] pub fn set_configs( @@ -2286,6 +2296,7 @@ pub mod pallet { max_pools: ConfigOp, max_members: ConfigOp, max_members_per_pool: ConfigOp, + global_max_commission: ConfigOp, ) -> DispatchResult { ensure_root(origin)?; @@ -2304,6 +2315,7 @@ pub mod pallet { config_op_exp!(MaxPools::, max_pools); config_op_exp!(MaxPoolMembers::, max_members); config_op_exp!(MaxPoolMembersPerPool::, max_members_per_pool); + config_op_exp!(GlobalMaxCommission::, global_max_commission); Ok(()) } diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index caf0e5ab828db..dae99098d42f8 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -493,6 +493,9 @@ pub mod v4 { ); if current == 4 && onchain == 3 { + GlobalMaxCommission::::set(None); + log!(info, "Set initial global max commission of None"); + let mut translated = 0u64; BondedPools::::translate::, _>(|_key, old_value| { translated.saturating_inc(); diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index e0398f0904f31..4230bb1a570d2 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -251,11 +251,17 @@ pub struct ExtBuilder { members: Vec<(AccountId, Balance)>, max_members: Option, max_members_per_pool: Option, + global_max_commission: Option, } impl Default for ExtBuilder { fn default() -> Self { - Self { members: Default::default(), max_members: Some(4), max_members_per_pool: Some(3) } + Self { + members: Default::default(), + max_members: Some(4), + max_members_per_pool: Some(3), + global_max_commission: Some(Perbill::from_percent(50)), + } } } @@ -297,6 +303,11 @@ impl ExtBuilder { self } + pub fn global_max_commission(mut self, commission: Option) -> Self { + self.global_max_commission = commission; + self + } + pub fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut storage = @@ -308,6 +319,7 @@ impl ExtBuilder { max_pools: Some(2), max_members_per_pool: self.max_members_per_pool, max_members: self.max_members, + global_max_commission: self.global_max_commission, } .assimilate_storage(&mut storage); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index c97b0bb4435a7..198b2f3d03182 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5107,12 +5107,14 @@ mod set_configs { ConfigOp::Set(3u32), ConfigOp::Set(4u32), ConfigOp::Set(5u32), + ConfigOp::Set(Perbill::from_percent(6)) )); assert_eq!(MinJoinBond::::get(), 1); assert_eq!(MinCreateBond::::get(), 2); assert_eq!(MaxPools::::get(), Some(3)); assert_eq!(MaxPoolMembers::::get(), Some(4)); assert_eq!(MaxPoolMembersPerPool::::get(), Some(5)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::from_percent(6))); // Noop does nothing assert_storage_noop!(assert_ok!(Pools::set_configs( @@ -5122,6 +5124,7 @@ mod set_configs { ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop, + ConfigOp::Noop, ))); // Removing works @@ -5132,12 +5135,14 @@ mod set_configs { ConfigOp::Remove, ConfigOp::Remove, ConfigOp::Remove, + ConfigOp::Remove, )); assert_eq!(MinJoinBond::::get(), 0); assert_eq!(MinCreateBond::::get(), 0); assert_eq!(MaxPools::::get(), None); assert_eq!(MaxPoolMembers::::get(), None); assert_eq!(MaxPoolMembersPerPool::::get(), None); + assert_eq!(GlobalMaxCommission::::get(), None); }); } } From 9ba4347c4a1d940c3b1eeb77e46c0e57f159ff38 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 08:12:08 +0700 Subject: [PATCH 119/221] + global_max_commission --- frame/nomination-pools/test-staking/src/mock.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 568dec7b3a340..4ca30ceedd904 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ }; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128,Perbill }; type AccountId = u128; @@ -209,6 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(5), max_members: Some(3 * 5), + global_max_commission: Some(Perbill::from_percent(50)), } .assimilate_storage(&mut storage) .unwrap(); From b9f5b1ab091f460f5dea05cc7acff671d76e8ac1 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 08:46:05 +0700 Subject: [PATCH 120/221] fmt --- frame/nomination-pools/test-staking/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 4ca30ceedd904..4b87e82d92c16 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ }; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128,Perbill + FixedU128, Perbill, }; type AccountId = u128; From e029514506e6cb3f9cb00867245edc59bd262a2c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 08:54:15 +0700 Subject: [PATCH 121/221] fix --- frame/nomination-pools/benchmarking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index ebde9bcb8ecf7..cbf072bf235f2 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -631,7 +631,7 @@ frame_benchmarking::benchmarks! { ConfigOp::Set(BalanceOf::::max_value()), ConfigOp::Set(u32::MAX), ConfigOp::Set(u32::MAX), - ConfigOp::Set(u32::MAX) + ConfigOp::Set(u32::MAX), ConfigOp::Set(Perbill::max_value()) ) verify { assert_eq!(MinJoinBond::::get(), BalanceOf::::max_value()); From e30b2def2fd8741cdace4ebe0ca27b6158102082 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 09:03:48 +0700 Subject: [PATCH 122/221] + global_max_commission --- frame/nomination-pools/benchmarking/src/mock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 6959aa9783ee5..730bbd19e12ea 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -195,6 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(3), max_members: Some(3 * 3), + global_max_commission: Some(Perbill::from_percent(500)) } .assimilate_storage(&mut storage); sp_io::TestExternalities::from(storage) From b3d84ffaef436e4192f17896260ca7adef5320d8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 09:10:37 +0700 Subject: [PATCH 123/221] use Perbill --- frame/nomination-pools/benchmarking/src/mock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 730bbd19e12ea..2bafd3c0fb99c 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -20,7 +20,7 @@ use frame_election_provider_support::VoteWeight; use frame_support::{pallet_prelude::*, parameter_types, traits::ConstU64, PalletId}; use sp_runtime::{ traits::{Convert, IdentityLookup}, - FixedU128, + FixedU128, Perbill, }; type AccountId = u128; @@ -195,7 +195,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(3), max_members: Some(3 * 3), - global_max_commission: Some(Perbill::from_percent(500)) + global_max_commission: Some(Perbill::from_percent(500)), } .assimilate_storage(&mut storage); sp_io::TestExternalities::from(storage) From 6fa4671af8cf29579d8dec93b7bd4703d37bef20 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 09:19:20 +0700 Subject: [PATCH 124/221] some re-wording --- frame/nomination-pools/src/lib.rs | 16 ++++++++-------- frame/nomination-pools/src/tests.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b18076a6b9781..6b622d2ad46f6 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -791,14 +791,14 @@ impl CommissionThrottle { } } -/// Pool commission throttle preferences. +/// Pool commission change rate preferences. /// -/// A commission throttle consists of 2 values; (1) the maximum allowed +/// A commission change rate consists of 2 values; (1) the maximum allowed /// commission change, and (2) the minimum amount of blocks that must elapse /// before commission updates are allowed again. /// -/// Throttle prefs need to be passed and configured together. This struct is -/// used in the `set_commission_throttle` call as well as in CommissionThrottle. +/// This struct is used in the `set_commission_throttle` call as well as in +/// CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionChangeRate { /// The maximum amount the commission can be updated by per `min_delay` period. @@ -1667,7 +1667,7 @@ pub mod pallet { /// A pool's commission throttle has been changed. PoolCommissionThrottleUpdated { pool_id: PoolId, - prefs: CommissionChangeRate, + change_rate: CommissionChangeRate, }, } @@ -2470,16 +2470,16 @@ pub mod pallet { pub fn set_commission_throttle( origin: OriginFor, pool_id: PoolId, - prefs: CommissionChangeRate, + change_rate: CommissionChangeRate, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_throttle(prefs)?; + bonded_pool.commission.maybe_update_throttle(change_rate)?; bonded_pool.put(); - Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, prefs }); + Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, change_rate }); Ok(()) } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 198b2f3d03182..8806f7b7d882c 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -481,7 +481,7 @@ mod bonded_pool { }, Event::PoolCommissionThrottleUpdated { pool_id: 1, - prefs: CommissionChangeRate { + change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2 } From df2d07dfe6beb1c99dec79e55d9325db9738b9d1 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 13:00:06 +0700 Subject: [PATCH 125/221] enforce global max commission + tests --- frame/nomination-pools/fuzzer/src/call.rs | 2 +- frame/nomination-pools/src/lib.rs | 24 ++- frame/nomination-pools/src/mock.rs | 2 +- frame/nomination-pools/src/tests.rs | 151 ++++++++++++++++-- .../nomination-pools/test-staking/src/mock.rs | 2 +- 5 files changed, 158 insertions(+), 23 deletions(-) diff --git a/frame/nomination-pools/fuzzer/src/call.rs b/frame/nomination-pools/fuzzer/src/call.rs index 307a647aebcb8..121271bb67f6e 100644 --- a/frame/nomination-pools/fuzzer/src/call.rs +++ b/frame/nomination-pools/fuzzer/src/call.rs @@ -224,7 +224,7 @@ fn main() { MaxPoolMembers::::set(Some(10_000)); MaxPoolMembersPerPool::::set(Some(1000)); MaxPools::::set(Some(1_000)); - GlobalMaxCommission::::set(Some(Perbill::from_percent(50))); + GlobalMaxCommission::::set(Some(Perbill::from_percent(90))); MinCreateBond::::set(10 * ExistentialDeposit::get()); MinJoinBond::::set(5 * ExistentialDeposit::get()); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6b622d2ad46f6..c004fadc9a5af 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -671,16 +671,20 @@ impl Commission { /// Set the pool's commission. /// - /// Update commission accordingly based on `commission` and `payee`. If - /// throttle is present, record the current block as the previously updated - /// commission. If the supplied commission is zero, `None` will be inserted - /// and `payee` will be ignored. + /// Update commission based on `commission` and `payee`. Do not allow a + /// commission above global maximum if set. If throttle is present, record the + /// current block as the previously updated commission. If the supplied + /// commission is zero, `None` will be inserted and `payee` will be ignored. fn maybe_update_current( &mut self, commission: &Perbill, payee: T::AccountId, ) -> DispatchResult { ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!( + GlobalMaxCommission::::get().map_or(true, |m| commission <= &m), + Error::::GlobalMaxCommissionExceeded + ); ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); self.current = Some((*commission, payee)); @@ -695,13 +699,17 @@ impl Commission { /// Set the pool's maximum commission. /// - /// The pool's maximum commission can be set to any value initially, and only - /// smaller values thereafter. If larger values are attempted, this function - /// will return a dispatch error. + /// The pool's maximum commission can initially be set to any value, below + /// global maximum if set, and only smaller values thereafter. If larger + /// values are attempted, this function will return a dispatch error. /// /// If `current.0` is larger than an updated max commission value, then /// `current.0` will also be updated to the new maximum. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { + ensure!( + GlobalMaxCommission::::get().map_or(true, |m| new_max <= m), + Error::::GlobalMaxCommissionExceeded + ); if let Some(old) = self.max.as_mut() { if new_max > *old { return Err(Error::::MaxCommissionRestricted.into()) @@ -1728,6 +1736,8 @@ pub mod pallet { NoCommissionSet, /// No account has been set to receive commission. NoCommissionPayeeSet, + /// The supplied commission exceeds the global maximum commission. + GlobalMaxCommissionExceeded, /// The pool's max commission cannot be set higher than the existing value. MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. diff --git a/frame/nomination-pools/src/mock.rs b/frame/nomination-pools/src/mock.rs index 4230bb1a570d2..e779cae67814c 100644 --- a/frame/nomination-pools/src/mock.rs +++ b/frame/nomination-pools/src/mock.rs @@ -260,7 +260,7 @@ impl Default for ExtBuilder { members: Default::default(), max_members: Some(4), max_members_per_pool: Some(3), - global_max_commission: Some(Perbill::from_percent(50)), + global_max_commission: Some(Perbill::from_percent(90)), } } } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 8806f7b7d882c..8cf2199ed64b1 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -299,6 +299,94 @@ mod bonded_pool { }); } + #[test] + fn global_max_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Remove 90% global maximum commission + GlobalMaxCommission::::set(None); + + // With global maximum commission removed, we can now set a 100% + // commission. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(100)), + Some(900) + )); + + // Set a more restrictive commission of 95% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(95)), + Some(900) + )); + + // Bring back global max commission, this time of 80% + GlobalMaxCommission::::set(Some(Perbill::from_percent(80))); + + // Attempting to set a commission of 85% will now fail, even though it is + // more restrictive than the current commission of 95% + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(85)), + Some(900) + ), + Error::::GlobalMaxCommissionExceeded + ); + + // Set a max commission now of 86%. Fails - above global. + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(86)), + Error::::GlobalMaxCommissionExceeded + ); + + // Succesfully set max commission of 75% + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(75) + )); + + // Succesfully update commission to 75% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(75)), + Some(900) + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(100), + payee: 900 + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(95), + payee: 900 + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(75) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + commission: Perbill::from_percent(75), + payee: 900 + } + ] + ); + }); + } + #[test] fn set_commission_handles_errors() { ExtBuilder::default().build_and_execute(|| { @@ -338,6 +426,18 @@ mod bonded_pool { Error::::NoCommissionSet ); + // We attempt to increase the commission to 100%, which is disallowed due + // to global max commission of 90%. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some(Perbill::from_percent(100)), + Some(900) + ), + Error::::GlobalMaxCommissionExceeded + ); + // Set the initial commission to 5%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -445,7 +545,8 @@ mod bonded_pool { Error::::CommissionChangeThrottled ); - // We will now set a max commission to the current 2% + // We will now set a max commission to the current 2%. This will also + // update commission.current to 2%. assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, @@ -523,29 +624,49 @@ mod bonded_pool { Error::::DoesNotHavePermission ); - // Set a max commission commission pool 1 to 90% + // We attempt to increase the max commission to 100%, but increasing is + // disallowed due to global max commission. + assert_noop!( + Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(100) + ), + Error::::GlobalMaxCommissionExceeded + ); + + // Remove global maximum commission + GlobalMaxCommission::::set(None); + + // With global maximum commission removed, we can now set a 100% + // commission. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(100) + )); + + // Set a max commission commission pool 1 to 80% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(90) + Perbill::from_percent(80) )); assert_eq!( BondedPools::::get(1).unwrap().commission.max, - Some(Perbill::from_percent(90)) + Some(Perbill::from_percent(80)) ); - // We attempt to increase the max commission to 100%, but increasing is disallowed. + // We attempt to increase the max commission to 90%, but increasing is + // disallowed due to pool's max commission. assert_noop!( - Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(100) - ), + Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)), Error::::MaxCommissionRestricted ); - // We will now set a commission to 75% and then amend the max commission to 50%. - // The max commission change should decrease the current commission to 50%. + // We will now set a commission to 75% and then amend the max commission + // to 50%. The max commission change should decrease the current + // commission to 50%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, @@ -574,7 +695,11 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolMaxCommissionUpdated { pool_id: 1, - max_commission: Perbill::from_percent(90) + max_commission: Perbill::from_percent(100) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(80) }, Event::PoolCommissionUpdated { pool_id: 1, diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index 4b87e82d92c16..e1b9cc981463a 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -209,7 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { max_pools: Some(3), max_members_per_pool: Some(5), max_members: Some(3 * 5), - global_max_commission: Some(Perbill::from_percent(50)), + global_max_commission: Some(Perbill::from_percent(90)), } .assimilate_storage(&mut storage) .unwrap(); From b142637b43e5ca9cecf205a839c404c3e564952b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sat, 24 Dec 2022 13:10:59 +0700 Subject: [PATCH 126/221] re-run benchmarks --- frame/nomination-pools/src/weights.rs | 196 +++++++++++++------------- 1 file changed, 101 insertions(+), 95 deletions(-) diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index a94374fc3a83f..9a5b028121ebb 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-24, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `192.168.1.33`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 167_000 nanoseconds. - Weight::from_ref_time(170_000_000) + // Minimum execution time: 146_000 nanoseconds. + Weight::from_ref_time(202_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 163_000 nanoseconds. - Weight::from_ref_time(164_000_000) + // Minimum execution time: 138_000 nanoseconds. + Weight::from_ref_time(192_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 242_000 nanoseconds. - Weight::from_ref_time(247_000_000) + // Minimum execution time: 165_000 nanoseconds. + Weight::from_ref_time(166_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 61_000 nanoseconds. - Weight::from_ref_time(63_000_000) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(52_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(184_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(145_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -146,10 +146,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_118_829) - // Standard Error: 5_215 - .saturating_add(Weight::from_ref_time(62_099).saturating_mul(s.into())) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(53_610_228) + // Standard Error: 1_132 + .saturating_add(Weight::from_ref_time(14_777).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -164,10 +164,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 107_000 nanoseconds. - Weight::from_ref_time(114_433_516) - // Standard Error: 3_429 - .saturating_add(Weight::from_ref_time(29_129).saturating_mul(s.into())) + // Minimum execution time: 94_000 nanoseconds. + Weight::from_ref_time(96_737_116) + // Standard Error: 1_756 + .saturating_add(Weight::from_ref_time(29_702).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -193,10 +193,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 172_000 nanoseconds. - Weight::from_ref_time(179_866_176) - // Standard Error: 3_909 - .saturating_add(Weight::from_ref_time(9_931).saturating_mul(s.into())) + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(150_336_111) + // Standard Error: 5_051 + .saturating_add(Weight::from_ref_time(69_545).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -222,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 155_000 nanoseconds. - Weight::from_ref_time(158_000_000) + // Minimum execution time: 132_000 nanoseconds. + Weight::from_ref_time(138_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -241,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 69_000 nanoseconds. - Weight::from_ref_time(69_153_832) - // Standard Error: 14_591 - .saturating_add(Weight::from_ref_time(1_534_185).saturating_mul(n.into())) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(59_991_725) + // Standard Error: 19_231 + .saturating_add(Weight::from_ref_time(1_360_632).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -253,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 40_000 nanoseconds. - Weight::from_ref_time(41_000_000) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(36_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -263,8 +263,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(_n: u32, ) -> Weight { - // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_899_205) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_880_959) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -272,16 +272,17 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MaxPoolMembers (r:0 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) // Storage: NominationPools MinCreateBond (r:0 w:1) + // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 8_000 nanoseconds. - Weight::from_ref_time(8_000_000) - .saturating_add(T::DbWeight::get().writes(5)) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(27_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -295,29 +296,31 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 67_000 nanoseconds. - Weight::from_ref_time(71_000_000) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(59_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(27_000_000) - .saturating_add(T::DbWeight::get().reads(1)) + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission_max() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(T::DbWeight::get().reads(1)) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(26_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -339,8 +342,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 167_000 nanoseconds. - Weight::from_ref_time(170_000_000) + // Minimum execution time: 146_000 nanoseconds. + Weight::from_ref_time(202_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -354,8 +357,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 163_000 nanoseconds. - Weight::from_ref_time(164_000_000) + // Minimum execution time: 138_000 nanoseconds. + Weight::from_ref_time(192_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -369,8 +372,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 242_000 nanoseconds. - Weight::from_ref_time(247_000_000) + // Minimum execution time: 165_000 nanoseconds. + Weight::from_ref_time(166_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -379,8 +382,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 61_000 nanoseconds. - Weight::from_ref_time(63_000_000) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(52_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -399,8 +402,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(184_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(145_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -411,10 +414,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(62_118_829) - // Standard Error: 5_215 - .saturating_add(Weight::from_ref_time(62_099).saturating_mul(s.into())) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(53_610_228) + // Standard Error: 1_132 + .saturating_add(Weight::from_ref_time(14_777).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -429,10 +432,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 107_000 nanoseconds. - Weight::from_ref_time(114_433_516) - // Standard Error: 3_429 - .saturating_add(Weight::from_ref_time(29_129).saturating_mul(s.into())) + // Minimum execution time: 94_000 nanoseconds. + Weight::from_ref_time(96_737_116) + // Standard Error: 1_756 + .saturating_add(Weight::from_ref_time(29_702).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -458,10 +461,10 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 172_000 nanoseconds. - Weight::from_ref_time(179_866_176) - // Standard Error: 3_909 - .saturating_add(Weight::from_ref_time(9_931).saturating_mul(s.into())) + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(150_336_111) + // Standard Error: 5_051 + .saturating_add(Weight::from_ref_time(69_545).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -487,8 +490,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 155_000 nanoseconds. - Weight::from_ref_time(158_000_000) + // Minimum execution time: 132_000 nanoseconds. + Weight::from_ref_time(138_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -506,10 +509,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 69_000 nanoseconds. - Weight::from_ref_time(69_153_832) - // Standard Error: 14_591 - .saturating_add(Weight::from_ref_time(1_534_185).saturating_mul(n.into())) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(59_991_725) + // Standard Error: 19_231 + .saturating_add(Weight::from_ref_time(1_360_632).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -518,8 +521,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 40_000 nanoseconds. - Weight::from_ref_time(41_000_000) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(36_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -528,8 +531,8 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(_n: u32, ) -> Weight { - // Minimum execution time: 14_000 nanoseconds. - Weight::from_ref_time(15_899_205) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_880_959) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -537,16 +540,17 @@ impl WeightInfo for () { // Storage: NominationPools MaxPoolMembers (r:0 w:1) // Storage: NominationPools MaxPoolMembersPerPool (r:0 w:1) // Storage: NominationPools MinCreateBond (r:0 w:1) + // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 8_000 nanoseconds. - Weight::from_ref_time(8_000_000) - .saturating_add(RocksDbWeight::get().writes(5)) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(27_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -560,29 +564,31 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 67_000 nanoseconds. - Weight::from_ref_time(71_000_000) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(59_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { - // Minimum execution time: 26_000 nanoseconds. - Weight::from_ref_time(27_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) + // Minimum execution time: 23_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission_max() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(25_000_000) - .saturating_add(RocksDbWeight::get().reads(1)) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) + .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_throttle() -> Weight { - // Minimum execution time: 25_000 nanoseconds. - Weight::from_ref_time(26_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } From c6a5e95e7138ebe3248694ece5c52497aebbceee Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 10:34:43 +0700 Subject: [PATCH 127/221] initial global commission of 0% --- frame/nomination-pools/src/migration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index dae99098d42f8..b24c50f0a9e00 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -493,8 +493,8 @@ pub mod v4 { ); if current == 4 && onchain == 3 { - GlobalMaxCommission::::set(None); - log!(info, "Set initial global max commission of None"); + GlobalMaxCommission::::set(Some(Perbill::zero())); + log!(info, "Set initial global max commission to 0%"); let mut translated = 0u64; BondedPools::::translate::, _>(|_key, old_value| { From 0eb3d2de04a5835aa01ddbdae3cc19ef6f0418bc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 17:50:57 +0700 Subject: [PATCH 128/221] feedback amendments --- frame/nomination-pools/src/lib.rs | 157 ++++++++++++---------------- frame/nomination-pools/src/tests.rs | 89 ---------------- 2 files changed, 67 insertions(+), 179 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c004fadc9a5af..b4d870b3979cc 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -589,13 +589,13 @@ pub struct PoolRoles { /// values are `None`. Pool `root`s can set `max` and `throttle` configurations before setting an /// initial commission `current` value - the commission percentage and payee of commission. /// -/// The `max` commission value can only be decreased after the initial value is -/// set, to prevent commission from repeatedly increasing. +/// The `max` commission value can only be decreased after the initial value is set, to prevent +/// commission from repeatedly increasing. /// -/// An optional commission throttle allows the pool to set strict limits to how much -/// commission can change in each update, and how often updates can take place. -/// If a `throttle` is set before a commission `current` is set, the initial commission `current` -/// value will not be subject to throttling. Subsequent commission updates will be. +/// An optional commission throttle allows the pool to set strict limits to how much commission can +/// change in each update, and how often updates can take place. If a `throttle` is set before a +/// commission `current` is set, the initial commission `current` value will not be subject to +/// throttling. Subsequent commission updates will be. #[derive( Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, )] @@ -604,8 +604,8 @@ pub struct PoolRoles { pub struct Commission { /// Optional commission rate of the pool along with the account commission is paid to. pub current: Option<(Perbill, T::AccountId)>, - /// Optional maximum commission that can be set by the pool `root`. Once set, this value - /// can only be updated to a decreased value. + /// Optional maximum commission that can be set by the pool `root`. Once set, this value can only + /// be updated to a decreased value. pub max: Option, /// Optional configuration around how often commission can be updated, and when the last /// commission update took place. @@ -631,8 +631,8 @@ impl Commission { if let Some(t) = self.throttle.as_ref() { // factor previously updated block into whether user is throttled. if t.previous_set_at.map_or( - // if no `previous_set_at` is set, throttled if the attempted increase - // in commission is greater than `max_increase`. + // if no `previous_set_at` is set, throttled if the attempted increase in commission is + // greater than `max_increase`. (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase, |p| { // `min_delay` blocks must have been surpassed since last update. @@ -641,25 +641,31 @@ impl Commission { { return true } - // ensure the `max_increase` durations surpassed since the previous - // commission update allow the attempted commission increase. + // ensure the `max_increase` durations surpassed since the previous commission update + // allow the attempted commission increase. // // the attempted increase in commission relative to the current commission. let attempted_increase = (*to).saturating_sub(self.commission_or_zero()); // the total durations passed since the last commission update. - let intervals_passed = self.intervals_since_block(&p, &t.change_rate.min_delay); + let blocks_passed = >::block_number().saturating_sub(p); - // the maximum allowed increase, where the current `max_increase` - // Perbill is converted into a u32 by multiplying itself with 100_u32 - // and multiplied by durations passed, before being converted back - // into a Perbill. + // calculate intervals passed since last `previous_set_at` + let intervals_passed = if blocks_passed == Zero::zero() { + Zero::zero() + } else { + blocks_passed.div(t.change_rate.min_delay).saturated_into::() + }; + + // the maximum allowed increase, where the current `max_increase` Perbill is converted + // into a u32 by multiplying itself with 100_u32 and multiplied by durations passed, + // before being converted back into a Perbill. let max_allowed_increase = Perbill::from_percent( (t.change_rate.max_increase * 100_u32) * intervals_passed, ); - // throttled (true) if attempted increase is greater than the maximum - // allowed increase, as a percentage. + // throttled (true) if attempted increase is greater than the maximum allowed increase, as + // a percentage. attempted_increase > max_allowed_increase }, ) { @@ -671,11 +677,11 @@ impl Commission { /// Set the pool's commission. /// - /// Update commission based on `commission` and `payee`. Do not allow a - /// commission above global maximum if set. If throttle is present, record the - /// current block as the previously updated commission. If the supplied - /// commission is zero, `None` will be inserted and `payee` will be ignored. - fn maybe_update_current( + /// Update commission based on `commission` and `payee`. Do not allow a commission above global + /// maximum if set. If throttle is present, record the current block as the previously updated + /// commission. If the supplied commission is zero, `None` will be inserted and `payee` will be + /// ignored. + fn update_current( &mut self, commission: &Perbill, payee: T::AccountId, @@ -699,17 +705,13 @@ impl Commission { /// Set the pool's maximum commission. /// - /// The pool's maximum commission can initially be set to any value, below - /// global maximum if set, and only smaller values thereafter. If larger - /// values are attempted, this function will return a dispatch error. + /// The pool's maximum commission can initially be set to any value, below global maximum if set, + /// and only smaller values thereafter. If larger values are attempted, this function will return + /// a dispatch error. /// - /// If `current.0` is larger than an updated max commission value, then - /// `current.0` will also be updated to the new maximum. + /// If `current.0` is larger than an updated max commission value, then `current.0` will also be + /// updated to the new maximum. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { - ensure!( - GlobalMaxCommission::::get().map_or(true, |m| new_max <= m), - Error::::GlobalMaxCommissionExceeded - ); if let Some(old) = self.max.as_mut() { if new_max > *old { return Err(Error::::MaxCommissionRestricted.into()) @@ -724,9 +726,9 @@ impl Commission { /// Set the pool's commission throttle settings. /// - /// Once a throttle configuration has been set, only more restrictive values - /// can be set thereafter. These restrictions translate to increased - /// `min_delay` values and decreased `max_increase` values. + /// Once a throttle configuration has been set, only more restrictive values can be set + /// thereafter. These restrictions translate to increased `min_delay` values and decreased + /// `max_increase` values. fn maybe_update_throttle( &mut self, change_rate: CommissionChangeRate, @@ -746,8 +748,8 @@ impl Commission { /// Gets the current commission (if any) and payee to be paid. /// - /// A zero commission along with a `None` payee is returned in the event a - /// commission has not been configured to the pool. + /// A zero commission along with a `None` payee is returned in the event a commission has not been + /// configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -755,24 +757,6 @@ impl Commission { self.current .as_ref() .map(|(commission, payee)| (*commission * *pending_rewards, payee.clone())) - .or(None) - } - - /// Calculate the amount of intervals that consume a period of blocks. - /// - /// The final number of intervals is returned. - pub fn intervals_since_block( - &self, - start_block: &T::BlockNumber, - interval: &T::BlockNumber, - ) -> u32 { - let blocks_passed = >::block_number().saturating_sub(*start_block); - - // ensure divsion by zero is not possible. - if blocks_passed == Zero::zero() { - return Zero::zero() - } - blocks_passed.div(*interval).saturated_into::() } } @@ -801,12 +785,10 @@ impl CommissionThrottle { /// Pool commission change rate preferences. /// -/// A commission change rate consists of 2 values; (1) the maximum allowed -/// commission change, and (2) the minimum amount of blocks that must elapse -/// before commission updates are allowed again. +/// A commission change rate consists of 2 values; (1) the maximum allowed commission change, and +/// (2) the minimum amount of blocks that must elapse before commission updates are allowed again. /// -/// This struct is used in the `set_commission_throttle` call as well as in -/// CommissionThrottle. +/// This struct is used in the `set_commission_throttle` call as well as in CommissionThrottle. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionChangeRate { /// The maximum amount the commission can be updated by per `min_delay` period. @@ -1532,14 +1514,13 @@ pub mod pallet { pub type BondedPools = CountedStorageMap<_, Twox64Concat, PoolId, BondedPoolInner>; - /// Reward pools. This is where there rewards for each pool accumulate. When a - /// members payout is claimed, the balance comes out fo the reward pool. Keyed - /// by the bonded pools account. + /// Reward pools. This is where there rewards for each pool accumulate. When a members payout is + /// claimed, the balance comes out fo the reward pool. Keyed by the bonded pools account. #[pallet::storage] pub type RewardPools = CountedStorageMap<_, Twox64Concat, PoolId, RewardPool>; - /// Groups of unbonding pools. Each group of unbonding pools belongs to a bonded pool, - /// hence the name sub-pools. Keyed by the bonded pools account. + /// Groups of unbonding pools. Each group of unbonding pools belongs to a + /// bonded pool, hence the name sub-pools. Keyed by the bonded pools account. #[pallet::storage] pub type SubPoolsStorage = CountedStorageMap<_, Twox64Concat, PoolId, SubPools>; @@ -1554,8 +1535,8 @@ pub mod pallet { /// A reverse lookup from the pool's account id to its id. /// - /// This is only used for slashing. In all other instances, the pool id is used, and the - /// accounts are deterministically derived from it. + /// This is only used for slashing. In all other instances, the pool id is used, and the accounts + /// are deterministically derived from it. #[pallet::storage] pub type ReversePoolIdLookup = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolId, OptionQuery>; @@ -2400,15 +2381,13 @@ pub mod pallet { /// Set the commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If the pool has a max commission set, the commission supplied - /// must be less or equal to that value. + /// The dispatch origin of this call must be signed by the `root` role of the pool. If the pool + /// has a max commission set, the commission supplied must be less or equal to that value. /// - /// If the max commission has not yet been set, then the commission range is - /// not bounded. A `payee` must be provided if commission has not yet been - /// set (still `None`). Once commission has been set, the `payee` can be - /// omitted in further calls. If a `payee` update is desired, the commission - /// must still be passed into the call. + /// If the max commission has not yet been set, then the commission range is not bounded. A + /// `payee` must be provided if commission has not yet been set (still `None`). Once commission + /// has been set, the `payee` can be omitted in further calls. If a `payee` update is desired, + /// the commission must still be passed into the call. #[pallet::call_index(14)] #[pallet::weight(T::WeightInfo::set_commission())] pub fn set_commission( @@ -2431,7 +2410,7 @@ pub mod pallet { bonded_pool .commission - .maybe_update_current(&final_commission, final_payee.clone())?; + .update_current(&final_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, @@ -2443,13 +2422,12 @@ pub mod pallet { /// Set the maximum commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If a maximum commission already exists prior to this call, - /// then the updated max commission must be lower, otherwise this call will - /// fail. + /// The dispatch origin of this call must be signed by the `root` role of the pool. If a maximum + /// commission already exists prior to this call, then the updated max commission must be lower, + /// otherwise this call will fail. /// - /// This call also updates the pool's current commission to the new maximum - /// if the current commission is higher than the maximum supplied. + /// This call also updates the pool's current commission to the new maximum if the current + /// commission is higher than the maximum supplied. #[pallet::call_index(15)] #[pallet::weight(T::WeightInfo::set_commission_max())] pub fn set_commission_max( @@ -2469,12 +2447,11 @@ pub mod pallet { /// Set the commission throttle for a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of - /// the pool. If a throttle is already present, this call will only succeed - /// if a more restrictive throttle configuration is given. + /// The dispatch origin of this call must be signed by the `root` role of the pool. If a + /// throttle is already present, this call will only succeed if a more restrictive throttle + /// configuration is given. /// - /// If a throttle configuration does not yet exist, the provided values are - /// set. + /// If a throttle configuration does not yet exist, the provided values are set. #[pallet::call_index(16)] #[pallet::weight(T::WeightInfo::set_commission_throttle())] pub fn set_commission_throttle( @@ -2540,8 +2517,8 @@ impl Pallet { /// It is the responsibility of the depositor to put these funds into the pool initially. Upon /// unbond, they can never unbond to a value below this amount. /// - /// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former - /// is coming from the staking pallet and the latter two are configured in this pallet. + /// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former is + /// coming from the staking pallet and the latter two are configured in this pallet. pub fn depositor_min_bond() -> BalanceOf { T::Staking::minimum_nominator_bond() .max(MinCreateBond::::get()) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 8cf2199ed64b1..8f96014965803 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -337,12 +337,6 @@ mod bonded_pool { Error::::GlobalMaxCommissionExceeded ); - // Set a max commission now of 86%. Fails - above global. - assert_noop!( - Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(86)), - Error::::GlobalMaxCommissionExceeded - ); - // Succesfully set max commission of 75% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), @@ -624,28 +618,9 @@ mod bonded_pool { Error::::DoesNotHavePermission ); - // We attempt to increase the max commission to 100%, but increasing is - // disallowed due to global max commission. - assert_noop!( - Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(100) - ), - Error::::GlobalMaxCommissionExceeded - ); - // Remove global maximum commission GlobalMaxCommission::::set(None); - // With global maximum commission removed, we can now set a 100% - // commission. - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(100) - )); - // Set a max commission commission pool 1 to 80% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), @@ -693,10 +668,6 @@ mod bonded_pool { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(100) - }, Event::PoolMaxCommissionUpdated { pool_id: 1, max_commission: Perbill::from_percent(80) @@ -879,66 +850,6 @@ mod bonded_pool { )); }); } - - #[test] - fn intervals_since_block_works() { - ExtBuilder::default().build_and_execute(|| { - // some duration to work with - let duration = 3_u64; - - // get commission to access `intervals_since_block` - let commission = BondedPool::::get(1).unwrap().commission; - - // run 5 durations in the future - let block_number = duration * 5; - run_to_block(block_number); - - // interval of 14 blocks should result in 4 durations - assert_eq!(commission.intervals_since_block(&1, &duration), 4_u32); - - // interval of 13 blocks should result in 4 durations - assert_eq!(commission.intervals_since_block(&2, &duration), 4_u32); - - // interval of 12 blocks should result in 4 durations - assert_eq!(commission.intervals_since_block(&3, &duration), 4_u32); - - // interval of 11 blocks should result in 3 durations - assert_eq!(commission.intervals_since_block(&4, &duration), 3_u32); - - // interval of 10 blocks should result in 3 durations - assert_eq!(commission.intervals_since_block(&5, &duration), 3_u32); - - // interval of 9 blocks should result in 3 durations - assert_eq!(commission.intervals_since_block(&6, &duration), 3_u32); - - // interval of 8 blocks should result in 2 durations - assert_eq!(commission.intervals_since_block(&7, &duration), 2_u32); - - // interval of 7 blocks should result in 2 duration - assert_eq!(commission.intervals_since_block(&8, &duration), 2_u32); - - // interval of 6 blocks should result in 2 durations - assert_eq!(commission.intervals_since_block(&9, &duration), 2_u32); - - // interval of 5 blocks should result in 1 duration - assert_eq!(commission.intervals_since_block(&10, &duration), 1_u32); - - // interval of 4 blocks should result in 1 duration - assert_eq!(commission.intervals_since_block(&11, &duration), 1_u32); - - // interval of 3 blocks should result in 1 duration - assert_eq!(commission.intervals_since_block(&12, &duration), 1_u32); - - // interval of 2 blocks should result in 0 durations - assert_eq!(commission.intervals_since_block(&13, &duration), 0_u32); - - // interval of 1 block should result in 0 durations - assert_eq!(commission.intervals_since_block(&14, &duration), 0_u32); - - // interval of 0 block should result in 0 durations - assert_eq!(commission.intervals_since_block(&15, &duration), 0_u32); - }) - } } mod reward_pool { From 4c008af09ba8c11d8edf8e4d433333522aa5f91b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 17:51:15 +0700 Subject: [PATCH 129/221] fmt --- frame/nomination-pools/src/lib.rs | 69 ++++++++++++++----------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b4d870b3979cc..0269a4fb6231c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -604,8 +604,8 @@ pub struct PoolRoles { pub struct Commission { /// Optional commission rate of the pool along with the account commission is paid to. pub current: Option<(Perbill, T::AccountId)>, - /// Optional maximum commission that can be set by the pool `root`. Once set, this value can only - /// be updated to a decreased value. + /// Optional maximum commission that can be set by the pool `root`. Once set, this value can + /// only be updated to a decreased value. pub max: Option, /// Optional configuration around how often commission can be updated, and when the last /// commission update took place. @@ -631,8 +631,8 @@ impl Commission { if let Some(t) = self.throttle.as_ref() { // factor previously updated block into whether user is throttled. if t.previous_set_at.map_or( - // if no `previous_set_at` is set, throttled if the attempted increase in commission is - // greater than `max_increase`. + // if no `previous_set_at` is set, throttled if the attempted increase in + // commission is greater than `max_increase`. (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase, |p| { // `min_delay` blocks must have been surpassed since last update. @@ -641,8 +641,8 @@ impl Commission { { return true } - // ensure the `max_increase` durations surpassed since the previous commission update - // allow the attempted commission increase. + // ensure the `max_increase` durations surpassed since the previous commission + // update allow the attempted commission increase. // // the attempted increase in commission relative to the current commission. let attempted_increase = (*to).saturating_sub(self.commission_or_zero()); @@ -656,16 +656,16 @@ impl Commission { } else { blocks_passed.div(t.change_rate.min_delay).saturated_into::() }; - - // the maximum allowed increase, where the current `max_increase` Perbill is converted - // into a u32 by multiplying itself with 100_u32 and multiplied by durations passed, - // before being converted back into a Perbill. + + // the maximum allowed increase, where the current `max_increase` Perbill is + // converted into a u32 by multiplying itself with 100_u32 and multiplied by + // durations passed, before being converted back into a Perbill. let max_allowed_increase = Perbill::from_percent( (t.change_rate.max_increase * 100_u32) * intervals_passed, ); - // throttled (true) if attempted increase is greater than the maximum allowed increase, as - // a percentage. + // throttled (true) if attempted increase is greater than the maximum allowed + // increase, as a percentage. attempted_increase > max_allowed_increase }, ) { @@ -681,11 +681,7 @@ impl Commission { /// maximum if set. If throttle is present, record the current block as the previously updated /// commission. If the supplied commission is zero, `None` will be inserted and `payee` will be /// ignored. - fn update_current( - &mut self, - commission: &Perbill, - payee: T::AccountId, - ) -> DispatchResult { + fn update_current(&mut self, commission: &Perbill, payee: T::AccountId) -> DispatchResult { ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); ensure!( GlobalMaxCommission::::get().map_or(true, |m| commission <= &m), @@ -705,9 +701,9 @@ impl Commission { /// Set the pool's maximum commission. /// - /// The pool's maximum commission can initially be set to any value, below global maximum if set, - /// and only smaller values thereafter. If larger values are attempted, this function will return - /// a dispatch error. + /// The pool's maximum commission can initially be set to any value, below global maximum if + /// set, and only smaller values thereafter. If larger values are attempted, this function will + /// return a dispatch error. /// /// If `current.0` is larger than an updated max commission value, then `current.0` will also be /// updated to the new maximum. @@ -748,8 +744,8 @@ impl Commission { /// Gets the current commission (if any) and payee to be paid. /// - /// A zero commission along with a `None` payee is returned in the event a commission has not been - /// configured to the pool. + /// A zero commission along with a `None` payee is returned in the event a commission has not + /// been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -1535,8 +1531,8 @@ pub mod pallet { /// A reverse lookup from the pool's account id to its id. /// - /// This is only used for slashing. In all other instances, the pool id is used, and the accounts - /// are deterministically derived from it. + /// This is only used for slashing. In all other instances, the pool id is used, and the + /// accounts are deterministically derived from it. #[pallet::storage] pub type ReversePoolIdLookup = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolId, OptionQuery>; @@ -2381,13 +2377,14 @@ pub mod pallet { /// Set the commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. If the pool - /// has a max commission set, the commission supplied must be less or equal to that value. + /// The dispatch origin of this call must be signed by the `root` role of the pool. If the + /// pool has a max commission set, the commission supplied must be less or equal to that + /// value. /// /// If the max commission has not yet been set, then the commission range is not bounded. A - /// `payee` must be provided if commission has not yet been set (still `None`). Once commission - /// has been set, the `payee` can be omitted in further calls. If a `payee` update is desired, - /// the commission must still be passed into the call. + /// `payee` must be provided if commission has not yet been set (still `None`). Once + /// commission has been set, the `payee` can be omitted in further calls. If a `payee` + /// update is desired, the commission must still be passed into the call. #[pallet::call_index(14)] #[pallet::weight(T::WeightInfo::set_commission())] pub fn set_commission( @@ -2408,9 +2405,7 @@ pub mod pallet { .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) .ok_or(Error::::NoCommissionPayeeSet)?; - bonded_pool - .commission - .update_current(&final_commission, final_payee.clone())?; + bonded_pool.commission.update_current(&final_commission, final_payee.clone())?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, @@ -2422,9 +2417,9 @@ pub mod pallet { /// Set the maximum commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. If a maximum - /// commission already exists prior to this call, then the updated max commission must be lower, - /// otherwise this call will fail. + /// The dispatch origin of this call must be signed by the `root` role of the pool. If a + /// maximum commission already exists prior to this call, then the updated max commission + /// must be lower, otherwise this call will fail. /// /// This call also updates the pool's current commission to the new maximum if the current /// commission is higher than the maximum supplied. @@ -2517,8 +2512,8 @@ impl Pallet { /// It is the responsibility of the depositor to put these funds into the pool initially. Upon /// unbond, they can never unbond to a value below this amount. /// - /// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former is - /// coming from the staking pallet and the latter two are configured in this pallet. + /// It is essentially `max { MinNominatorBond, MinCreateBond, MinJoinBond }`, where the former + /// is coming from the staking pallet and the latter two are configured in this pallet. pub fn depositor_min_bond() -> BalanceOf { T::Staking::minimum_nominator_bond() .max(MinCreateBond::::get()) From 301a3d5daa97d587aafe811639d0bddadd88e215 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 17:52:42 +0700 Subject: [PATCH 130/221] + comment --- frame/nomination-pools/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 0269a4fb6231c..c47be7ee214dd 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -746,6 +746,7 @@ impl Commission { /// /// A zero commission along with a `None` payee is returned in the event a commission has not /// been configured to the pool. + /// `None` is returned in the event a commission has not been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, From 5dfa94c7cb5eff19736d8657380e2241594d8b06 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 10:58:31 +0000 Subject: [PATCH 131/221] Update frame/nomination-pools/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/nomination-pools/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c47be7ee214dd..9f7842c8db08e 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2437,6 +2437,7 @@ pub mod pallet { bonded_pool.commission.maybe_update_max(max_commission)?; bonded_pool.put(); + Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); Ok(()) } From 9ccc4f1878bcbc1ed148aaf60a673f388e4a5118 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Sun, 25 Dec 2022 18:11:40 +0700 Subject: [PATCH 132/221] fmt --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 9f7842c8db08e..a03e682e904de 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -746,7 +746,7 @@ impl Commission { /// /// A zero commission along with a `None` payee is returned in the event a commission has not /// been configured to the pool. - /// `None` is returned in the event a commission has not been configured to the pool. + /// `None` is returned in the event a commission has not been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, From 67810eabc695e8fb951a950279fed8d56311d521 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 18:00:24 +0700 Subject: [PATCH 133/221] simplify set_commission + allow rm current --- .../nomination-pools/benchmarking/src/lib.rs | 3 +- frame/nomination-pools/src/lib.rs | 55 ++++--- frame/nomination-pools/src/tests.rs | 135 ++++++------------ 3 files changed, 72 insertions(+), 121 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index cbf072bf235f2..1e3a02a462b6e 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -94,8 +94,7 @@ fn create_pool_account( Pools::::set_commission( RuntimeOrigin::Signed(pool_creator.clone()).into(), pool_id, - Some(c), - Some(pool_creator.clone()), + Some((c, pool_creator.clone())), ) .expect("pool commission has been set"); }); diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a03e682e904de..3974cc46f5d6d 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -677,19 +677,29 @@ impl Commission { /// Set the pool's commission. /// - /// Update commission based on `commission` and `payee`. Do not allow a commission above global - /// maximum if set. If throttle is present, record the current block as the previously updated - /// commission. If the supplied commission is zero, `None` will be inserted and `payee` will be - /// ignored. - fn update_current(&mut self, commission: &Perbill, payee: T::AccountId) -> DispatchResult { - ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); - ensure!( - GlobalMaxCommission::::get().map_or(true, |m| commission <= &m), - Error::::GlobalMaxCommissionExceeded - ); - ensure!(self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum); + /// Update commission based on `current`. If a `None` current value is supplied, allow the + /// commission to be removed in all cases, without any throttling restrictions. Do not allow a + /// commission above global maximum if set. If throttle is present, record the current block as + /// the previously updated commission. If the supplied commission is zero, `None` will be + /// inserted and `payee` will be ignored. + fn update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { + if current.is_none() { + self.current = None; + } else { + let (commission, payee) = + current.as_ref().map(|c| c).ok_or(Error::::NoCommissionSet)?; - self.current = Some((*commission, payee)); + ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!( + GlobalMaxCommission::::get().map_or(true, |g| commission <= &g), + Error::::GlobalMaxCommissionExceeded + ); + ensure!( + self.max.map_or(true, |m| commission <= &m), + Error::::CommissionExceedsMaximum + ); + self.current = Some((*commission, payee.clone())); + } // update `throttle.previous_set_at` if a throttle has been configured. let _ = self @@ -1647,7 +1657,7 @@ pub mod pallet { /// The unbond pool at `era` of pool `pool_id` has been slashed to `balance`. UnbondingPoolSlashed { pool_id: PoolId, era: EraIndex, balance: BalanceOf }, /// A pool's commission setting has been changed. - PoolCommissionUpdated { pool_id: PoolId, commission: Perbill, payee: T::AccountId }, + PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> }, /// A pool's maximum commission setting has been changed. PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, /// A pool's commission throttle has been changed. @@ -2391,28 +2401,15 @@ pub mod pallet { pub fn set_commission( origin: OriginFor, pool_id: PoolId, - commission: Option, - payee: Option, + current: Option<(Perbill, T::AccountId)>, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - let final_commission = commission - .or(bonded_pool.commission.current.as_ref().map(|(c, _)| c).cloned()) - .ok_or(Error::::NoCommissionSet)?; - - let final_payee = payee - .or(bonded_pool.commission.current.as_ref().map(|(_, p)| p).cloned()) - .ok_or(Error::::NoCommissionPayeeSet)?; - - bonded_pool.commission.update_current(&final_commission, final_payee.clone())?; + bonded_pool.commission.update_current(¤t)?; bonded_pool.put(); - Self::deposit_event(Event::::PoolCommissionUpdated { - pool_id, - commission: final_commission, - payee: final_payee, - }); + Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, current }); Ok(()) } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 8f96014965803..02c885fd1e24e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -247,31 +247,32 @@ mod bonded_pool { #[test] fn set_commission_works() { ExtBuilder::default().build_and_execute(|| { - // Set a commission pool 1 + // Set a commission for pool 1. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(50)), - Some(900) + Some((Perbill::from_percent(50), 900)) )); let commission = BondedPool::::get(1).unwrap().commission; assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); - // Set a new payee only - assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None, Some(901))); - let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.current, Some((Perbill::from_percent(50), 901))); + // update commission only. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(25), 900)) + )); - // Set a new commission only + // update payee only. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(25)), - None + Some((Perbill::from_percent(25), 901)) )); - let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.current, Some((Perbill::from_percent(25), 901))); + + // remove the commission for pool 1. + assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None)); // Commission change events triggered successfully assert_eq!( @@ -281,19 +282,17 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(50), - payee: 900 + current: Some((Perbill::from_percent(50), 900)) }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(50), - payee: 901 + current: Some((Perbill::from_percent(25), 900)) }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(25), - payee: 901 + current: Some((Perbill::from_percent(25), 901)) }, + Event::PoolCommissionUpdated { pool_id: 1, current: None } ] ); }); @@ -310,16 +309,14 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(100)), - Some(900) + Some((Perbill::from_percent(100), 900)) )); // Set a more restrictive commission of 95% assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(95)), - Some(900) + Some((Perbill::from_percent(95), 900)) )); // Bring back global max commission, this time of 80% @@ -331,8 +328,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(85)), - Some(900) + Some((Perbill::from_percent(85), 900)) ), Error::::GlobalMaxCommissionExceeded ); @@ -348,8 +344,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(75)), - Some(900) + Some((Perbill::from_percent(75), 900)) )); assert_eq!( @@ -359,13 +354,11 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(100), - payee: 900 + current: Some((Perbill::from_percent(100), 900)) }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(95), - payee: 900 + current: Some((Perbill::from_percent(95), 900)) }, Event::PoolMaxCommissionUpdated { pool_id: 1, @@ -373,8 +366,7 @@ mod bonded_pool { }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(75), - payee: 900 + current: Some((Perbill::from_percent(75), 900)) } ] ); @@ -389,8 +381,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 9999, - Some(Perbill::from_percent(1)), - Some(900) + Some((Perbill::from_percent(1), 900)), ), Error::::PoolNotFound ); @@ -399,26 +390,10 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(1), 1, - Some(Perbill::from_percent(5)), - Some(900) + Some((Perbill::from_percent(5), 900)), ), Error::::DoesNotHavePermission ); - // No payee has been set, and `None` is provided. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some(Perbill::from_percent(5)), - None - ), - Error::::NoCommissionPayeeSet - ); - // No commission has been set, and `None` is provided. - assert_noop!( - Pools::set_commission(RuntimeOrigin::signed(900), 1, None, Some(900)), - Error::::NoCommissionSet - ); // We attempt to increase the commission to 100%, which is disallowed due // to global max commission of 90%. @@ -426,8 +401,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(100)), - Some(900) + Some((Perbill::from_percent(100), 900)) ), Error::::GlobalMaxCommissionExceeded ); @@ -436,8 +410,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(5)), - Some(900) + Some((Perbill::from_percent(5), 900)), )); // Throttle test. We will throttle commission to be a +1% commission increase every 2 @@ -467,8 +440,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(10)), - Some(900) + Some((Perbill::from_percent(10), 900)) ), Error::::CommissionChangeThrottled ); @@ -481,8 +453,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(6)), - Some(900) + Some((Perbill::from_percent(6), 900)) )); assert_eq!( BondedPool::::get(1).unwrap().commission, @@ -506,8 +477,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(2)), - Some(900) + Some((Perbill::from_percent(2), 900)) ), Error::::CommissionChangeThrottled ); @@ -516,12 +486,10 @@ mod bonded_pool { run_blocks(2); // We can now successfully increase the commission again, to 2%. - // We no longer need to provide the payee again. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(2)), - None + Some((Perbill::from_percent(2), 900)), )); // Run 2 blocks into the future, to block 5. @@ -533,8 +501,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(4)), - None, + Some((Perbill::from_percent(4), 900)), ), Error::::CommissionChangeThrottled ); @@ -557,8 +524,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(3)), - None, + Some((Perbill::from_percent(3), 900)), ), Error::::CommissionExceedsMaximum ); @@ -571,8 +537,7 @@ mod bonded_pool { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(5), - payee: 900 + current: Some((Perbill::from_percent(5), 900)), }, Event::PoolCommissionThrottleUpdated { pool_id: 1, @@ -583,13 +548,11 @@ mod bonded_pool { }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(6), - payee: 900 + current: Some((Perbill::from_percent(6), 900)) }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(2), - payee: 900 + current: Some((Perbill::from_percent(2), 900)) }, Event::PoolMaxCommissionUpdated { pool_id: 1, @@ -645,8 +608,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(75)), - Some(900), + Some((Perbill::from_percent(75), 900)) )); assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), @@ -674,8 +636,7 @@ mod bonded_pool { }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(75), - payee: 900 + current: Some((Perbill::from_percent(75), 900)), }, Event::PoolMaxCommissionUpdated { pool_id: 1, @@ -693,8 +654,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(50)), - Some(900), + Some((Perbill::from_percent(50), 900)), )); // now set the max commission to something less than the current @@ -823,8 +783,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(1)), - Some(900), + Some((Perbill::from_percent(1), 900)), )); // Run 90 blocks into the future so we are eligible to update commission // with 3 `min_delay` durations passed. @@ -835,8 +794,7 @@ mod bonded_pool { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(5)), - Some(900), + Some((Perbill::from_percent(5), 900)) ), Error::::CommissionChangeThrottled ); @@ -845,8 +803,7 @@ mod bonded_pool { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some(Perbill::from_percent(4)), - Some(900), + Some((Perbill::from_percent(4), 900)) )); }); } @@ -1517,8 +1474,7 @@ mod claim_payout { assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, - Some(Perbill::from_percent(33)), - Some(2) + Some((Perbill::from_percent(33), 2)), )); assert_eq!( @@ -1528,8 +1484,7 @@ mod claim_payout { Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::PoolCommissionUpdated { pool_id: 1, - commission: Perbill::from_percent(33), - payee: 2 + current: Some((Perbill::from_percent(33), 2)) }, ] ); From d4db02eda9767598b99a378bd55b13bf3ac7fc49 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 18:08:25 +0700 Subject: [PATCH 134/221] + mod commission to tests --- frame/nomination-pools/src/tests.rs | 8696 ++++++++++++++------------- 1 file changed, 4350 insertions(+), 4346 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 02c885fd1e24e..823bb593a4798 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -243,940 +243,776 @@ mod bonded_pool { assert_ok!(pool.ok_to_join()); }); } +} +mod reward_pool { #[test] - fn set_commission_works() { + fn current_balance_only_counts_balance_over_existential_deposit() { + use super::*; + ExtBuilder::default().build_and_execute(|| { - // Set a commission for pool 1. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(50), 900)) - )); + let reward_account = Pools::create_reward_account(2); - let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); + // Given + assert_eq!(Balances::free_balance(&reward_account), 0); - // update commission only. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(25), 900)) - )); + // Then + assert_eq!(RewardPool::::current_balance(2), 0); - // update payee only. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(25), 901)) - )); + // Given + Balances::make_free_balance_be(&reward_account, Balances::minimum_balance()); - // remove the commission for pool 1. - assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None)); + // Then + assert_eq!(RewardPool::::current_balance(2), 0); - // Commission change events triggered successfully - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(50), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(25), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(25), 901)) - }, - Event::PoolCommissionUpdated { pool_id: 1, current: None } - ] - ); + // Given + Balances::make_free_balance_be(&reward_account, Balances::minimum_balance() + 1); + + // Then + assert_eq!(RewardPool::::current_balance(2), 1); }); } +} + +mod unbond_pool { + use super::*; #[test] - fn global_max_commission_works() { + fn points_to_issue_works() { ExtBuilder::default().build_and_execute(|| { - // Remove 90% global maximum commission - GlobalMaxCommission::::set(None); + // 1 points : 1 balance ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 100 }; + assert_eq!(unbond_pool.balance_to_point(10), 10); + assert_eq!(unbond_pool.balance_to_point(0), 0); - // With global maximum commission removed, we can now set a 100% - // commission. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(100), 900)) - )); + // 2 points : 1 balance ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 50 }; + assert_eq!(unbond_pool.balance_to_point(10), 20); - // Set a more restrictive commission of 95% - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(95), 900)) - )); + // 1 points : 2 balance ratio + let unbond_pool = UnbondPool:: { points: 50, balance: 100 }; + assert_eq!(unbond_pool.balance_to_point(10), 5); - // Bring back global max commission, this time of 80% - GlobalMaxCommission::::set(Some(Perbill::from_percent(80))); + // 100 points : 0 balance ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 0 }; + assert_eq!(unbond_pool.balance_to_point(10), 100 * 10); - // Attempting to set a commission of 85% will now fail, even though it is - // more restrictive than the current commission of 95% - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(85), 900)) - ), - Error::::GlobalMaxCommissionExceeded - ); + // 0 points : 100 balance + let unbond_pool = UnbondPool:: { points: 0, balance: 100 }; + assert_eq!(unbond_pool.balance_to_point(10), 10); - // Succesfully set max commission of 75% - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(75) - )); + // 10 points : 3 balance ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 30 }; + assert_eq!(unbond_pool.balance_to_point(10), 33); - // Succesfully update commission to 75% - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(75), 900)) - )); + // 2 points : 3 balance ratio + let unbond_pool = UnbondPool:: { points: 200, balance: 300 }; + assert_eq!(unbond_pool.balance_to_point(10), 6); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(100), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(95), 900)) - }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(75) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(75), 900)) - } - ] - ); - }); + // 4 points : 9 balance ratio + let unbond_pool = UnbondPool:: { points: 400, balance: 900 }; + assert_eq!(unbond_pool.balance_to_point(90), 40); + }) } #[test] - fn set_commission_handles_errors() { - ExtBuilder::default().build_and_execute(|| { - // Provided pool does not exist - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 9999, - Some((Perbill::from_percent(1), 900)), - ), - Error::::PoolNotFound - ); - // Sender does not have permission to set commission - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(1), - 1, - Some((Perbill::from_percent(5), 900)), - ), - Error::::DoesNotHavePermission - ); + fn balance_to_unbond_works() { + // 1 balance : 1 points ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 100 }; + assert_eq!(unbond_pool.point_to_balance(10), 10); + assert_eq!(unbond_pool.point_to_balance(0), 0); - // We attempt to increase the commission to 100%, which is disallowed due - // to global max commission of 90%. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(100), 900)) - ), - Error::::GlobalMaxCommissionExceeded - ); + // 1 balance : 2 points ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 50 }; + assert_eq!(unbond_pool.point_to_balance(10), 5); - // Set the initial commission to 5%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(5), 900)), - )); + // 2 balance : 1 points ratio + let unbond_pool = UnbondPool:: { points: 50, balance: 100 }; + assert_eq!(unbond_pool.point_to_balance(10), 20); - // Throttle test. We will throttle commission to be a +1% commission increase every 2 - // blocks. - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } - )); - assert_eq!( - BondedPool::::get(1).unwrap().commission, - Commission { - current: Some((Perbill::from_percent(5), 900)), - max: None, - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2_u64 - }, - previous_set_at: None, - }) - } - ); + // 100 balance : 0 points ratio + let unbond_pool = UnbondPool:: { points: 0, balance: 100 }; + assert_eq!(unbond_pool.point_to_balance(10), 0); - // We now try to increase commission to 10% (5% increase): this should be throttled. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(10), 900)) - ), - Error::::CommissionChangeThrottled - ); + // 0 balance : 100 points ratio + let unbond_pool = UnbondPool:: { points: 100, balance: 0 }; + assert_eq!(unbond_pool.point_to_balance(10), 0); - // Run to block 3 - run_blocks(2); + // 10 balance : 3 points ratio + let unbond_pool = UnbondPool:: { points: 30, balance: 100 }; + assert_eq!(unbond_pool.point_to_balance(10), 33); - // We now try to increase commission by 1%, and provide an initial payee. - // This should work, and set the `previous_set_at` field. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(6), 900)) - )); - assert_eq!( - BondedPool::::get(1).unwrap().commission, - Commission { - current: Some((Perbill::from_percent(6), 900)), - max: None, - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2_u64 - }, - previous_set_at: Some(3_u64), - }) - } - ); + // 2 balance : 3 points ratio + let unbond_pool = UnbondPool:: { points: 300, balance: 200 }; + assert_eq!(unbond_pool.point_to_balance(10), 6); - // Attempt to increase the commission an additional 1% (now 2%) again immediately. - // this will fail as `previous_set_at` is now the current block, and at least 2 - // blocks need to pass before we can set commission again. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(2), 900)) - ), - Error::::CommissionChangeThrottled - ); + // 4 balance : 9 points ratio + let unbond_pool = UnbondPool:: { points: 900, balance: 400 }; + assert_eq!(unbond_pool.point_to_balance(90), 40); + } +} - // Run 2 blocks into the future, to block 3. - run_blocks(2); +mod sub_pools { + use super::*; - // We can now successfully increase the commission again, to 2%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(2), 900)), - )); + #[test] + fn maybe_merge_pools_works() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!(TotalUnbondingPools::::get(), 5); + assert_eq!(BondingDuration::get(), 3); + assert_eq!(PostUnbondingPoolsWindow::get(), 2); - // Run 2 blocks into the future, to block 5. - run_blocks(2); + // Given + let mut sub_pool_0 = SubPools:: { + no_era: UnbondPool::::default(), + with_era: unbonding_pools_with_era! { + 0 => UnbondPool:: { points: 10, balance: 10 }, + 1 => UnbondPool:: { points: 10, balance: 10 }, + 2 => UnbondPool:: { points: 20, balance: 20 }, + 3 => UnbondPool:: { points: 30, balance: 30 }, + 4 => UnbondPool:: { points: 40, balance: 40 }, + }, + }; - // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is - // still at play. An attempted commission change now to 4% (+2% increase) should fail. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(4), 900)), - ), - Error::::CommissionChangeThrottled - ); + // When `current_era < TotalUnbondingPools`, + let sub_pool_1 = sub_pool_0.clone().maybe_merge_pools(0); - // We will now set a max commission to the current 2%. This will also - // update commission.current to 2%. - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(2) - )); + // Then it exits early without modifications + assert_eq!(sub_pool_1, sub_pool_0); - // Run 2 blocks into the future so we are eligible to update commission again. - run_blocks(2); + // When `current_era == TotalUnbondingPools`, + let sub_pool_1 = sub_pool_1.maybe_merge_pools(1); - // We will now attempt again to increase the commission by 1%, to 3%. This is within the - // change rate allowance, but the max_commission will now prevent us from going any - // higher. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(3), 900)), - ), - Error::::CommissionExceedsMaximum - ); + // Then it exits early without modifications + assert_eq!(sub_pool_1, sub_pool_0); + + // When `current_era - TotalUnbondingPools == 0`, + let mut sub_pool_1 = sub_pool_1.maybe_merge_pools(2); + + // Then era 0 is merged into the `no_era` pool + sub_pool_0.no_era = sub_pool_0.with_era.remove(&0).unwrap(); + assert_eq!(sub_pool_1, sub_pool_0); + + // Given we have entries for era 1..=5 + sub_pool_1 + .with_era + .try_insert(5, UnbondPool:: { points: 50, balance: 50 }) + .unwrap(); + sub_pool_0 + .with_era + .try_insert(5, UnbondPool:: { points: 50, balance: 50 }) + .unwrap(); + + // When `current_era - TotalUnbondingPools == 1` + let sub_pool_2 = sub_pool_1.maybe_merge_pools(3); + let era_1_pool = sub_pool_0.with_era.remove(&1).unwrap(); + + // Then era 1 is merged into the `no_era` pool + sub_pool_0.no_era.points += era_1_pool.points; + sub_pool_0.no_era.balance += era_1_pool.balance; + assert_eq!(sub_pool_2, sub_pool_0); + + // When `current_era - TotalUnbondingPools == 5`, so all pools with era <= 4 are removed + let sub_pool_3 = sub_pool_2.maybe_merge_pools(7); + + // Then all eras <= 5 are merged into the `no_era` pool + for era in 2..=5 { + let to_merge = sub_pool_0.with_era.remove(&era).unwrap(); + sub_pool_0.no_era.points += to_merge.points; + sub_pool_0.no_era.balance += to_merge.balance; + } + assert_eq!(sub_pool_3, sub_pool_0); + }); + } +} + +mod join { + use super::*; + + #[test] + fn join_works() { + let bonded = |points, member_counter| BondedPool:: { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter, + points, + roles: DEFAULT_ROLES, + state: PoolState::Open, + }, + }; + ExtBuilder::default().with_check(0).build_and_execute(|| { + // Given + Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2); + assert!(!PoolMembers::::contains_key(&11)); + + // When + assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); + + // Then - // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(5), 900)), - }, - Event::PoolCommissionThrottleUpdated { - pool_id: 1, - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2 - } - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(6), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(2), 900)) - }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(2) - } + Event::Bonded { member: 11, pool_id: 1, bonded: 2, joined: true }, ] ); + + assert_eq!( + PoolMembers::::get(&11).unwrap(), + PoolMember:: { pool_id: 1, points: 2, ..Default::default() } + ); + assert_eq!(BondedPool::::get(1).unwrap(), bonded(12, 2)); + + // Given + // The bonded balance is slashed in half + StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 6); + + // And + Balances::make_free_balance_be(&12, ExistentialDeposit::get() + 12); + assert!(!PoolMembers::::contains_key(&12)); + + // When + assert_ok!(Pools::join(RuntimeOrigin::signed(12), 12, 1)); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::Bonded { member: 12, pool_id: 1, bonded: 12, joined: true }] + ); + + assert_eq!( + PoolMembers::::get(&12).unwrap(), + PoolMember:: { pool_id: 1, points: 24, ..Default::default() } + ); + assert_eq!(BondedPool::::get(1).unwrap(), bonded(12 + 24, 3)); }); } #[test] - fn set_commission_max_works_with_error_tests() { - ExtBuilder::default().build_and_execute(|| { - // Provided pool does not exist + fn join_errors_correctly() { + ExtBuilder::default().with_check(0).build_and_execute(|| { + // 10 is already part of the default pool created. + assert_eq!(PoolMembers::::get(&10).unwrap().pool_id, 1); + assert_noop!( - Pools::set_commission_max( - RuntimeOrigin::signed(900), - 9999, - Perbill::from_percent(1) - ), - Error::::PoolNotFound + Pools::join(RuntimeOrigin::signed(10), 420, 123), + Error::::AccountBelongsToOtherPool ); - // Sender does not have permission to set commission + assert_noop!( - Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), - Error::::DoesNotHavePermission + Pools::join(RuntimeOrigin::signed(11), 420, 123), + Error::::PoolNotFound ); - // Remove global maximum commission - GlobalMaxCommission::::set(None); - - // Set a max commission commission pool 1 to 80% - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(80) - )); - assert_eq!( - BondedPools::::get(1).unwrap().commission.max, - Some(Perbill::from_percent(80)) + // Force the pools bonded balance to 0, simulating a 100% slash + StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 0); + assert_noop!( + Pools::join(RuntimeOrigin::signed(11), 420, 1), + Error::::OverflowRisk ); - // We attempt to increase the max commission to 90%, but increasing is - // disallowed due to pool's max commission. + // Given a mocked bonded pool + BondedPool:: { + id: 123, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 100, + roles: DEFAULT_ROLES, + state: PoolState::Open, + }, + } + .put(); + + // and reward pool + RewardPools::::insert(123, RewardPool:: { ..Default::default() }); + + // Force the points:balance ratio to `MaxPointsToBalance` (100/10) + let max_points_to_balance: u128 = + <::MaxPointsToBalance as Get>::get().into(); + + StakingMock::set_bonded_balance( + Pools::create_bonded_account(123), + max_points_to_balance, + ); assert_noop!( - Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)), - Error::::MaxCommissionRestricted + Pools::join(RuntimeOrigin::signed(11), 420, 123), + Error::::OverflowRisk ); - // We will now set a commission to 75% and then amend the max commission - // to 50%. The max commission change should decrease the current - // commission to 50%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(75), 900)) - )); - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(50) - )); - assert_eq!( - BondedPools::::get(1).unwrap().commission, - Commission { - current: Some((Perbill::from_percent(50), 900)), - max: Some(Perbill::from_percent(50)), - throttle: None - } + StakingMock::set_bonded_balance( + Pools::create_bonded_account(123), + Balance::MAX / max_points_to_balance, + ); + // Balance needs to be gt Balance::MAX / `MaxPointsToBalance` + assert_noop!( + Pools::join(RuntimeOrigin::signed(11), 5, 123), + pallet_balances::Error::::InsufficientBalance, ); - // Commission change events triggered successfully - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(80) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(75), 900)), - }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(50) - } - ] + StakingMock::set_bonded_balance(Pools::create_bonded_account(1), max_points_to_balance); + + // Cannot join a pool that isn't open + unsafe_set_state(123, PoolState::Blocked); + assert_noop!( + Pools::join(RuntimeOrigin::signed(11), max_points_to_balance, 123), + Error::::NotOpen + ); + + unsafe_set_state(123, PoolState::Destroying); + assert_noop!( + Pools::join(RuntimeOrigin::signed(11), max_points_to_balance, 123), + Error::::NotOpen + ); + + // Given + MinJoinBond::::put(100); + + // Then + assert_noop!( + Pools::join(RuntimeOrigin::signed(11), 99, 123), + Error::::MinimumBondNotMet ); }); } #[test] - fn max_commission_after_current_commission_works() { + #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] + #[cfg_attr(not(debug_assertions), should_panic)] + fn join_panics_when_reward_pool_not_found() { ExtBuilder::default().build_and_execute(|| { - // set pool commission to 50% first. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(50), 900)), - )); - - // now set the max commission to something less than the current - // commission. - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(25) - )); - - // the current commission should now be 25%. - assert_eq!( - BondedPools::::get(1).unwrap().commission, - Commission { - current: Some((Perbill::from_percent(25), 900)), - max: Some(Perbill::from_percent(25)), - throttle: None - } - ); - }) + StakingMock::set_bonded_balance(Pools::create_bonded_account(123), 100); + BondedPool:: { + id: 123, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 100, + roles: DEFAULT_ROLES, + state: PoolState::Open, + }, + } + .put(); + let _ = Pools::join(RuntimeOrigin::signed(11), 420, 123); + }); } #[test] - fn set_commission_throttle_works_with_error_tests() { + fn join_max_member_limits_are_respected() { ExtBuilder::default().build_and_execute(|| { - // Provided pool does not exist - assert_noop!( - Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 9999, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 1000_u64 - } - ), - Error::::PoolNotFound + // Given + assert_eq!(MaxPoolMembersPerPool::::get(), Some(3)); + for i in 1..3 { + let account = i + 100; + Balances::make_free_balance_be(&account, 100 + Balances::minimum_balance()); + + assert_ok!(Pools::join(RuntimeOrigin::signed(account), 100, 1)); + } + + Balances::make_free_balance_be(&103, 100 + Balances::minimum_balance()); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 101, pool_id: 1, bonded: 100, joined: true }, + Event::Bonded { member: 102, pool_id: 1, bonded: 100, joined: true } + ] ); - // Sender does not have permission to set commission + assert_noop!( - Pools::set_commission_throttle( - RuntimeOrigin::signed(1), - 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 1000_u64 - } - ), - Error::::DoesNotHavePermission + Pools::join(RuntimeOrigin::signed(103), 100, 1), + Error::::MaxPoolMembers ); - // Set a commission throttle for pool 1 - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } - )); + // Given + assert_eq!(PoolMembers::::count(), 3); + assert_eq!(MaxPoolMembers::::get(), Some(4)); + + Balances::make_free_balance_be(&104, 100 + Balances::minimum_balance()); + assert_ok!(Pools::create(RuntimeOrigin::signed(104), 100, 104, 104, 104)); + + let pool_account = BondedPools::::iter() + .find(|(_, bonded_pool)| bonded_pool.roles.depositor == 104) + .map(|(pool_account, _)| pool_account) + .unwrap(); + + // Then assert_eq!( - BondedPools::::get(1).unwrap().commission.throttle, - Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 10_u64 - }, - previous_set_at: None - }) + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 104, pool_id: 2 }, + Event::Bonded { member: 104, pool_id: 2, bonded: 100, joined: true } + ] ); - // We now try to half the min_delay - this will be disallowed. - // A greater delay between commission changes is seen as more restrictive. assert_noop!( - Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 5_u64 - } - ), - Error::::CommissionThrottleNotAllowed + Pools::join(RuntimeOrigin::signed(103), 100, pool_account), + Error::::MaxPoolMembers ); + }); + } +} - // We now try to increase the allowed max_increase - this will fail. - // A smaller allowed commission change is seen as more restrictive. - assert_noop!( - Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { - max_increase: Perbill::from_percent(10), - min_delay: 10_u64 - } - ), - Error::::CommissionThrottleNotAllowed - ); +mod claim_payout { + use super::*; - // Successful more restrictive change of min_delay with the current max_increase - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } - )); - - // Successful more restrictive change of max_increase with the current min_delay - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } - )); - - // Successful more restrictive change of both max_increase and min_delay - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } - )); - - // multi duration `min_delay` test. - // - // set the commission throttle change_rate of 1% per 3000 blocks. - assert_ok!(Pools::set_commission_throttle( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } - )); - // pre-requisite: set the commission to 1%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(1), 900)), - )); - // Run 90 blocks into the future so we are eligible to update commission - // with 3 `min_delay` durations passed. - run_blocks(91); + fn del(points: Balance, last_recorded_reward_counter: u128) -> PoolMember { + PoolMember { + pool_id: 1, + points, + last_recorded_reward_counter: last_recorded_reward_counter.into(), + unbonding_eras: Default::default(), + } + } - // we should not be able to increase the commission to 5%: 1% beyond the throttle limit. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(5), 900)) - ), - Error::::CommissionChangeThrottled - ); + fn del_float(points: Balance, last_recorded_reward_counter: f64) -> PoolMember { + PoolMember { + pool_id: 1, + points, + last_recorded_reward_counter: RewardCounter::from_float(last_recorded_reward_counter), + unbonding_eras: Default::default(), + } + } - // we should however be able to increase the commission to 4%: 1% + (3*1%). - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(4), 900)) - )); - }); + fn rew( + last_recorded_reward_counter: u128, + last_recorded_total_payouts: Balance, + total_rewards_claimed: Balance, + ) -> RewardPool { + RewardPool { + last_recorded_reward_counter: last_recorded_reward_counter.into(), + last_recorded_total_payouts, + total_rewards_claimed, + } } -} -mod reward_pool { #[test] - fn current_balance_only_counts_balance_over_existential_deposit() { - use super::*; + fn claim_payout_works() { + ExtBuilder::default() + .add_members(vec![(40, 40), (50, 50)]) + .build_and_execute(|| { + // Given each member currently has a free balance of + Balances::make_free_balance_be(&10, 0); + Balances::make_free_balance_be(&40, 0); + Balances::make_free_balance_be(&50, 0); + let ed = Balances::minimum_balance(); - ExtBuilder::default().build_and_execute(|| { - let reward_account = Pools::create_reward_account(2); + // and the reward pool has earned 100 in rewards + assert_eq!(Balances::free_balance(default_reward_account()), ed); + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); - // Given - assert_eq!(Balances::free_balance(&reward_account), 0); + let _ = pool_events_since_last_call(); - // Then - assert_eq!(RewardPool::::current_balance(2), 0); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - // Given - Balances::make_free_balance_be(&reward_account, Balances::minimum_balance()); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 },] + ); + // last recorded reward counter at the time of this member's payout is 1 + assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 1)); + // pool's 'last_recorded_reward_counter' and 'last_recorded_total_payouts' don't + // really change unless if someone bonds/unbonds. + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 10)); + assert_eq!(Balances::free_balance(&10), 10); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 90); - // Then - assert_eq!(RewardPool::::current_balance(2), 0); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); - // Given - Balances::make_free_balance_be(&reward_account, Balances::minimum_balance() + 1); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(40).unwrap(), del(40, 1)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 50)); + assert_eq!(Balances::free_balance(&40), 40); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 50); - // Then - assert_eq!(RewardPool::::current_balance(2), 1); - }); - } -} + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); -mod unbond_pool { - use super::*; + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(50).unwrap(), del(50, 1)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 100)); + assert_eq!(Balances::free_balance(&50), 50); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); - #[test] - fn points_to_issue_works() { - ExtBuilder::default().build_and_execute(|| { - // 1 points : 1 balance ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 100 }; - assert_eq!(unbond_pool.balance_to_point(10), 10); - assert_eq!(unbond_pool.balance_to_point(0), 0); + // Given the reward pool has some new rewards + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); - // 2 points : 1 balance ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 50 }; - assert_eq!(unbond_pool.balance_to_point(10), 20); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - // 1 points : 2 balance ratio - let unbond_pool = UnbondPool:: { points: 50, balance: 100 }; - assert_eq!(unbond_pool.balance_to_point(10), 5); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 1.5)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 105)); + assert_eq!(Balances::free_balance(&10), 10 + 5); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 45); - // 100 points : 0 balance ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 0 }; - assert_eq!(unbond_pool.balance_to_point(10), 100 * 10); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); - // 0 points : 100 balance - let unbond_pool = UnbondPool:: { points: 0, balance: 100 }; - assert_eq!(unbond_pool.balance_to_point(10), 10); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 1.5)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 125)); + assert_eq!(Balances::free_balance(&40), 40 + 20); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); - // 10 points : 3 balance ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 30 }; - assert_eq!(unbond_pool.balance_to_point(10), 33); + // Given del 50 hasn't claimed and the reward pools has just earned 50 + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 75); - // 2 points : 3 balance ratio - let unbond_pool = UnbondPool:: { points: 200, balance: 300 }; - assert_eq!(unbond_pool.balance_to_point(10), 6); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); - // 4 points : 9 balance ratio - let unbond_pool = UnbondPool:: { points: 400, balance: 900 }; - assert_eq!(unbond_pool.balance_to_point(90), 40); - }) - } + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 2.0)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 175)); + assert_eq!(Balances::free_balance(&50), 50 + 50); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); - #[test] - fn balance_to_unbond_works() { - // 1 balance : 1 points ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 100 }; - assert_eq!(unbond_pool.point_to_balance(10), 10); - assert_eq!(unbond_pool.point_to_balance(0), 0); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - // 1 balance : 2 points ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 50 }; - assert_eq!(unbond_pool.point_to_balance(10), 5); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 2)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 180)); + assert_eq!(Balances::free_balance(&10), 15 + 5); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 20); - // 2 balance : 1 points ratio - let unbond_pool = UnbondPool:: { points: 50, balance: 100 }; - assert_eq!(unbond_pool.point_to_balance(10), 20); + // Given del 40 hasn't claimed and the reward pool has just earned 400 + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 400)); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 420); - // 100 balance : 0 points ratio - let unbond_pool = UnbondPool:: { points: 0, balance: 100 }; - assert_eq!(unbond_pool.point_to_balance(10), 0); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - // 0 balance : 100 points ratio - let unbond_pool = UnbondPool:: { points: 100, balance: 0 }; - assert_eq!(unbond_pool.point_to_balance(10), 0); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] + ); - // 10 balance : 3 points ratio - let unbond_pool = UnbondPool:: { points: 30, balance: 100 }; - assert_eq!(unbond_pool.point_to_balance(10), 33); - - // 2 balance : 3 points ratio - let unbond_pool = UnbondPool:: { points: 300, balance: 200 }; - assert_eq!(unbond_pool.point_to_balance(10), 6); - - // 4 balance : 9 points ratio - let unbond_pool = UnbondPool:: { points: 900, balance: 400 }; - assert_eq!(unbond_pool.point_to_balance(90), 40); - } -} - -mod sub_pools { - use super::*; - - #[test] - fn maybe_merge_pools_works() { - ExtBuilder::default().build_and_execute(|| { - assert_eq!(TotalUnbondingPools::::get(), 5); - assert_eq!(BondingDuration::get(), 3); - assert_eq!(PostUnbondingPoolsWindow::get(), 2); - - // Given - let mut sub_pool_0 = SubPools:: { - no_era: UnbondPool::::default(), - with_era: unbonding_pools_with_era! { - 0 => UnbondPool:: { points: 10, balance: 10 }, - 1 => UnbondPool:: { points: 10, balance: 10 }, - 2 => UnbondPool:: { points: 20, balance: 20 }, - 3 => UnbondPool:: { points: 30, balance: 30 }, - 4 => UnbondPool:: { points: 40, balance: 40 }, - }, - }; - - // When `current_era < TotalUnbondingPools`, - let sub_pool_1 = sub_pool_0.clone().maybe_merge_pools(0); + // We expect a payout of 40 + assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 6)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 220)); + assert_eq!(Balances::free_balance(&10), 20 + 40); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 380); - // Then it exits early without modifications - assert_eq!(sub_pool_1, sub_pool_0); + // Given del 40 + del 50 haven't claimed and the reward pool has earned 20 + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 20)); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 400); - // When `current_era == TotalUnbondingPools`, - let sub_pool_1 = sub_pool_1.maybe_merge_pools(1); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - // Then it exits early without modifications - assert_eq!(sub_pool_1, sub_pool_0); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 6.2)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 222)); + assert_eq!(Balances::free_balance(&10), 60 + 2); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 398); - // When `current_era - TotalUnbondingPools == 0`, - let mut sub_pool_1 = sub_pool_1.maybe_merge_pools(2); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); - // Then era 0 is merged into the `no_era` pool - sub_pool_0.no_era = sub_pool_0.with_era.remove(&0).unwrap(); - assert_eq!(sub_pool_1, sub_pool_0); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 6.2)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 410)); + assert_eq!(Balances::free_balance(&40), 60 + 188); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 210); - // Given we have entries for era 1..=5 - sub_pool_1 - .with_era - .try_insert(5, UnbondPool:: { points: 50, balance: 50 }) - .unwrap(); - sub_pool_0 - .with_era - .try_insert(5, UnbondPool:: { points: 50, balance: 50 }) - .unwrap(); + // When + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); - // When `current_era - TotalUnbondingPools == 1` - let sub_pool_2 = sub_pool_1.maybe_merge_pools(3); - let era_1_pool = sub_pool_0.with_era.remove(&1).unwrap(); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210, commission: 0 }] + ); + assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 6.2)); + assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 620)); + assert_eq!(Balances::free_balance(&50), 100 + 210); + assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); + }); + } - // Then era 1 is merged into the `no_era` pool - sub_pool_0.no_era.points += era_1_pool.points; - sub_pool_0.no_era.balance += era_1_pool.balance; - assert_eq!(sub_pool_2, sub_pool_0); + #[test] + fn reward_payout_errors_if_a_member_is_fully_unbonding() { + ExtBuilder::default().add_members(vec![(11, 11)]).build_and_execute(|| { + // fully unbond the member. + assert_ok!(fully_unbond_permissioned(11)); - // When `current_era - TotalUnbondingPools == 5`, so all pools with era <= 4 are removed - let sub_pool_3 = sub_pool_2.maybe_merge_pools(7); + assert_noop!( + Pools::claim_payout(RuntimeOrigin::signed(11)), + Error::::FullyUnbonding + ); - // Then all eras <= 5 are merged into the `no_era` pool - for era in 2..=5 { - let to_merge = sub_pool_0.with_era.remove(&era).unwrap(); - sub_pool_0.no_era.points += to_merge.points; - sub_pool_0.no_era.balance += to_merge.balance; - } - assert_eq!(sub_pool_3, sub_pool_0); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 11, pool_id: 1, bonded: 11, joined: true }, + Event::Unbonded { member: 11, pool_id: 1, points: 11, balance: 11, era: 3 } + ] + ); }); } -} - -mod join { - use super::*; #[test] - fn join_works() { - let bonded = |points, member_counter| BondedPool:: { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter, - points, - roles: DEFAULT_ROLES, - state: PoolState::Open, - }, - }; - ExtBuilder::default().with_check(0).build_and_execute(|| { - // Given - Balances::make_free_balance_be(&11, ExistentialDeposit::get() + 2); - assert!(!PoolMembers::::contains_key(&11)); + fn do_reward_payout_with_various_commissions() { + ExtBuilder::default().build_and_execute(|| { + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); - // When - assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1)); + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); - // Then + // Set a commission pool 1 to 50%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(33), 2)), + )); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 11, pool_id: 1, bonded: 2, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(33), 2)) + }, ] ); + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then assert_eq!( - PoolMembers::::get(&11).unwrap(), - PoolMember:: { pool_id: 1, points: 2, ..Default::default() } + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] ); - assert_eq!(BondedPool::::get(1).unwrap(), bonded(12, 2)); - // Given - // The bonded balance is slashed in half - StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 6); + // The pool earns 17 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); - // And - Balances::make_free_balance_be(&12, ExistentialDeposit::get() + 12); - assert!(!PoolMembers::::contains_key(&12)); + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] + ); - // When - assert_ok!(Pools::join(RuntimeOrigin::signed(12), 12, 1)); + // The pool earns 50 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); // Then assert_eq!( pool_events_since_last_call(), - vec![Event::Bonded { member: 12, pool_id: 1, bonded: 12, joined: true }] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] ); + // The pool earns 10439 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then assert_eq!( - PoolMembers::::get(&12).unwrap(), - PoolMember:: { pool_id: 1, points: 24, ..Default::default() } + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] ); - assert_eq!(BondedPool::::get(1).unwrap(), bonded(12 + 24, 3)); - }); + }) } #[test] - fn join_errors_correctly() { - ExtBuilder::default().with_check(0).build_and_execute(|| { - // 10 is already part of the default pool created. - assert_eq!(PoolMembers::::get(&10).unwrap().pool_id, 1); + fn do_reward_payout_works_with_a_pool_of_1() { + let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); - assert_noop!( - Pools::join(RuntimeOrigin::signed(10), 420, 123), - Error::::AccountBelongsToOtherPool - ); + ExtBuilder::default().build_and_execute(|| { + let (mut member, mut bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + let ed = Balances::minimum_balance(); - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 420, 123), - Error::::PoolNotFound - ); - - // Force the pools bonded balance to 0, simulating a 100% slash - StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 0); - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 420, 1), - Error::::OverflowRisk - ); - - // Given a mocked bonded pool - BondedPool:: { - id: 123, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 100, - roles: DEFAULT_ROLES, - state: PoolState::Open, - }, - } - .put(); - - // and reward pool - RewardPools::::insert(123, RewardPool:: { ..Default::default() }); - - // Force the points:balance ratio to `MaxPointsToBalance` (100/10) - let max_points_to_balance: u128 = - <::MaxPointsToBalance as Get>::get().into(); - - StakingMock::set_bonded_balance( - Pools::create_bonded_account(123), - max_points_to_balance, - ); - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 420, 123), - Error::::OverflowRisk - ); - - StakingMock::set_bonded_balance( - Pools::create_bonded_account(123), - Balance::MAX / max_points_to_balance, - ); - // Balance needs to be gt Balance::MAX / `MaxPointsToBalance` - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 5, 123), - pallet_balances::Error::::InsufficientBalance, - ); - - StakingMock::set_bonded_balance(Pools::create_bonded_account(1), max_points_to_balance); - - // Cannot join a pool that isn't open - unsafe_set_state(123, PoolState::Blocked); - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), max_points_to_balance, 123), - Error::::NotOpen - ); - - unsafe_set_state(123, PoolState::Destroying); - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), max_points_to_balance, 123), - Error::::NotOpen - ); - - // Given - MinJoinBond::::put(100); + let payout = + Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then - assert_noop!( - Pools::join(RuntimeOrigin::signed(11), 99, 123), - Error::::MinimumBondNotMet - ); - }); - } - - #[test] - #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] - #[cfg_attr(not(debug_assertions), should_panic)] - fn join_panics_when_reward_pool_not_found() { - ExtBuilder::default().build_and_execute(|| { - StakingMock::set_bonded_balance(Pools::create_bonded_account(123), 100); - BondedPool:: { - id: 123, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 100, - roles: DEFAULT_ROLES, - state: PoolState::Open, - }, - } - .put(); - let _ = Pools::join(RuntimeOrigin::signed(11), 420, 123); - }); - } - - #[test] - fn join_max_member_limits_are_respected() { - ExtBuilder::default().build_and_execute(|| { - // Given - assert_eq!(MaxPoolMembersPerPool::::get(), Some(3)); - for i in 1..3 { - let account = i + 100; - Balances::make_free_balance_be(&account, 100 + Balances::minimum_balance()); + assert_eq!(payout, 0); + assert_eq!(member, del(0.0)); + assert_eq!(reward_pool, rew(0, 0, 0)); - assert_ok!(Pools::join(RuntimeOrigin::signed(account), 100, 1)); - } + // Given the pool has earned some rewards for the first time + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 5)); - Balances::make_free_balance_be(&103, 100 + Balances::minimum_balance()); + // When + let payout = + Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( @@ -1184,660 +1020,529 @@ mod join { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 101, pool_id: 1, bonded: 100, joined: true }, - Event::Bonded { member: 102, pool_id: 1, bonded: 100, joined: true } + Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 } ] ); + assert_eq!(payout, 5); + assert_eq!(reward_pool, rew(0, 0, 5)); + assert_eq!(member, del(0.5)); - assert_noop!( - Pools::join(RuntimeOrigin::signed(103), 100, 1), - Error::::MaxPoolMembers - ); - - // Given - assert_eq!(PoolMembers::::count(), 3); - assert_eq!(MaxPoolMembers::::get(), Some(4)); - - Balances::make_free_balance_be(&104, 100 + Balances::minimum_balance()); - assert_ok!(Pools::create(RuntimeOrigin::signed(104), 100, 104, 104, 104)); + // Given the pool has earned rewards again + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); - let pool_account = BondedPools::::iter() - .find(|(_, bonded_pool)| bonded_pool.roles.depositor == 104) - .map(|(pool_account, _)| pool_account) - .unwrap(); + // When + let payout = + Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), - vec![ - Event::Created { depositor: 104, pool_id: 2 }, - Event::Bonded { member: 104, pool_id: 2, bonded: 100, joined: true } - ] - ); - - assert_noop!( - Pools::join(RuntimeOrigin::signed(103), 100, pool_account), - Error::::MaxPoolMembers + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); - }); - } -} - -mod claim_payout { - use super::*; + assert_eq!(payout, 10); + assert_eq!(reward_pool, rew(0, 0, 15)); + assert_eq!(member, del(1.5)); - fn del(points: Balance, last_recorded_reward_counter: u128) -> PoolMember { - PoolMember { - pool_id: 1, - points, - last_recorded_reward_counter: last_recorded_reward_counter.into(), - unbonding_eras: Default::default(), - } - } + // Given the pool has earned no new rewards + Balances::make_free_balance_be(&default_reward_account(), ed + 0); - fn del_float(points: Balance, last_recorded_reward_counter: f64) -> PoolMember { - PoolMember { - pool_id: 1, - points, - last_recorded_reward_counter: RewardCounter::from_float(last_recorded_reward_counter), - unbonding_eras: Default::default(), - } - } + // When + let payout = + Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) + .unwrap(); - fn rew( - last_recorded_reward_counter: u128, - last_recorded_total_payouts: Balance, - total_rewards_claimed: Balance, - ) -> RewardPool { - RewardPool { - last_recorded_reward_counter: last_recorded_reward_counter.into(), - last_recorded_total_payouts, - total_rewards_claimed, - } + // Then + assert_eq!(pool_events_since_last_call(), vec![]); + assert_eq!(payout, 0); + assert_eq!(reward_pool, rew(0, 0, 15)); + assert_eq!(member, del(1.5)); + }); } #[test] - fn claim_payout_works() { + fn do_reward_payout_works_with_a_pool_of_3() { ExtBuilder::default() .add_members(vec![(40, 40), (50, 50)]) .build_and_execute(|| { - // Given each member currently has a free balance of - Balances::make_free_balance_be(&10, 0); - Balances::make_free_balance_be(&40, 0); - Balances::make_free_balance_be(&50, 0); - let ed = Balances::minimum_balance(); + let mut bonded_pool = BondedPool::::get(1).unwrap(); + let mut reward_pool = RewardPools::::get(1).unwrap(); + + let mut del_10 = PoolMembers::::get(10).unwrap(); + let mut del_40 = PoolMembers::::get(40).unwrap(); + let mut del_50 = PoolMembers::::get(50).unwrap(); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, + Event::Bonded { member: 50, pool_id: 1, bonded: 50, joined: true } + ] + ); + + // Given we have a total of 100 points split among the members + assert_eq!(del_50.points + del_40.points + del_10.points, 100); + assert_eq!(bonded_pool.points, 100); // and the reward pool has earned 100 in rewards - assert_eq!(Balances::free_balance(default_reward_account()), ed); assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); - let _ = pool_events_since_last_call(); - // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let payout = + Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 },] + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] ); - // last recorded reward counter at the time of this member's payout is 1 - assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 1)); - // pool's 'last_recorded_reward_counter' and 'last_recorded_total_payouts' don't - // really change unless if someone bonds/unbonds. - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 10)); - assert_eq!(Balances::free_balance(&10), 10); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 90); + assert_eq!(payout, 10); + assert_eq!(del_10, del(10, 1)); + assert_eq!(reward_pool, rew(0, 0, 10)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); + let payout = + Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] ); - assert_eq!(PoolMembers::::get(40).unwrap(), del(40, 1)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 50)); - assert_eq!(Balances::free_balance(&40), 40); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 50); + assert_eq!(payout, 40); + assert_eq!(del_40, del(40, 1)); + assert_eq!(reward_pool, rew(0, 0, 50)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); - - // Then + let payout = + Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) + .unwrap(); + + // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); - assert_eq!(PoolMembers::::get(50).unwrap(), del(50, 1)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 100)); - assert_eq!(Balances::free_balance(&50), 50); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); + assert_eq!(payout, 50); + assert_eq!(del_50, del(50, 1)); + assert_eq!(reward_pool, rew(0, 0, 100)); // Given the reward pool has some new rewards assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let payout = + Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); - assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 1.5)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 105)); - assert_eq!(Balances::free_balance(&10), 10 + 5); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 45); + assert_eq!(payout, 5); + assert_eq!(del_10, del_float(10, 1.5)); + assert_eq!(reward_pool, rew(0, 0, 105)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); + let payout = + Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] ); - assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 1.5)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 125)); - assert_eq!(Balances::free_balance(&40), 40 + 20); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); + assert_eq!(payout, 20); + assert_eq!(del_40, del_float(40, 1.5)); + assert_eq!(reward_pool, rew(0, 0, 125)); - // Given del 50 hasn't claimed and the reward pools has just earned 50 + // Given del_50 hasn't claimed and the reward pools has just earned 50 assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 75); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); + let payout = + Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] ); - assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 2.0)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 175)); - assert_eq!(Balances::free_balance(&50), 50 + 50); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 25); + assert_eq!(payout, 50); + assert_eq!(del_50, del_float(50, 2.0)); + assert_eq!(reward_pool, rew(0, 0, 175)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let payout = + Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] ); - assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 180)); - assert_eq!(Balances::free_balance(&10), 15 + 5); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 20); + assert_eq!(payout, 5); + assert_eq!(del_10, del_float(10, 2.0)); + assert_eq!(reward_pool, rew(0, 0, 180)); - // Given del 40 hasn't claimed and the reward pool has just earned 400 + // Given del_40 hasn't claimed and the reward pool has just earned 400 assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 400)); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 420); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let payout = + Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] ); + assert_eq!(payout, 40); + assert_eq!(del_10, del_float(10, 6.0)); + assert_eq!(reward_pool, rew(0, 0, 220)); - // We expect a payout of 40 - assert_eq!(PoolMembers::::get(10).unwrap(), del(10, 6)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 220)); - assert_eq!(Balances::free_balance(&10), 20 + 40); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 380); - - // Given del 40 + del 50 haven't claimed and the reward pool has earned 20 + // Given del_40 + del_50 haven't claimed and the reward pool has earned 20 assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 20)); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 400); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let payout = + Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 2, commission: 0 }] - ); - assert_eq!(PoolMembers::::get(10).unwrap(), del_float(10, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 222)); - assert_eq!(Balances::free_balance(&10), 60 + 2); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 398); + assert_eq!(payout, 2); + assert_eq!(del_10, del_float(10, 6.2)); + assert_eq!(reward_pool, rew(0, 0, 222)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(40))); + let payout = + Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 188, commission: 0 }] - ); - assert_eq!(PoolMembers::::get(40).unwrap(), del_float(40, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 410)); - assert_eq!(Balances::free_balance(&40), 60 + 188); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 210); + assert_eq!(payout, 188); // 20 (from the 50) + 160 (from the 400) + 8 (from the 20) + assert_eq!(del_40, del_float(40, 6.2)); + assert_eq!(reward_pool, rew(0, 0, 410)); // When - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(50))); + let payout = + Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) + .unwrap(); // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 210, commission: 0 }] - ); - assert_eq!(PoolMembers::::get(50).unwrap(), del_float(50, 6.2)); - assert_eq!(RewardPools::::get(&1).unwrap(), rew(0, 0, 620)); - assert_eq!(Balances::free_balance(&50), 100 + 210); - assert_eq!(Balances::free_balance(&default_reward_account()), ed + 0); + assert_eq!(payout, 210); // 200 (from the 400) + 10 (from the 20) + assert_eq!(del_50, del_float(50, 6.2)); + assert_eq!(reward_pool, rew(0, 0, 620)); }); } #[test] - fn reward_payout_errors_if_a_member_is_fully_unbonding() { - ExtBuilder::default().add_members(vec![(11, 11)]).build_and_execute(|| { - // fully unbond the member. - assert_ok!(fully_unbond_permissioned(11)); + fn rewards_distribution_is_fair_basic() { + ExtBuilder::default().build_and_execute(|| { + // reward pool by 10. + Balances::mutate_account(&default_reward_account(), |f| f.free += 10).unwrap(); - assert_noop!( - Pools::claim_payout(RuntimeOrigin::signed(11)), - Error::::FullyUnbonding - ); + // 20 joins afterwards. + Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + + // reward by another 20 + Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + // 10 should claim 10 + 10, 20 should claim 20 / 2. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 11, pool_id: 1, bonded: 11, joined: true }, - Event::Unbonded { member: 11, pool_id: 1, points: 11, balance: 11, era: 3 } + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + ] + ); + + // any upcoming rewards are shared equally. + Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, ] ); }); } #[test] - fn do_reward_payout_with_various_commissions() { + fn rewards_distribution_is_fair_basic_with_fractions() { + // basically checks the case where the amount of rewards is less than the pool shares. for + // this, we have to rely on fixed point arithmetic. ExtBuilder::default().build_and_execute(|| { - let (mut member, bonded_pool, mut reward_pool) = - Pools::get_member_with_pools(&10).unwrap(); + Balances::mutate_account(&default_reward_account(), |f| f.free += 3).unwrap(); - // top up commission payee account to existential deposit - let _ = Balances::deposit_creating(&2, 5); + Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - // Set a commission pool 1 to 50%, with a payee set to `2` - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - bonded_pool.id, - Some((Perbill::from_percent(33), 2)), - )); + Balances::mutate_account(&default_reward_account(), |f| f.free += 6).unwrap(); + + // 10 should claim 3, 20 should claim 3 + 3. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(33), 2)) - }, + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, ] ); - // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + // any upcoming rewards are shared equally. + Balances::mutate_account(&default_reward_account(), |f| f.free += 8).unwrap(); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 4, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 4, commission: 0 }, + ] ); - // The pool earns 17 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] - ); + // uneven upcoming rewards are shared equally, rounded down. + Balances::mutate_account(&default_reward_account(), |f| f.free += 7).unwrap(); - // The pool earns 50 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 3, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, + ] ); + }); + } - // The pool earns 10439 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); + #[test] + fn rewards_distribution_is_fair_3() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] - ); - }) - } - - #[test] - fn do_reward_payout_works_with_a_pool_of_1() { - let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - ExtBuilder::default().build_and_execute(|| { - let (mut member, mut bonded_pool, mut reward_pool) = - Pools::get_member_with_pools(&10).unwrap(); - let ed = Balances::minimum_balance(); + Balances::make_free_balance_be(&20, ed + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - let payout = - Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) - .unwrap(); + Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); - // Then - assert_eq!(payout, 0); - assert_eq!(member, del(0.0)); - assert_eq!(reward_pool, rew(0, 0, 0)); + Balances::make_free_balance_be(&30, ed + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - // Given the pool has earned some rewards for the first time - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 5)); + Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - // When - let payout = - Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) - .unwrap(); + // 10 should claim 10, 20 should claim nothing. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - // Then assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 } + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 30 + 100 / 2 + 60 / 3, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 100 / 2 + 60 / 3, + commission: 0 + }, + Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3, commission: 0 }, ] ); - assert_eq!(payout, 5); - assert_eq!(reward_pool, rew(0, 0, 5)); - assert_eq!(member, del(0.5)); - // Given the pool has earned rewards again - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + // any upcoming rewards are shared equally. + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - // When - let payout = - Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - // Then assert_eq!( pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 }, + ] ); - assert_eq!(payout, 10); - assert_eq!(reward_pool, rew(0, 0, 15)); - assert_eq!(member, del(1.5)); - - // Given the pool has earned no new rewards - Balances::make_free_balance_be(&default_reward_account(), ed + 0); - - // When - let payout = - Pools::do_reward_payout(&10, &mut member, &mut bonded_pool, &mut reward_pool) - .unwrap(); - - // Then - assert_eq!(pool_events_since_last_call(), vec![]); - assert_eq!(payout, 0); - assert_eq!(reward_pool, rew(0, 0, 15)); - assert_eq!(member, del(1.5)); }); } #[test] - fn do_reward_payout_works_with_a_pool_of_3() { - ExtBuilder::default() - .add_members(vec![(40, 40), (50, 50)]) - .build_and_execute(|| { - let mut bonded_pool = BondedPool::::get(1).unwrap(); - let mut reward_pool = RewardPools::::get(1).unwrap(); + fn pending_rewards_per_member_works() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); - let mut del_10 = PoolMembers::::get(10).unwrap(); - let mut del_40 = PoolMembers::::get(40).unwrap(); - let mut del_50 = PoolMembers::::get(50).unwrap(); + assert_eq!(Pools::pending_rewards(10), Some(0)); + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + assert_eq!(Pools::pending_rewards(10), Some(30)); + assert_eq!(Pools::pending_rewards(20), None); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, - Event::Bonded { member: 50, pool_id: 1, bonded: 50, joined: true } - ] - ); + Balances::make_free_balance_be(&20, ed + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); - // Given we have a total of 100 points split among the members - assert_eq!(del_50.points + del_40.points + del_10.points, 100); - assert_eq!(bonded_pool.points, 100); + assert_eq!(Pools::pending_rewards(10), Some(30)); + assert_eq!(Pools::pending_rewards(20), Some(0)); - // and the reward pool has earned 100 in rewards - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 100)); + Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); - // When - let payout = - Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); + assert_eq!(Pools::pending_rewards(20), Some(50)); + assert_eq!(Pools::pending_rewards(30), None); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] - ); - assert_eq!(payout, 10); - assert_eq!(del_10, del(10, 1)); - assert_eq!(reward_pool, rew(0, 0, 10)); + Balances::make_free_balance_be(&30, ed + 10); + assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - // When - let payout = - Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); + assert_eq!(Pools::pending_rewards(20), Some(50)); + assert_eq!(Pools::pending_rewards(30), Some(0)); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }] - ); - assert_eq!(payout, 40); - assert_eq!(del_40, del(40, 1)); - assert_eq!(reward_pool, rew(0, 0, 50)); + Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - // When - let payout = - Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_eq!(Pools::pending_rewards(10), Some(30 + 50 + 20)); + assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); + assert_eq!(Pools::pending_rewards(30), Some(20)); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] - ); - assert_eq!(payout, 50); - assert_eq!(del_50, del(50, 1)); - assert_eq!(reward_pool, rew(0, 0, 100)); + // 10 should claim 10, 20 should claim nothing. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!(Pools::pending_rewards(10), Some(0)); + assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); + assert_eq!(Pools::pending_rewards(30), Some(20)); - // Given the reward pool has some new rewards - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_eq!(Pools::pending_rewards(10), Some(0)); + assert_eq!(Pools::pending_rewards(20), Some(0)); + assert_eq!(Pools::pending_rewards(30), Some(20)); - // When - let payout = - Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + assert_eq!(Pools::pending_rewards(10), Some(0)); + assert_eq!(Pools::pending_rewards(20), Some(0)); + assert_eq!(Pools::pending_rewards(30), Some(0)); + }); + } - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] - ); - assert_eq!(payout, 5); - assert_eq!(del_10, del_float(10, 1.5)); - assert_eq!(reward_pool, rew(0, 0, 105)); + #[test] + fn rewards_distribution_is_fair_bond_extra() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); - // When - let payout = - Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) - .unwrap(); + Balances::make_free_balance_be(&20, ed + 20); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); + Balances::make_free_balance_be(&30, ed + 20); + assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 40, pool_id: 1, payout: 20, commission: 0 }] - ); - assert_eq!(payout, 20); - assert_eq!(del_40, del_float(40, 1.5)); - assert_eq!(reward_pool, rew(0, 0, 125)); + Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); - // Given del_50 hasn't claimed and the reward pools has just earned 50 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + // everyone claims. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - // When - let payout = - Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 } + ] + ); - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 50, pool_id: 1, payout: 50, commission: 0 }] - ); - assert_eq!(payout, 50); - assert_eq!(del_50, del_float(50, 2.0)); - assert_eq!(reward_pool, rew(0, 0, 175)); - - // When - let payout = - Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) - .unwrap(); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 0 }] - ); - assert_eq!(payout, 5); - assert_eq!(del_10, del_float(10, 2.0)); - assert_eq!(reward_pool, rew(0, 0, 180)); - - // Given del_40 hasn't claimed and the reward pool has just earned 400 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 400)); - - // When - let payout = - Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) - .unwrap(); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 40, commission: 0 }] - ); - assert_eq!(payout, 40); - assert_eq!(del_10, del_float(10, 6.0)); - assert_eq!(reward_pool, rew(0, 0, 220)); - - // Given del_40 + del_50 haven't claimed and the reward pool has earned 20 - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 20)); - - // When - let payout = - Pools::do_reward_payout(&10, &mut del_10, &mut bonded_pool, &mut reward_pool) - .unwrap(); - - // Then - assert_eq!(payout, 2); - assert_eq!(del_10, del_float(10, 6.2)); - assert_eq!(reward_pool, rew(0, 0, 222)); - - // When - let payout = - Pools::do_reward_payout(&40, &mut del_40, &mut bonded_pool, &mut reward_pool) - .unwrap(); + // 30 now bumps itself to be like 20. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(30), BondExtra::FreeBalance(10))); - // Then - assert_eq!(payout, 188); // 20 (from the 50) + 160 (from the 400) + 8 (from the 20) - assert_eq!(del_40, del_float(40, 6.2)); - assert_eq!(reward_pool, rew(0, 0, 410)); + // more rewards come in. + Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); - // When - let payout = - Pools::do_reward_payout(&50, &mut del_50, &mut bonded_pool, &mut reward_pool) - .unwrap(); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - // Then - assert_eq!(payout, 210); // 200 (from the 400) + 10 (from the 20) - assert_eq!(del_50, del_float(50, 6.2)); - assert_eq!(reward_pool, rew(0, 0, 620)); - }); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: false }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 40, commission: 0 } + ] + ); + }); } #[test] - fn rewards_distribution_is_fair_basic() { + fn rewards_distribution_is_fair_unbond() { ExtBuilder::default().build_and_execute(|| { - // reward pool by 10. - Balances::mutate_account(&default_reward_account(), |f| f.free += 10).unwrap(); + let ed = Balances::minimum_balance(); - // 20 joins afterwards. - Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + Balances::make_free_balance_be(&20, ed + 20); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - // reward by another 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - // 10 should claim 10 + 10, 20 should claim 20 / 2. + // everyone claims. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); - // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 20).unwrap(); + // 20 unbonds to be equal to 10 (10 points each). + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); + + // more rewards come in. + Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1845,26 +1550,28 @@ mod claim_payout { assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 50, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 50, commission: 0 }, ] ); }); } #[test] - fn rewards_distribution_is_fair_basic_with_fractions() { - // basically checks the case where the amount of rewards is less than the pool shares. for - // this, we have to rely on fixed point arithmetic. + fn unclaimed_reward_is_safe() { ExtBuilder::default().build_and_execute(|| { - Balances::mutate_account(&default_reward_account(), |f| f.free += 3).unwrap(); + let ed = Balances::minimum_balance(); - Balances::make_free_balance_be(&20, Balances::minimum_balance() + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + Balances::make_free_balance_be(&20, ed + 20); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); + Balances::make_free_balance_be(&30, ed + 20); + assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 6).unwrap(); + // 10 gets 10, 20 gets 20, 30 gets 10 + Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); - // 10 should claim 3, 20 should claim 3 + 3. + // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); @@ -1873,173 +1580,169 @@ mod claim_payout { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 3 + 3, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } ] ); - // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 8).unwrap(); + // 10 gets 20, 20 gets 40, 30 gets 20 + Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 4, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 4, commission: 0 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); - // uneven upcoming rewards are shared equally, rounded down. - Balances::mutate_account(&default_reward_account(), |f| f.free += 7).unwrap(); + // 10 gets 20, 20 gets 40, 30 gets 20 + Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + // some claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 3, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } ] ); + + // now 30 claims all at once + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { + member: 30, + pool_id: 1, + payout: 10 + 20 + 20, + commission: 0 + }] + ); }); } #[test] - fn rewards_distribution_is_fair_3() { + fn bond_extra_and_delayed_claim() { ExtBuilder::default().build_and_execute(|| { let ed = Balances::minimum_balance(); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + Balances::make_free_balance_be(&20, ed + 200); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - Balances::make_free_balance_be(&20, ed + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + // 10 gets 10, 20 gets 20, 30 gets 10 + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + // some claim. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - Balances::make_free_balance_be(&30, ed + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 } + ] + ); + // 20 has not claimed yet, more reward comes Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - // 10 should claim 10, 20 should claim nothing. + // and 20 bonds more -- they should not have more share of this reward. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(10))); + + // everyone claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); assert_eq!( pool_events_since_last_call(), vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { - member: 10, - pool_id: 1, - payout: 30 + 100 / 2 + 60 / 3, - commission: 0 - }, - Event::PaidOut { - member: 20, - pool_id: 1, - payout: 100 / 2 + 60 / 3, - commission: 0 - }, - Event::PaidOut { member: 30, pool_id: 1, payout: 60 / 3, commission: 0 }, + // 20 + 40, which means the extra amount they bonded did not impact us. + Event::PaidOut { member: 20, pool_id: 1, payout: 60, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false }, + Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 } ] ); - // any upcoming rewards are shared equally. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + // but in the next round of rewards, the extra10 they bonded has an impact. + Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + // everyone claim. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); assert_eq!( pool_events_since_last_call(), vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 45, commission: 0 } ] ); }); } #[test] - fn pending_rewards_per_member_works() { + fn create_sets_recorded_data() { ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); + MaxPools::::set(None); + // pool 10 has already been created. + let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(0)); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(30)); - assert_eq!(Pools::pending_rewards(20), None); + assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); + assert_eq!(reward_pool_10.total_rewards_claimed, 0); + assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); - Balances::make_free_balance_be(&20, ed + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1)); + assert_eq!(member_10.last_recorded_reward_counter, 0.into()); - assert_eq!(Pools::pending_rewards(10), Some(30)); - assert_eq!(Pools::pending_rewards(20), Some(0)); + // transfer some reward to pool 1. + Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + // create pool 2 + Balances::make_free_balance_be(&20, 100); + assert_ok!(Pools::create(RuntimeOrigin::signed(20), 10, 20, 20, 20)); - assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); - assert_eq!(Pools::pending_rewards(20), Some(50)); - assert_eq!(Pools::pending_rewards(30), None); - - Balances::make_free_balance_be(&30, ed + 10); - assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); - - assert_eq!(Pools::pending_rewards(10), Some(30 + 50)); - assert_eq!(Pools::pending_rewards(20), Some(50)); - assert_eq!(Pools::pending_rewards(30), Some(0)); - - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + // has no impact -- initial + let (member_20, _, reward_pool_20) = Pools::get_member_with_pools(&20).unwrap(); - assert_eq!(Pools::pending_rewards(10), Some(30 + 50 + 20)); - assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + assert_eq!(reward_pool_20.last_recorded_total_payouts, 0); + assert_eq!(reward_pool_20.total_rewards_claimed, 0); + assert_eq!(reward_pool_20.last_recorded_reward_counter, 0.into()); - // 10 should claim 10, 20 should claim nothing. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(50 + 20)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + assert_eq!(member_20.last_recorded_reward_counter, 0.into()); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(0)); - assert_eq!(Pools::pending_rewards(30), Some(20)); + // pre-fund the reward account of pool id 3 with some funds. + Balances::make_free_balance_be(&Pools::create_reward_account(3), 10); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); - assert_eq!(Pools::pending_rewards(10), Some(0)); - assert_eq!(Pools::pending_rewards(20), Some(0)); - assert_eq!(Pools::pending_rewards(30), Some(0)); - }); - } + // create pool 3 + Balances::make_free_balance_be(&30, 100); + assert_ok!(Pools::create(RuntimeOrigin::signed(30), 10, 30, 30, 30)); - #[test] - fn rewards_distribution_is_fair_bond_extra() { - ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); + // reward counter is still the same. + let (member_30, _, reward_pool_30) = Pools::get_member_with_pools(&30).unwrap(); + assert_eq!( + Balances::free_balance(&Pools::create_reward_account(3)), + 10 + Balances::minimum_balance() + ); - Balances::make_free_balance_be(&20, ed + 20); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - Balances::make_free_balance_be(&30, ed + 20); - assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); + assert_eq!(reward_pool_30.last_recorded_total_payouts, 0); + assert_eq!(reward_pool_30.total_rewards_claimed, 0); + assert_eq!(reward_pool_30.last_recorded_reward_counter, 0.into()); - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); + assert_eq!(member_30.last_recorded_reward_counter, 0.into()); - // everyone claims. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // and 30 can claim the reward now. assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); assert_eq!( @@ -2047,97 +1750,149 @@ mod claim_payout { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 10, commission: 0 } + Event::Created { depositor: 20, pool_id: 2 }, + Event::Bonded { member: 20, pool_id: 2, bonded: 10, joined: true }, + Event::Created { depositor: 30, pool_id: 3 }, + Event::Bonded { member: 30, pool_id: 3, bonded: 10, joined: true }, + Event::PaidOut { member: 30, pool_id: 3, payout: 10, commission: 0 } ] ); + }) + } - // 30 now bumps itself to be like 20. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(30), BondExtra::FreeBalance(10))); + #[test] + fn join_updates_recorded_data() { + ExtBuilder::default().build_and_execute(|| { + MaxPoolMembers::::set(None); + MaxPoolMembersPerPool::::set(None); + let join = |x, y| { + Balances::make_free_balance_be(&x, y + Balances::minimum_balance()); + assert_ok!(Pools::join(RuntimeOrigin::signed(x), y, 1)); + }; - // more rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + { + let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); + assert_eq!(reward_pool_10.total_rewards_claimed, 0); + assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 40, commission: 0 } - ] - ); - }); - } + assert_eq!(member_10.last_recorded_reward_counter, 0.into()); + } - #[test] - fn rewards_distribution_is_fair_unbond() { - ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); + // someone joins without any rewards being issued. + { + join(20, 10); + let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); + // reward counter is 0 both before.. + assert_eq!(member.last_recorded_reward_counter, 0.into()); + assert_eq!(reward_pool.last_recorded_total_payouts, 0); + assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + } - Balances::make_free_balance_be(&20, ed + 20); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); + // transfer some reward to pool 1. + Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + { + join(30, 10); + let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); + assert_eq!(reward_pool.last_recorded_total_payouts, 60); + // explanation: we have a total of 20 points so far (excluding the 10 that just got + // bonded), and 60 unclaimed rewards. each share is then roughly worth of 3 units of + // rewards, thus reward counter is 3. member's reward counter is the same + assert_eq!(member.last_recorded_reward_counter, 3.into()); + assert_eq!(reward_pool.last_recorded_reward_counter, 3.into()); + } - // everyone claims. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // someone else joins + { + join(40, 10); + let (member, _, reward_pool) = Pools::get_member_with_pools(&40).unwrap(); + // reward counter does not change since no rewards have came in. + assert_eq!(member.last_recorded_reward_counter, 3.into()); + assert_eq!(reward_pool.last_recorded_reward_counter, 3.into()); + assert_eq!(reward_pool.last_recorded_total_payouts, 60); + } assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 40, pool_id: 1, bonded: 10, joined: true } ] ); + }) + } - // 20 unbonds to be equal to 10 (10 points each). - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); + #[test] + fn bond_extra_updates_recorded_data() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + MaxPoolMembers::::set(None); + MaxPoolMembersPerPool::::set(None); - // more rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 100).unwrap(); + // initial state of pool 1. + { + let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); + assert_eq!(reward_pool_10.total_rewards_claimed, 0); + assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 50, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 50, commission: 0 }, - ] - ); - }); - } + assert_eq!(member_10.last_recorded_reward_counter, 0.into()); + } - #[test] - fn unclaimed_reward_is_safe() { - ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); + Balances::make_free_balance_be(&10, 100); + Balances::make_free_balance_be(&20, 100); - Balances::make_free_balance_be(&20, ed + 20); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); - Balances::make_free_balance_be(&30, ed + 20); - assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10, 1)); + // 10 bonds extra without any rewards. + { + assert_ok!(Pools::bond_extra( + RuntimeOrigin::signed(10), + BondExtra::FreeBalance(10) + )); + let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + assert_eq!(member.last_recorded_reward_counter, 0.into()); + assert_eq!(reward_pool.last_recorded_total_payouts, 0); + assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + } - // 10 gets 10, 20 gets 20, 30 gets 10 - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); + // 10 bonds extra again with some rewards. This reward should be split equally between + // 10 and 20, as they both have equal points now. + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - // some claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + { + assert_ok!(Pools::bond_extra( + RuntimeOrigin::signed(10), + BondExtra::FreeBalance(10) + )); + let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + // explanation: before bond_extra takes place, there is 40 points and 30 balance in + // the system, RewardCounter is therefore 7.5 + assert_eq!(member.last_recorded_reward_counter, RewardCounter::from_float(0.75)); + assert_eq!( + reward_pool.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + } + + // 20 bonds extra again, without further rewards. + { + assert_ok!(Pools::bond_extra( + RuntimeOrigin::signed(20), + BondExtra::FreeBalance(10) + )); + let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); + assert_eq!(member.last_recorded_reward_counter, RewardCounter::from_float(0.75)); + assert_eq!( + reward_pool.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + } assert_eq!( pool_events_since_last_call(), @@ -2145,3675 +1900,3924 @@ mod claim_payout { Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 } + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, + Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, + Event::PaidOut { member: 20, pool_id: 1, payout: 15, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false } ] ); + }) + } - // 10 gets 20, 20 gets 40, 30 gets 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + #[test] + fn bond_extra_pending_rewards_works() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + MaxPoolMembers::::set(None); + MaxPoolMembersPerPool::::set(None); - // some claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // pool receives some rewards. + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + System::reset_events(); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } - ] - ); + // 10 cashes it out, and bonds it. + { + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + // there is 30 points and 30 reward points in the system RC is 1. + assert_eq!(member.last_recorded_reward_counter, 1.into()); + assert_eq!(reward_pool.total_rewards_claimed, 10); + // these two are not updated -- only updated when the points change. + assert_eq!(reward_pool.last_recorded_total_payouts, 0); + assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); - // 10 gets 20, 20 gets 40, 30 gets 20 - Balances::mutate_account(&default_reward_account(), |f| f.free += 80).unwrap(); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] + ); + } - // some claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // 20 re-bonds it. + { + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards)); + let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + assert_eq!(member.last_recorded_reward_counter, 1.into()); + assert_eq!(reward_pool.total_rewards_claimed, 30); + // since points change, these two are updated. + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + assert_eq!(reward_pool.last_recorded_reward_counter, 1.into()); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 40, commission: 0 } - ] - ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: false } + ] + ); + } + }) + } - // now 30 claims all at once - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + #[test] + fn unbond_updates_recorded_data() { + ExtBuilder::default() + .add_members(vec![(20, 20), (30, 20)]) + .build_and_execute(|| { + MaxPoolMembers::::set(None); + MaxPoolMembersPerPool::::set(None); - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { - member: 30, - pool_id: 1, - payout: 10 + 20 + 20, - commission: 0 - }] - ); - }); + // initial state of pool 1. + { + let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + + assert_eq!(reward_pool.last_recorded_total_payouts, 0); + assert_eq!(reward_pool.total_rewards_claimed, 0); + assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + + assert_eq!(member.last_recorded_reward_counter, 0.into()); + } + + // 20 unbonds without any rewards. + { + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); + let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); + assert_eq!(member.last_recorded_reward_counter, 0.into()); + assert_eq!(reward_pool.last_recorded_total_payouts, 0); + assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + } + + // some rewards come in. + Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + + // and 30 also unbonds half. + { + assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 10)); + let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); + // 30 reward in the system, and 40 points before this unbond to collect it, + // RewardCounter is 3/4. + assert_eq!( + member.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + assert_eq!( + reward_pool.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + } + + // 30 unbonds again, not change this time. + { + assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 5)); + let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); + assert_eq!( + member.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + assert_eq!( + reward_pool.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + } + + // 20 unbonds again, not change this time, just collecting their reward. + { + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); + assert_eq!( + member.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + assert_eq!(reward_pool.last_recorded_total_payouts, 30); + assert_eq!( + reward_pool.last_recorded_reward_counter, + RewardCounter::from_float(0.75) + ); + } + + // trigger 10's reward as well to see all of the payouts. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::Bonded { member: 30, pool_id: 1, bonded: 20, joined: true }, + Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, + Event::PaidOut { member: 30, pool_id: 1, payout: 15, commission: 0 }, + Event::Unbonded { member: 30, pool_id: 1, balance: 10, points: 10, era: 3 }, + Event::Unbonded { member: 30, pool_id: 1, balance: 5, points: 5, era: 3 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 7, commission: 0 }, + Event::Unbonded { member: 20, pool_id: 1, balance: 5, points: 5, era: 3 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 0 } + ] + ); + }) } #[test] - fn bond_extra_and_delayed_claim() { - ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); + fn rewards_are_rounded_down_depositor_collects_them() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + // initial balance of 10. - Balances::make_free_balance_be(&20, ed + 200); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1)); + assert_eq!(Balances::free_balance(&10), 35); + assert_eq!( + Balances::free_balance(&default_reward_account()), + Balances::minimum_balance() + ); - // 10 gets 10, 20 gets 20, 30 gets 10 - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + // some rewards come in. + Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); - // some claim. + // everyone claims assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // some dust (1) remains in the reward account. assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 } + Event::PaidOut { member: 10, pool_id: 1, payout: 13, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 26, commission: 0 } ] ); - // 20 has not claimed yet, more reward comes - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + // start dismantling the pool. + assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); + assert_ok!(fully_unbond_permissioned(20)); - // and 20 bonds more -- they should not have more share of this reward. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::FreeBalance(10))); + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + assert_ok!(fully_unbond_permissioned(10)); - // everyone claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + CurrentEra::set(6); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); assert_eq!( pool_events_since_last_call(), vec![ - // 20 + 40, which means the extra amount they bonded did not impact us. - Event::PaidOut { member: 20, pool_id: 1, payout: 60, commission: 0 }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 20, commission: 0 } + Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 3 }, + Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, + Event::MemberRemoved { pool_id: 1, member: 20 }, + Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 6 }, + Event::Withdrawn { member: 10, pool_id: 1, balance: 10, points: 10 }, + Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::Destroyed { pool_id: 1 } ] ); - // but in the next round of rewards, the extra10 they bonded has an impact. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); - - // everyone claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 45, commission: 0 } - ] - ); - }); + assert!(!Metadata::::contains_key(1)); + // original ed + ed put into reward account + reward + bond + dust. + assert_eq!(Balances::free_balance(&10), 35 + 5 + 13 + 10 + 1); + }) } #[test] - fn create_sets_recorded_data() { - ExtBuilder::default().build_and_execute(|| { - MaxPools::::set(None); - // pool 10 has already been created. - let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); + fn claim_payout_large_numbers() { + let unit = 10u128.pow(12); // akin to KSM + ExistentialDeposit::set(unit); + StakingMinBond::set(unit * 1000); - assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); - assert_eq!(reward_pool_10.total_rewards_claimed, 0); - assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); - - assert_eq!(member_10.last_recorded_reward_counter, 0.into()); - - // transfer some reward to pool 1. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + ExtBuilder::default() + .max_members(Some(4)) + .max_members_per_pool(Some(4)) + .add_members(vec![(20, 1500 * unit), (21, 2500 * unit), (22, 5000 * unit)]) + .build_and_execute(|| { + // some rewards come in. + assert_eq!(Balances::free_balance(&default_reward_account()), unit); + Balances::mutate_account(&default_reward_account(), |f| f.free += unit / 1000) + .unwrap(); - // create pool 2 - Balances::make_free_balance_be(&20, 100); - assert_ok!(Pools::create(RuntimeOrigin::signed(20), 10, 20, 20, 20)); + // everyone claims + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(21))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(22))); - // has no impact -- initial - let (member_20, _, reward_pool_20) = Pools::get_member_with_pools(&20).unwrap(); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 1000000000000000, + joined: true + }, + Event::Bonded { + member: 20, + pool_id: 1, + bonded: 1500000000000000, + joined: true + }, + Event::Bonded { + member: 21, + pool_id: 1, + bonded: 2500000000000000, + joined: true + }, + Event::Bonded { + member: 22, + pool_id: 1, + bonded: 5000000000000000, + joined: true + }, + Event::PaidOut { member: 10, pool_id: 1, payout: 100000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 150000000, commission: 0 }, + Event::PaidOut { member: 21, pool_id: 1, payout: 250000000, commission: 0 }, + Event::PaidOut { member: 22, pool_id: 1, payout: 500000000, commission: 0 } + ] + ); + }) + } +} - assert_eq!(reward_pool_20.last_recorded_total_payouts, 0); - assert_eq!(reward_pool_20.total_rewards_claimed, 0); - assert_eq!(reward_pool_20.last_recorded_reward_counter, 0.into()); +mod unbond { + use super::*; - assert_eq!(member_20.last_recorded_reward_counter, 0.into()); + #[test] + fn member_unbond_open() { + // depositor in pool, pool state open + // - member unbond above limit + // - member unbonds to 0 + // - member cannot unbond between within limit and 0 + ExtBuilder::default() + .min_join_bond(10) + .add_members(vec![(20, 20)]) + .build_and_execute(|| { + // can unbond to above limit + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 15); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 5); - // pre-fund the reward account of pool id 3 with some funds. - Balances::make_free_balance_be(&Pools::create_reward_account(3), 10); + // cannot go to below 10: + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(20), 20, 10), + Error::::MinimumBondNotMet + ); - // create pool 3 - Balances::make_free_balance_be(&30, 100); - assert_ok!(Pools::create(RuntimeOrigin::signed(30), 10, 30, 30, 30)); + // but can go to 0 + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 15)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); + }) + } - // reward counter is still the same. - let (member_30, _, reward_pool_30) = Pools::get_member_with_pools(&30).unwrap(); - assert_eq!( - Balances::free_balance(&Pools::create_reward_account(3)), - 10 + Balances::minimum_balance() - ); + #[test] + fn member_kicked() { + // depositor in pool, pool state blocked + // - member cannot be kicked to above limit + // - member cannot be kicked between within limit and 0 + // - member kicked to 0 + ExtBuilder::default() + .min_join_bond(10) + .add_members(vec![(20, 20)]) + .build_and_execute(|| { + unsafe_set_state(1, PoolState::Blocked); + let kicker = DEFAULT_ROLES.state_toggler.unwrap(); - assert_eq!(reward_pool_30.last_recorded_total_payouts, 0); - assert_eq!(reward_pool_30.total_rewards_claimed, 0); - assert_eq!(reward_pool_30.last_recorded_reward_counter, 0.into()); + // cannot be kicked to above the limit. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(kicker), 20, 5), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - assert_eq!(member_30.last_recorded_reward_counter, 0.into()); + // cannot go to below 10: + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(kicker), 20, 15), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - // and 30 can claim the reward now. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + // but they themselves can do an unbond + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 18); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 2); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Created { depositor: 20, pool_id: 2 }, - Event::Bonded { member: 20, pool_id: 2, bonded: 10, joined: true }, - Event::Created { depositor: 30, pool_id: 3 }, - Event::Bonded { member: 30, pool_id: 3, bonded: 10, joined: true }, - Event::PaidOut { member: 30, pool_id: 3, payout: 10, commission: 0 } - ] - ); - }) + // can be kicked to 0. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(kicker), 20, 18)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); + }) } #[test] - fn join_updates_recorded_data() { - ExtBuilder::default().build_and_execute(|| { - MaxPoolMembers::::set(None); - MaxPoolMembersPerPool::::set(None); - let join = |x, y| { - Balances::make_free_balance_be(&x, y + Balances::minimum_balance()); - assert_ok!(Pools::join(RuntimeOrigin::signed(x), y, 1)); - }; - - { - let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); - - assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); - assert_eq!(reward_pool_10.total_rewards_claimed, 0); - assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); - - assert_eq!(member_10.last_recorded_reward_counter, 0.into()); - } - - // someone joins without any rewards being issued. - { - join(20, 10); - let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); - // reward counter is 0 both before.. - assert_eq!(member.last_recorded_reward_counter, 0.into()); - assert_eq!(reward_pool.last_recorded_total_payouts, 0); - assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); - } + fn member_unbond_destroying() { + // depositor in pool, pool state destroying + // - member cannot be permissionlessly unbonded to above limit + // - member cannot be permissionlessly unbonded between within limit and 0 + // - member permissionlessly unbonded to 0 + ExtBuilder::default() + .min_join_bond(10) + .add_members(vec![(20, 20)]) + .build_and_execute(|| { + unsafe_set_state(1, PoolState::Destroying); + let random = 123; - // transfer some reward to pool 1. - Balances::mutate_account(&default_reward_account(), |f| f.free += 60).unwrap(); + // cannot be kicked to above the limit. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(random), 20, 5), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - { - join(30, 10); - let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); - assert_eq!(reward_pool.last_recorded_total_payouts, 60); - // explanation: we have a total of 20 points so far (excluding the 10 that just got - // bonded), and 60 unclaimed rewards. each share is then roughly worth of 3 units of - // rewards, thus reward counter is 3. member's reward counter is the same - assert_eq!(member.last_recorded_reward_counter, 3.into()); - assert_eq!(reward_pool.last_recorded_reward_counter, 3.into()); - } + // cannot go to below 10: + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(random), 20, 15), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - // someone else joins - { - join(40, 10); - let (member, _, reward_pool) = Pools::get_member_with_pools(&40).unwrap(); - // reward counter does not change since no rewards have came in. - assert_eq!(member.last_recorded_reward_counter, 3.into()); - assert_eq!(reward_pool.last_recorded_reward_counter, 3.into()); - assert_eq!(reward_pool.last_recorded_total_payouts, 60); - } + // but they themselves can do an unbond + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 18); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 2); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 30, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 40, pool_id: 1, bonded: 10, joined: true } - ] - ); - }) + // but can go to 0 + assert_ok!(Pools::unbond(RuntimeOrigin::signed(random), 20, 18)); + assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); + }) } #[test] - fn bond_extra_updates_recorded_data() { - ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { - MaxPoolMembers::::set(None); - MaxPoolMembersPerPool::::set(None); + fn depositor_unbond_open() { + // depositor in pool, pool state open + // - depositor unbonds to above limit + // - depositor cannot unbond to below limit or 0 + ExtBuilder::default().min_join_bond(10).build_and_execute(|| { + // give the depositor some extra funds. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // initial state of pool 1. - { - let (member_10, _, reward_pool_10) = Pools::get_member_with_pools(&10).unwrap(); + // can unbond to above the limit. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); - assert_eq!(reward_pool_10.last_recorded_total_payouts, 0); - assert_eq!(reward_pool_10.total_rewards_claimed, 0); - assert_eq!(reward_pool_10.last_recorded_reward_counter, 0.into()); + // cannot go to below 10: + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 10), + Error::::MinimumBondNotMet + ); - assert_eq!(member_10.last_recorded_reward_counter, 0.into()); - } + // cannot go to 0 either. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 15), + Error::::MinimumBondNotMet + ); + }) + } - Balances::make_free_balance_be(&10, 100); - Balances::make_free_balance_be(&20, 100); + #[test] + fn depositor_kick() { + // depositor in pool, pool state blocked + // - depositor can never be kicked. + ExtBuilder::default().min_join_bond(10).build_and_execute(|| { + // give the depositor some extra funds. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // 10 bonds extra without any rewards. - { - assert_ok!(Pools::bond_extra( - RuntimeOrigin::signed(10), - BondExtra::FreeBalance(10) - )); - let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - assert_eq!(member.last_recorded_reward_counter, 0.into()); - assert_eq!(reward_pool.last_recorded_total_payouts, 0); - assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); - } + // set the stage + unsafe_set_state(1, PoolState::Blocked); + let kicker = DEFAULT_ROLES.state_toggler.unwrap(); - // 10 bonds extra again with some rewards. This reward should be split equally between - // 10 and 20, as they both have equal points now. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + // cannot be kicked to above limit. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(kicker), 10, 5), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - { - assert_ok!(Pools::bond_extra( - RuntimeOrigin::signed(10), - BondExtra::FreeBalance(10) - )); - let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // explanation: before bond_extra takes place, there is 40 points and 30 balance in - // the system, RewardCounter is therefore 7.5 - assert_eq!(member.last_recorded_reward_counter, RewardCounter::from_float(0.75)); - assert_eq!( - reward_pool.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - } + // or below the limit + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(kicker), 10, 15), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - // 20 bonds extra again, without further rewards. - { - assert_ok!(Pools::bond_extra( - RuntimeOrigin::signed(20), - BondExtra::FreeBalance(10) - )); - let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); - assert_eq!(member.last_recorded_reward_counter, RewardCounter::from_float(0.75)); - assert_eq!( - reward_pool.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - } + // or 0. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(kicker), 10, 20), + Error::::DoesNotHavePermission + ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 10, pool_id: 1, payout: 15, commission: 0 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 15, commission: 0 }, - Event::Bonded { member: 20, pool_id: 1, bonded: 10, joined: false } - ] + // they themselves cannot do it either + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 20), + Error::::MinimumBondNotMet ); }) } #[test] - fn bond_extra_pending_rewards_works() { - ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { - MaxPoolMembers::::set(None); - MaxPoolMembersPerPool::::set(None); + fn depositor_unbond_destroying_permissionless() { + // depositor can never be permissionlessly unbonded. + ExtBuilder::default().min_join_bond(10).build_and_execute(|| { + // give the depositor some extra funds. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // pool receives some rewards. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); - System::reset_events(); + // set the stage + unsafe_set_state(1, PoolState::Destroying); + let random = 123; - // 10 cashes it out, and bonds it. - { - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // there is 30 points and 30 reward points in the system RC is 1. - assert_eq!(member.last_recorded_reward_counter, 1.into()); - assert_eq!(reward_pool.total_rewards_claimed, 10); - // these two are not updated -- only updated when the points change. - assert_eq!(reward_pool.last_recorded_total_payouts, 0); - assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + // cannot be kicked to above limit. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(random), 10, 5), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }] - ); - } + // or below the limit + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(random), 10, 15), + Error::::PartialUnbondNotAllowedPermissionlessly + ); - // 20 re-bonds it. - { - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards)); - let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - assert_eq!(member.last_recorded_reward_counter, 1.into()); - assert_eq!(reward_pool.total_rewards_claimed, 30); - // since points change, these two are updated. - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - assert_eq!(reward_pool.last_recorded_reward_counter, 1.into()); + // or 0. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(random), 10, 20), + Error::::DoesNotHavePermission + ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 20, commission: 0 }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: false } - ] - ); - } + // they themselves can do it in this case though. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 20)); }) } #[test] - fn unbond_updates_recorded_data() { + fn depositor_unbond_destroying_not_last_member() { + // deposit in pool, pool state destroying + // - depositor can never leave if there is another member in the pool. ExtBuilder::default() - .add_members(vec![(20, 20), (30, 20)]) + .min_join_bond(10) + .add_members(vec![(20, 20)]) .build_and_execute(|| { - MaxPoolMembers::::set(None); - MaxPoolMembersPerPool::::set(None); + // give the depositor some extra funds. + assert_ok!(Pools::bond_extra( + RuntimeOrigin::signed(10), + BondExtra::FreeBalance(10) + )); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // initial state of pool 1. - { - let (member, _, reward_pool) = Pools::get_member_with_pools(&10).unwrap(); + // set the stage + unsafe_set_state(1, PoolState::Destroying); - assert_eq!(reward_pool.last_recorded_total_payouts, 0); - assert_eq!(reward_pool.total_rewards_claimed, 0); - assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); + // can go above the limit + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); - assert_eq!(member.last_recorded_reward_counter, 0.into()); - } + // but not below the limit + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 10), + Error::::MinimumBondNotMet + ); - // 20 unbonds without any rewards. - { - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10)); - let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); - assert_eq!(member.last_recorded_reward_counter, 0.into()); - assert_eq!(reward_pool.last_recorded_total_payouts, 0); - assert_eq!(reward_pool.last_recorded_reward_counter, 0.into()); - } + // and certainly not zero + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 15), + Error::::MinimumBondNotMet + ); + }) + } - // some rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 30).unwrap(); + #[test] + fn depositor_unbond_destroying_last_member() { + // deposit in pool, pool state destroying + // - depositor can unbond to above limit always. + // - depositor cannot unbond to below limit if last. + // - depositor can unbond to 0 if last and destroying. + ExtBuilder::default().min_join_bond(10).build_and_execute(|| { + // give the depositor some extra funds. + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // and 30 also unbonds half. - { - assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 10)); - let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); - // 30 reward in the system, and 40 points before this unbond to collect it, - // RewardCounter is 3/4. - assert_eq!( - member.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - assert_eq!( - reward_pool.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - } + // set the stage + unsafe_set_state(1, PoolState::Destroying); - // 30 unbonds again, not change this time. - { - assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 5)); - let (member, _, reward_pool) = Pools::get_member_with_pools(&30).unwrap(); - assert_eq!( - member.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - assert_eq!( - reward_pool.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - } - - // 20 unbonds again, not change this time, just collecting their reward. - { - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - let (member, _, reward_pool) = Pools::get_member_with_pools(&20).unwrap(); - assert_eq!( - member.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - assert_eq!(reward_pool.last_recorded_total_payouts, 30); - assert_eq!( - reward_pool.last_recorded_reward_counter, - RewardCounter::from_float(0.75) - ); - } + // can unbond to above the limit. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); - // trigger 10's reward as well to see all of the payouts. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + // still cannot go to below limit + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 10), + Error::::MinimumBondNotMet + ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::Bonded { member: 30, pool_id: 1, bonded: 20, joined: true }, - Event::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::PaidOut { member: 30, pool_id: 1, payout: 15, commission: 0 }, - Event::Unbonded { member: 30, pool_id: 1, balance: 10, points: 10, era: 3 }, - Event::Unbonded { member: 30, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 7, commission: 0 }, - Event::Unbonded { member: 20, pool_id: 1, balance: 5, points: 5, era: 3 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 0 } - ] - ); - }) + // can go to 0 too. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 15)); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 20); + }) } #[test] - fn rewards_are_rounded_down_depositor_collects_them() { - ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { - // initial balance of 10. - - assert_eq!(Balances::free_balance(&10), 35); - assert_eq!( - Balances::free_balance(&default_reward_account()), - Balances::minimum_balance() - ); - - // some rewards come in. - Balances::mutate_account(&default_reward_account(), |f| f.free += 40).unwrap(); - - // everyone claims - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + fn unbond_of_1_works() { + ExtBuilder::default().build_and_execute(|| { + unsafe_set_state(1, PoolState::Destroying); + assert_ok!(fully_unbond_permissioned(10)); - // some dust (1) remains in the reward account. assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 13, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 26, commission: 0 } - ] + SubPoolsStorage::::get(1).unwrap().with_era, + unbonding_pools_with_era! { 0 + 3 => UnbondPool:: { points: 10, balance: 10 }} ); - // start dismantling the pool. - assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); - assert_ok!(fully_unbond_permissioned(20)); - - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - assert_ok!(fully_unbond_permissioned(10)); - - CurrentEra::set(6); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - Event::Unbonded { member: 20, pool_id: 1, balance: 20, points: 20, era: 3 }, - Event::Withdrawn { member: 20, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 20 }, - Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 6 }, - Event::Withdrawn { member: 10, pool_id: 1, balance: 10, points: 10 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, - Event::Destroyed { pool_id: 1 } - ] + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 0, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, + } + } ); - assert!(!Metadata::::contains_key(1)); - // original ed + ed put into reward account + reward + bond + dust. - assert_eq!(Balances::free_balance(&10), 35 + 5 + 13 + 10 + 1); - }) + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); + }); } #[test] - fn claim_payout_large_numbers() { - let unit = 10u128.pow(12); // akin to KSM - ExistentialDeposit::set(unit); - StakingMinBond::set(unit * 1000); - + fn unbond_of_3_works() { ExtBuilder::default() - .max_members(Some(4)) - .max_members_per_pool(Some(4)) - .add_members(vec![(20, 1500 * unit), (21, 2500 * unit), (22, 5000 * unit)]) + .add_members(vec![(40, 40), (550, 550)]) .build_and_execute(|| { - // some rewards come in. - assert_eq!(Balances::free_balance(&default_reward_account()), unit); - Balances::mutate_account(&default_reward_account(), |f| f.free += unit / 1000) - .unwrap(); + let ed = Balances::minimum_balance(); + // Given a slash from 600 -> 100 + StakingMock::set_bonded_balance(default_bonded_account(), 100); + // and unclaimed rewards of 600. + Balances::make_free_balance_be(&default_reward_account(), ed + 600); - // everyone claims - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(21))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(22))); + // When + assert_ok!(fully_unbond_permissioned(40)); + // Then + assert_eq!( + SubPoolsStorage::::get(1).unwrap().with_era, + unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 6, balance: 6 }} + ); + assert_eq!( + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 560, + roles: DEFAULT_ROLES, + state: PoolState::Open, + } + } + ); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, - pool_id: 1, - bonded: 1000000000000000, - joined: true - }, - Event::Bonded { - member: 20, - pool_id: 1, - bonded: 1500000000000000, - joined: true - }, - Event::Bonded { - member: 21, - pool_id: 1, - bonded: 2500000000000000, - joined: true - }, - Event::Bonded { - member: 22, - pool_id: 1, - bonded: 5000000000000000, - joined: true - }, - Event::PaidOut { member: 10, pool_id: 1, payout: 100000000, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 150000000, commission: 0 }, - Event::PaidOut { member: 21, pool_id: 1, payout: 250000000, commission: 0 }, - Event::PaidOut { member: 22, pool_id: 1, payout: 500000000, commission: 0 } + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, + Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, + Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }, + Event::Unbonded { member: 40, pool_id: 1, points: 6, balance: 6, era: 3 } ] ); - }) - } -} - -mod unbond { - use super::*; - - #[test] - fn member_unbond_open() { - // depositor in pool, pool state open - // - member unbond above limit - // - member unbonds to 0 - // - member cannot unbond between within limit and 0 - ExtBuilder::default() - .min_join_bond(10) - .add_members(vec![(20, 20)]) - .build_and_execute(|| { - // can unbond to above limit - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 15); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 5); - // cannot go to below 10: - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(20), 20, 10), - Error::::MinimumBondNotMet + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 94); + assert_eq!( + PoolMembers::::get(40).unwrap().unbonding_eras, + member_unbonding_eras!(0 + 3 => 6) ); + assert_eq!(Balances::free_balance(&40), 40 + 40); // We claim rewards when unbonding - // but can go to 0 - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 15)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); - }) - } + // When + unsafe_set_state(1, PoolState::Destroying); + assert_ok!(fully_unbond_permissioned(550)); - #[test] - fn member_kicked() { - // depositor in pool, pool state blocked - // - member cannot be kicked to above limit - // - member cannot be kicked between within limit and 0 - // - member kicked to 0 - ExtBuilder::default() - .min_join_bond(10) - .add_members(vec![(20, 20)]) - .build_and_execute(|| { - unsafe_set_state(1, PoolState::Blocked); - let kicker = DEFAULT_ROLES.state_toggler.unwrap(); + // Then + assert_eq!( + SubPoolsStorage::::get(&1).unwrap().with_era, + unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 98, balance: 98 }} + ); + assert_eq!( + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, + } + } + ); + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 2); + assert_eq!( + PoolMembers::::get(550).unwrap().unbonding_eras, + member_unbonding_eras!(0 + 3 => 92) + ); + assert_eq!(Balances::free_balance(&550), 550 + 550); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 550, pool_id: 1, payout: 550, commission: 0 }, + Event::Unbonded { + member: 550, + pool_id: 1, + points: 92, + balance: 92, + era: 3 + } + ] + ); - // cannot be kicked to above the limit. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(kicker), 20, 5), - Error::::PartialUnbondNotAllowedPermissionlessly + // When + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 40, 0)); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 550, 0)); + assert_ok!(fully_unbond_permissioned(10)); + + // Then + assert_eq!( + SubPoolsStorage::::get(1).unwrap().with_era, + unbonding_pools_with_era! { 6 => UnbondPool { points: 2, balance: 2 }} + ); + assert_eq!( + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 0, + roles: DEFAULT_ROLES, + state: PoolState::Destroying, + } + } ); + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); - // cannot go to below 10: - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(kicker), 20, 15), - Error::::PartialUnbondNotAllowedPermissionlessly + assert_eq!(Balances::free_balance(&550), 550 + 550 + 92); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 40, pool_id: 1, points: 6, balance: 6 }, + Event::MemberRemoved { pool_id: 1, member: 40 }, + Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, + Event::MemberRemoved { pool_id: 1, member: 550 }, + Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, + Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } + ] ); + }); + } - // but they themselves can do an unbond - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 18); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 2); + #[test] + fn unbond_merges_older_pools() { + ExtBuilder::default().with_check(1).build_and_execute(|| { + // Given + assert_eq!(StakingMock::bonding_duration(), 3); + SubPoolsStorage::::insert( + 1, + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 0 + 3 => UnbondPool { balance: 10, points: 100 }, + 1 + 3 => UnbondPool { balance: 20, points: 20 }, + 2 + 3 => UnbondPool { balance: 101, points: 101} + }, + }, + ); + unsafe_set_state(1, PoolState::Destroying); - // can be kicked to 0. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(kicker), 20, 18)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); - }) + // When + let current_era = 1 + TotalUnbondingPools::::get(); + CurrentEra::set(current_era); + + assert_ok!(fully_unbond_permissioned(10)); + + // Then + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: UnbondPool { balance: 10 + 20, points: 100 + 20 }, + with_era: unbonding_pools_with_era! { + 2 + 3 => UnbondPool { balance: 101, points: 101}, + current_era + 3 => UnbondPool { balance: 10, points: 10 }, + }, + }, + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10, era: 9 } + ] + ); + }); } #[test] - fn member_unbond_destroying() { - // depositor in pool, pool state destroying - // - member cannot be permissionlessly unbonded to above limit - // - member cannot be permissionlessly unbonded between within limit and 0 - // - member permissionlessly unbonded to 0 + fn unbond_kick_works() { + // Kick: the pool is blocked and the caller is either the root or state-toggler. ExtBuilder::default() - .min_join_bond(10) - .add_members(vec![(20, 20)]) + .add_members(vec![(100, 100), (200, 200)]) .build_and_execute(|| { - unsafe_set_state(1, PoolState::Destroying); - let random = 123; + // Given + unsafe_set_state(1, PoolState::Blocked); + let bonded_pool = BondedPool::::get(1).unwrap(); + assert_eq!(bonded_pool.roles.root.unwrap(), 900); + assert_eq!(bonded_pool.roles.nominator.unwrap(), 901); + assert_eq!(bonded_pool.roles.state_toggler.unwrap(), 902); - // cannot be kicked to above the limit. + // When the nominator tries to kick, then its a noop assert_noop!( - Pools::unbond(RuntimeOrigin::signed(random), 20, 5), - Error::::PartialUnbondNotAllowedPermissionlessly + Pools::fully_unbond(RuntimeOrigin::signed(901), 100), + Error::::NotKickerOrDestroying ); - // cannot go to below 10: - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(random), 20, 15), - Error::::PartialUnbondNotAllowedPermissionlessly + // When the root kicks then its ok + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(900), 100)); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, + Event::Bonded { member: 200, pool_id: 1, bonded: 200, joined: true }, + Event::Unbonded { + member: 100, + pool_id: 1, + points: 100, + balance: 100, + era: 3 + }, + ] ); - // but they themselves can do an unbond - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 18); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 2); + // When the state toggler kicks then its ok + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(902), 200)); - // but can go to 0 - assert_ok!(Pools::unbond(RuntimeOrigin::signed(random), 20, 18)); - assert_eq!(PoolMembers::::get(20).unwrap().active_points(), 0); - assert_eq!(PoolMembers::::get(20).unwrap().unbonding_points(), 20); - }) + assert_eq!( + pool_events_since_last_call(), + vec![Event::Unbonded { + member: 200, + pool_id: 1, + points: 200, + balance: 200, + era: 3 + }] + ); + + assert_eq!( + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, // Only 10 points because 200 + 100 was unbonded + roles: DEFAULT_ROLES, + state: PoolState::Blocked, + } + } + ); + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 10); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 0 + 3 => UnbondPool { points: 100 + 200, balance: 100 + 200 } + }, + } + ); + assert_eq!( + *UnbondingBalanceMap::get().get(&default_bonded_account()).unwrap(), + 100 + 200 + ); + }); } #[test] - fn depositor_unbond_open() { - // depositor in pool, pool state open - // - depositor unbonds to above limit - // - depositor cannot unbond to below limit or 0 - ExtBuilder::default().min_join_bond(10).build_and_execute(|| { - // give the depositor some extra funds. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - - // can unbond to above the limit. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); + fn unbond_permissionless_works() { + // Scenarios where non-admin accounts can unbond others + ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { + // Given the pool is blocked + unsafe_set_state(1, PoolState::Blocked); - // cannot go to below 10: + // A permissionless unbond attempt errors assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 10), - Error::::MinimumBondNotMet + Pools::fully_unbond(RuntimeOrigin::signed(420), 100), + Error::::NotKickerOrDestroying ); - // cannot go to 0 either. + // permissionless unbond must be full assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 15), - Error::::MinimumBondNotMet + Pools::unbond(RuntimeOrigin::signed(420), 100, 80), + Error::::PartialUnbondNotAllowedPermissionlessly, ); - }) - } - - #[test] - fn depositor_kick() { - // depositor in pool, pool state blocked - // - depositor can never be kicked. - ExtBuilder::default().min_join_bond(10).build_and_execute(|| { - // give the depositor some extra funds. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // set the stage - unsafe_set_state(1, PoolState::Blocked); - let kicker = DEFAULT_ROLES.state_toggler.unwrap(); + // Given the pool is destroying + unsafe_set_state(1, PoolState::Destroying); - // cannot be kicked to above limit. + // The depositor cannot be fully unbonded until they are the last member assert_noop!( - Pools::unbond(RuntimeOrigin::signed(kicker), 10, 5), - Error::::PartialUnbondNotAllowedPermissionlessly + Pools::fully_unbond(RuntimeOrigin::signed(10), 10), + Error::::MinimumBondNotMet, ); - // or below the limit - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(kicker), 10, 15), - Error::::PartialUnbondNotAllowedPermissionlessly + // Any account can unbond a member that is not the depositor + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(420), 100)); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, + Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 } + ] ); - // or 0. + // still permissionless unbond must be full assert_noop!( - Pools::unbond(RuntimeOrigin::signed(kicker), 10, 20), - Error::::DoesNotHavePermission + Pools::unbond(RuntimeOrigin::signed(420), 100, 80), + Error::::PartialUnbondNotAllowedPermissionlessly, ); - // they themselves cannot do it either + // Given the pool is blocked + unsafe_set_state(1, PoolState::Blocked); + + // The depositor cannot be unbonded assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 20), - Error::::MinimumBondNotMet + Pools::fully_unbond(RuntimeOrigin::signed(420), 10), + Error::::DoesNotHavePermission ); - }) - } - - #[test] - fn depositor_unbond_destroying_permissionless() { - // depositor can never be permissionlessly unbonded. - ExtBuilder::default().min_join_bond(10).build_and_execute(|| { - // give the depositor some extra funds. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - // set the stage + // Given the pools is destroying unsafe_set_state(1, PoolState::Destroying); - let random = 123; - // cannot be kicked to above limit. + // The depositor cannot be unbonded yet. assert_noop!( - Pools::unbond(RuntimeOrigin::signed(random), 10, 5), - Error::::PartialUnbondNotAllowedPermissionlessly + Pools::fully_unbond(RuntimeOrigin::signed(420), 10), + Error::::DoesNotHavePermission, ); - // or below the limit + // but when everyone is unbonded it can.. + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 100, 0)); + + // still permissionless unbond must be full. assert_noop!( - Pools::unbond(RuntimeOrigin::signed(random), 10, 15), - Error::::PartialUnbondNotAllowedPermissionlessly + Pools::unbond(RuntimeOrigin::signed(420), 10, 5), + Error::::PartialUnbondNotAllowedPermissionlessly, ); - // or 0. + // depositor can never be unbonded permissionlessly . assert_noop!( - Pools::unbond(RuntimeOrigin::signed(random), 10, 20), + Pools::fully_unbond(RuntimeOrigin::signed(420), 10), Error::::DoesNotHavePermission ); + // but depositor itself can do it. + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10)); - // they themselves can do it in this case though. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 20)); - }) + assert_eq!(BondedPools::::get(1).unwrap().points, 0); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 + 3 => UnbondPool { points: 10, balance: 10 } + } + } + ); + assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); + assert_eq!(*UnbondingBalanceMap::get().get(&default_bonded_account()).unwrap(), 10); + }); } #[test] - fn depositor_unbond_destroying_not_last_member() { - // deposit in pool, pool state destroying - // - depositor can never leave if there is another member in the pool. - ExtBuilder::default() - .min_join_bond(10) - .add_members(vec![(20, 20)]) - .build_and_execute(|| { - // give the depositor some extra funds. - assert_ok!(Pools::bond_extra( - RuntimeOrigin::signed(10), - BondExtra::FreeBalance(10) - )); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - - // set the stage - unsafe_set_state(1, PoolState::Destroying); - - // can go above the limit - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); + #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] + #[cfg_attr(not(debug_assertions), should_panic)] + fn unbond_errors_correctly() { + ExtBuilder::default().build_and_execute(|| { + assert_noop!( + Pools::fully_unbond(RuntimeOrigin::signed(11), 11), + Error::::PoolMemberNotFound + ); - // but not below the limit - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 10), - Error::::MinimumBondNotMet - ); + // Add the member + let member = PoolMember { pool_id: 2, points: 10, ..Default::default() }; + PoolMembers::::insert(11, member); - // and certainly not zero - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 15), - Error::::MinimumBondNotMet - ); - }) + let _ = Pools::fully_unbond(RuntimeOrigin::signed(11), 11); + }); } #[test] - fn depositor_unbond_destroying_last_member() { - // deposit in pool, pool state destroying - // - depositor can unbond to above limit always. - // - depositor cannot unbond to below limit if last. - // - depositor can unbond to 0 if last and destroying. - ExtBuilder::default().min_join_bond(10).build_and_execute(|| { - // give the depositor some extra funds. - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); + #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] + #[cfg_attr(not(debug_assertions), should_panic)] + fn unbond_panics_when_reward_pool_not_found() { + ExtBuilder::default().build_and_execute(|| { + let member = PoolMember { pool_id: 2, points: 10, ..Default::default() }; + PoolMembers::::insert(11, member); + BondedPool:: { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, + }, + } + .put(); - // set the stage - unsafe_set_state(1, PoolState::Destroying); + let _ = Pools::fully_unbond(RuntimeOrigin::signed(11), 11); + }); + } - // can unbond to above the limit. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 15); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 5); + #[test] + fn partial_unbond_era_tracking() { + ExtBuilder::default().ed(1).build_and_execute(|| { + // to make the depositor capable of withdrawing. + StakingMinBond::set(1); + MinCreateBond::::set(1); + MinJoinBond::::set(1); + assert_eq!(Pools::depositor_min_bond(), 1); - // still cannot go to below limit - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 10), - Error::::MinimumBondNotMet + // given + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); + assert_eq!(PoolMembers::::get(10).unwrap().pool_id, 1); + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!() ); + assert_eq!(BondedPool::::get(1).unwrap().points, 10); + assert!(SubPoolsStorage::::get(1).is_none()); + assert_eq!(CurrentEra::get(), 0); + assert_eq!(BondingDuration::get(), 3); - // can go to 0 too. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 15)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 0); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 20); - }) - } - - #[test] - fn unbond_of_1_works() { - ExtBuilder::default().build_and_execute(|| { + // so the depositor can leave, just keeps the test simpler. unsafe_set_state(1, PoolState::Destroying); - assert_ok!(fully_unbond_permissioned(10)); + // when: casual unbond + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); + + // then + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 9); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 1); + assert_eq!(BondedPool::::get(1).unwrap().points, 9); assert_eq!( - SubPoolsStorage::::get(1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool:: { points: 10, balance: 10 }} + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 1) ); - assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 0, - roles: DEFAULT_ROLES, - state: PoolState::Destroying, + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 1, balance: 1 } } } ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 3 } + ] + ); - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); - }); - } - - #[test] - fn unbond_of_3_works() { - ExtBuilder::default() - .add_members(vec![(40, 40), (550, 550)]) - .build_and_execute(|| { - let ed = Balances::minimum_balance(); - // Given a slash from 600 -> 100 - StakingMock::set_bonded_balance(default_bonded_account(), 100); - // and unclaimed rewards of 600. - Balances::make_free_balance_be(&default_reward_account(), ed + 600); - - // When - assert_ok!(fully_unbond_permissioned(40)); - - // Then - assert_eq!( - SubPoolsStorage::::get(1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 6, balance: 6 }} - ); - assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 3, - points: 560, - roles: DEFAULT_ROLES, - state: PoolState::Open, - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, - Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, - Event::PaidOut { member: 40, pool_id: 1, payout: 40, commission: 0 }, - Event::Unbonded { member: 40, pool_id: 1, points: 6, balance: 6, era: 3 } - ] - ); - - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 94); - assert_eq!( - PoolMembers::::get(40).unwrap().unbonding_eras, - member_unbonding_eras!(0 + 3 => 6) - ); - assert_eq!(Balances::free_balance(&40), 40 + 40); // We claim rewards when unbonding - - // When - unsafe_set_state(1, PoolState::Destroying); - assert_ok!(fully_unbond_permissioned(550)); - - // Then - assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 98, balance: 98 }} - ); - assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 3, - points: 10, - roles: DEFAULT_ROLES, - state: PoolState::Destroying, - } - } - ); - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 2); - assert_eq!( - PoolMembers::::get(550).unwrap().unbonding_eras, - member_unbonding_eras!(0 + 3 => 92) - ); - assert_eq!(Balances::free_balance(&550), 550 + 550); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 550, pool_id: 1, payout: 550, commission: 0 }, - Event::Unbonded { - member: 550, - pool_id: 1, - points: 92, - balance: 92, - era: 3 - } - ] - ); - - // When - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 40, 0)); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 550, 0)); - assert_ok!(fully_unbond_permissioned(10)); + // when: casual further unbond, same era. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); - // Then - assert_eq!( - SubPoolsStorage::::get(1).unwrap().with_era, - unbonding_pools_with_era! { 6 => UnbondPool { points: 2, balance: 2 }} - ); - assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 0, - roles: DEFAULT_ROLES, - state: PoolState::Destroying, - } + // then + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 4); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 6); + assert_eq!(BondedPool::::get(1).unwrap().points, 4); + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 6) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 6, balance: 6 } } - ); - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 3 }] + ); - assert_eq!(Balances::free_balance(&550), 550 + 550 + 92); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 40, pool_id: 1, points: 6, balance: 6 }, - Event::MemberRemoved { pool_id: 1, member: 40 }, - Event::Withdrawn { member: 550, pool_id: 1, points: 92, balance: 92 }, - Event::MemberRemoved { pool_id: 1, member: 550 }, - Event::PaidOut { member: 10, pool_id: 1, payout: 10, commission: 0 }, - Event::Unbonded { member: 10, pool_id: 1, points: 2, balance: 2, era: 6 } - ] - ); - }); - } + // when: casual further unbond, next era. + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); - #[test] - fn unbond_merges_older_pools() { - ExtBuilder::default().with_check(1).build_and_execute(|| { - // Given - assert_eq!(StakingMock::bonding_duration(), 3); - SubPoolsStorage::::insert( - 1, + // then + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 3); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 7); + assert_eq!(BondedPool::::get(1).unwrap().points, 3); + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 6, 4 => 1) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), SubPools { no_era: Default::default(), with_era: unbonding_pools_with_era! { - 0 + 3 => UnbondPool { balance: 10, points: 100 }, - 1 + 3 => UnbondPool { balance: 20, points: 20 }, - 2 + 3 => UnbondPool { balance: 101, points: 101} - }, - }, + 3 => UnbondPool { points: 6, balance: 6 }, + 4 => UnbondPool { points: 1, balance: 1 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 4 }] ); - unsafe_set_state(1, PoolState::Destroying); - - // When - let current_era = 1 + TotalUnbondingPools::::get(); - CurrentEra::set(current_era); - assert_ok!(fully_unbond_permissioned(10)); + // when: unbonding more than our active: error + assert_noop!( + frame_support::storage::with_storage_layer(|| Pools::unbond( + RuntimeOrigin::signed(10), + 10, + 5 + )), + Error::::MinimumBondNotMet + ); + // instead: + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); - // Then + // then + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 0); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 10); + assert_eq!(BondedPool::::get(1).unwrap().points, 0); + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 6, 4 => 4) + ); assert_eq!( SubPoolsStorage::::get(1).unwrap(), SubPools { - no_era: UnbondPool { balance: 10 + 20, points: 100 + 20 }, + no_era: Default::default(), with_era: unbonding_pools_with_era! { - 2 + 3 => UnbondPool { balance: 101, points: 101}, - current_era + 3 => UnbondPool { balance: 10, points: 10 }, - }, - }, + 3 => UnbondPool { points: 6, balance: 6 }, + 4 => UnbondPool { points: 4, balance: 4 } + } + } ); assert_eq!( pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10, era: 9 } - ] + vec![Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3, era: 4 }] ); }); } #[test] - fn unbond_kick_works() { - // Kick: the pool is blocked and the caller is either the root or state-toggler. - ExtBuilder::default() - .add_members(vec![(100, 100), (200, 200)]) - .build_and_execute(|| { - // Given - unsafe_set_state(1, PoolState::Blocked); - let bonded_pool = BondedPool::::get(1).unwrap(); - assert_eq!(bonded_pool.roles.root.unwrap(), 900); - assert_eq!(bonded_pool.roles.nominator.unwrap(), 901); - assert_eq!(bonded_pool.roles.state_toggler.unwrap(), 902); - - // When the nominator tries to kick, then its a noop - assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(901), 100), - Error::::NotKickerOrDestroying - ); - - // When the root kicks then its ok - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(900), 100)); + fn partial_unbond_max_chunks() { + ExtBuilder::default().add_members(vec![(20, 20)]).ed(1).build_and_execute(|| { + MaxUnbonding::set(2); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, - Event::Bonded { member: 200, pool_id: 1, bonded: 200, joined: true }, - Event::Unbonded { - member: 100, - pool_id: 1, - points: 100, - balance: 100, - era: 3 - }, - ] - ); + // given + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 3)); + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 2, 4 => 3) + ); - // When the state toggler kicks then its ok - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(902), 200)); + // when + CurrentEra::set(2); + assert_noop!( + frame_support::storage::with_storage_layer(|| Pools::unbond( + RuntimeOrigin::signed(20), + 20, + 4 + )), + Error::::MaxUnbondingLimit + ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { - member: 200, - pool_id: 1, - points: 200, - balance: 200, - era: 3 - }] - ); + // when + MaxUnbonding::set(3); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 1)); - assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 3, - points: 10, // Only 10 points because 200 + 100 was unbonded - roles: DEFAULT_ROLES, - state: PoolState::Blocked, - } - } - ); - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 10); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 0 + 3 => UnbondPool { points: 100 + 200, balance: 100 + 200 } - }, - } - ); - assert_eq!( - *UnbondingBalanceMap::get().get(&default_bonded_account()).unwrap(), - 100 + 200 - ); - }); + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 2, 4 => 3, 5 => 1) + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::Unbonded { member: 20, pool_id: 1, points: 2, balance: 2, era: 3 }, + Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 }, + Event::Unbonded { member: 20, pool_id: 1, points: 1, balance: 1, era: 5 } + ] + ); + }) } + // depositor can unbond only up to `MinCreateBond`. #[test] - fn unbond_permissionless_works() { - // Scenarios where non-admin accounts can unbond others - ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { - // Given the pool is blocked - unsafe_set_state(1, PoolState::Blocked); - - // A permissionless unbond attempt errors - assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(420), 100), - Error::::NotKickerOrDestroying - ); + fn depositor_permissioned_partial_unbond() { + ExtBuilder::default().ed(1).build_and_execute(|| { + // given + StakingMinBond::set(5); + assert_eq!(Pools::depositor_min_bond(), 5); - // permissionless unbond must be full - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(420), 100, 80), - Error::::PartialUnbondNotAllowedPermissionlessly, - ); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); - // Given the pool is destroying - unsafe_set_state(1, PoolState::Destroying); + // can unbond a bit.. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 7); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 3); - // The depositor cannot be fully unbonded until they are the last member + // but not less than 2 assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(10), 10), - Error::::MinimumBondNotMet, + Pools::unbond(RuntimeOrigin::signed(10), 10, 6), + Error::::MinimumBondNotMet ); - // Any account can unbond a member that is not the depositor - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(420), 100)); - assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, - Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 } + Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3, era: 3 } ] ); + }); + } - // still permissionless unbond must be full - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(420), 100, 80), - Error::::PartialUnbondNotAllowedPermissionlessly, - ); + #[test] + fn depositor_permissioned_partial_unbond_slashed() { + ExtBuilder::default().ed(1).build_and_execute(|| { + // given + assert_eq!(MinCreateBond::::get(), 2); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); - // Given the pool is blocked - unsafe_set_state(1, PoolState::Blocked); + // slash the default pool + StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 5); - // The depositor cannot be unbonded + // cannot unbond even 7, because the value of shares is now less. assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(420), 10), - Error::::DoesNotHavePermission + Pools::unbond(RuntimeOrigin::signed(10), 10, 7), + Error::::MinimumBondNotMet ); + }); + } - // Given the pools is destroying - unsafe_set_state(1, PoolState::Destroying); + #[test] + fn every_unbonding_triggers_payout() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + let initial_reward_account = Balances::free_balance(default_reward_account()); + assert_eq!(initial_reward_account, Balances::minimum_balance()); + assert_eq!(initial_reward_account, 5); - // The depositor cannot be unbonded yet. - assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(420), 10), - Error::::DoesNotHavePermission, + Balances::make_free_balance_be( + &default_reward_account(), + 4 * Balances::minimum_balance(), ); - // but when everyone is unbonded it can.. - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 100, 0)); - - // still permissionless unbond must be full. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(420), 10, 5), - Error::::PartialUnbondNotAllowedPermissionlessly, + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); + assert_eq!( + pool_events_since_last_call(), + vec![ + // 2/3 of ed, which is 20's share. + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, + Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2, era: 3 } + ] ); - // depositor can never be unbonded permissionlessly . - assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(420), 10), - Error::::DoesNotHavePermission + CurrentEra::set(1); + Balances::make_free_balance_be( + &default_reward_account(), + 4 * Balances::minimum_balance(), ); - // but depositor itself can do it. - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10)); - assert_eq!(BondedPools::::get(1).unwrap().points, 0); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 3)); assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 + 3 => UnbondPool { points: 10, balance: 10 } - } - } + pool_events_since_last_call(), + vec![ + // 2/3 of ed, which is 20's share. + Event::PaidOut { member: 20, pool_id: 1, payout: 6, commission: 0 }, + Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 } + ] ); - assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0); - assert_eq!(*UnbondingBalanceMap::get().get(&default_bonded_account()).unwrap(), 10); - }); - } - #[test] - #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] - #[cfg_attr(not(debug_assertions), should_panic)] - fn unbond_errors_correctly() { - ExtBuilder::default().build_and_execute(|| { - assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(11), 11), - Error::::PoolMemberNotFound + CurrentEra::set(2); + Balances::make_free_balance_be( + &default_reward_account(), + 4 * Balances::minimum_balance(), ); - // Add the member - let member = PoolMember { pool_id: 2, points: 10, ..Default::default() }; - PoolMembers::::insert(11, member); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, + Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 } + ] + ); - let _ = Pools::fully_unbond(RuntimeOrigin::signed(11), 11); + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 2, 4 => 3, 5 => 5) + ); }); } +} - #[test] - #[cfg_attr(debug_assertions, should_panic(expected = "Defensive failure has been triggered!"))] - #[cfg_attr(not(debug_assertions), should_panic)] - fn unbond_panics_when_reward_pool_not_found() { +mod pool_withdraw_unbonded { + use super::*; + + #[test] + fn pool_withdraw_unbonded_works() { ExtBuilder::default().build_and_execute(|| { - let member = PoolMember { pool_id: 2, points: 10, ..Default::default() }; - PoolMembers::::insert(11, member); - BondedPool:: { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 10, - roles: DEFAULT_ROLES, - state: PoolState::Open, - }, - } - .put(); + // Given 10 unbond'ed directly against the pool account + assert_ok!(StakingMock::unbond(&default_bonded_account(), 5)); + // and the pool account only has 10 balance + assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5)); + assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(10)); + assert_eq!(Balances::free_balance(&default_bonded_account()), 10); - let _ = Pools::fully_unbond(RuntimeOrigin::signed(11), 11); + // When + assert_ok!(Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0)); + + // Then there unbonding balance is no longer locked + assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5)); + assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(5)); + assert_eq!(Balances::free_balance(&default_bonded_account()), 10); }); } +} + +mod withdraw_unbonded { + use super::*; + use frame_support::bounded_btree_map; #[test] - fn partial_unbond_era_tracking() { - ExtBuilder::default().ed(1).build_and_execute(|| { - // to make the depositor capable of withdrawing. - StakingMinBond::set(1); - MinCreateBond::::set(1); - MinJoinBond::::set(1); - assert_eq!(Pools::depositor_min_bond(), 1); + fn withdraw_unbonded_works_against_slashed_no_era_sub_pool() { + ExtBuilder::default() + .add_members(vec![(40, 40), (550, 550)]) + .build_and_execute(|| { + // reduce the noise a bit. + let _ = balances_events_since_last_call(); - // given - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); - assert_eq!(PoolMembers::::get(10).unwrap().pool_id, 1); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!() - ); - assert_eq!(BondedPool::::get(1).unwrap().points, 10); - assert!(SubPoolsStorage::::get(1).is_none()); - assert_eq!(CurrentEra::get(), 0); - assert_eq!(BondingDuration::get(), 3); + // Given + assert_eq!(StakingMock::bonding_duration(), 3); + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(550), 550)); + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(40), 40)); + assert_eq!(Balances::free_balance(&default_bonded_account()), 600); - // so the depositor can leave, just keeps the test simpler. - unsafe_set_state(1, PoolState::Destroying); + let mut current_era = 1; + CurrentEra::set(current_era); - // when: casual unbond - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); + let mut sub_pools = SubPoolsStorage::::get(1).unwrap(); + let unbond_pool = sub_pools.with_era.get_mut(&3).unwrap(); + // Sanity check + assert_eq!(*unbond_pool, UnbondPool { points: 550 + 40, balance: 550 + 40 }); - // then - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 9); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 1); - assert_eq!(BondedPool::::get(1).unwrap().points, 9); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 1) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 1, balance: 1 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 3 } - ] - ); + // Simulate a slash to the pool with_era(current_era), decreasing the balance by + // half + { + unbond_pool.balance /= 2; // 295 + SubPoolsStorage::::insert(1, sub_pools); + // Update the equivalent of the unbonding chunks for the `StakingMock` + let mut x = UnbondingBalanceMap::get(); + *x.get_mut(&default_bonded_account()).unwrap() /= 5; + UnbondingBalanceMap::set(&x); + Balances::make_free_balance_be( + &default_bonded_account(), + Balances::free_balance(&default_bonded_account()) / 2, // 300 + ); + StakingMock::set_bonded_balance( + default_bonded_account(), + StakingMock::active_stake(&default_bonded_account()).unwrap() / 2, + ); + }; - // when: casual further unbond, same era. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 5)); + // Advance the current_era to ensure all `with_era` pools will be merged into + // `no_era` pool + current_era += TotalUnbondingPools::::get(); + CurrentEra::set(current_era); - // then - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 4); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 6); - assert_eq!(BondedPool::::get(1).unwrap().points, 4); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 6) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 6, balance: 6 } + // Simulate some other call to unbond that would merge `with_era` pools into + // `no_era` + let sub_pools = + SubPoolsStorage::::get(1).unwrap().maybe_merge_pools(current_era); + SubPoolsStorage::::insert(1, sub_pools); + + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: UnbondPool { points: 550 + 40, balance: 275 + 20 }, + with_era: Default::default() } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 3 }] - ); + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, + Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, + Event::Unbonded { + member: 550, + pool_id: 1, + points: 550, + balance: 550, + era: 3 + }, + Event::Unbonded { member: 40, pool_id: 1, points: 40, balance: 40, era: 3 }, + ] + ); + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::BalanceSet { + who: default_bonded_account(), + free: 300, + reserved: 0 + }] + ); - // when: casual further unbond, next era. - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); - // then - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 3); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 7); - assert_eq!(BondedPool::::get(1).unwrap().points, 3); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 6, 4 => 1) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 6, balance: 6 }, - 4 => UnbondPool { points: 1, balance: 1 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 4 }] - ); + // Then + assert_eq!( + SubPoolsStorage::::get(1).unwrap().no_era, + UnbondPool { points: 40, balance: 20 } + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 550 }, + Event::MemberRemoved { pool_id: 1, member: 550 } + ] + ); + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 }] + ); - // when: unbonding more than our active: error - assert_noop!( - frame_support::storage::with_storage_layer(|| Pools::unbond( - RuntimeOrigin::signed(10), - 10, - 5 - )), - Error::::MinimumBondNotMet - ); - // instead: - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); - // then - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 0); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 10); - assert_eq!(BondedPool::::get(1).unwrap().points, 0); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 6, 4 => 4) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 6, balance: 6 }, - 4 => UnbondPool { points: 4, balance: 4 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3, era: 4 }] - ); - }); + // Then + assert_eq!( + SubPoolsStorage::::get(1).unwrap().no_era, + UnbondPool { points: 0, balance: 0 } + ); + assert!(!PoolMembers::::contains_key(40)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 }, + Event::MemberRemoved { pool_id: 1, member: 40 } + ] + ); + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 }] + ); + + // now, finally, the depositor can take out its share. + unsafe_set_state(1, PoolState::Destroying); + assert_ok!(fully_unbond_permissioned(10)); + + current_era += 3; + CurrentEra::set(current_era); + + // when + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 }, + Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 }, + Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::Destroyed { pool_id: 1 } + ] + ); + assert!(!Metadata::::contains_key(1)); + assert_eq!( + balances_events_since_last_call(), + vec![ + BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, + BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } + ] + ); + }); } #[test] - fn partial_unbond_max_chunks() { - ExtBuilder::default().add_members(vec![(20, 20)]).ed(1).build_and_execute(|| { - MaxUnbonding::set(2); + fn withdraw_unbonded_works_against_slashed_with_era_sub_pools() { + ExtBuilder::default() + .add_members(vec![(40, 40), (550, 550)]) + .build_and_execute(|| { + let _ = balances_events_since_last_call(); - // given - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 3)); - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 2, 4 => 3) - ); + // Given + // current bond is 600, we slash it all to 300. + StakingMock::set_bonded_balance(default_bonded_account(), 300); + Balances::make_free_balance_be(&default_bonded_account(), 300); + assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(300)); - // when - CurrentEra::set(2); - assert_noop!( - frame_support::storage::with_storage_layer(|| Pools::unbond( - RuntimeOrigin::signed(20), - 20, - 4 - )), - Error::::MaxUnbondingLimit - ); + assert_ok!(fully_unbond_permissioned(40)); + assert_ok!(fully_unbond_permissioned(550)); - // when - MaxUnbonding::set(3); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 1)); + assert_eq!( + SubPoolsStorage::::get(&1).unwrap().with_era, + unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2 + 40 / 2, balance: 550 / 2 + 40 / 2 + }} + ); - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 2, 4 => 3, 5 => 1) - ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, + Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, + Event::Unbonded { member: 40, pool_id: 1, balance: 20, points: 20, era: 3 }, + Event::Unbonded { + member: 550, + pool_id: 1, + balance: 275, + points: 275, + era: 3, + } + ] + ); + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::BalanceSet { + who: default_bonded_account(), + free: 300, + reserved: 0 + },] + ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::Unbonded { member: 20, pool_id: 1, points: 2, balance: 2, era: 3 }, - Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 }, - Event::Unbonded { member: 20, pool_id: 1, points: 1, balance: 1, era: 5 } - ] - ); - }) - } + CurrentEra::set(StakingMock::bonding_duration()); - // depositor can unbond only up to `MinCreateBond`. - #[test] - fn depositor_permissioned_partial_unbond() { - ExtBuilder::default().ed(1).build_and_execute(|| { - // given - StakingMinBond::set(5); - assert_eq!(Pools::depositor_min_bond(), 5); + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); + // Then + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 20 }, + Event::MemberRemoved { pool_id: 1, member: 40 } + ] + ); - // can unbond a bit.. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 7); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 3); + assert_eq!( + SubPoolsStorage::::get(&1).unwrap().with_era, + unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2, balance: 550 / 2 }} + ); - // but not less than 2 - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 6), - Error::::MinimumBondNotMet - ); + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Unbonded { member: 10, pool_id: 1, points: 3, balance: 3, era: 3 } - ] - ); - }); - } + // Then + assert_eq!( + balances_events_since_last_call(), + vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 },] + ); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 275 }, + Event::MemberRemoved { pool_id: 1, member: 550 } + ] + ); + assert!(SubPoolsStorage::::get(&1).unwrap().with_era.is_empty()); - #[test] - fn depositor_permissioned_partial_unbond_slashed() { - ExtBuilder::default().ed(1).build_and_execute(|| { - // given - assert_eq!(MinCreateBond::::get(), 2); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 10); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 0); + // now, finally, the depositor can take out its share. + unsafe_set_state(1, PoolState::Destroying); + assert_ok!(fully_unbond_permissioned(10)); - // slash the default pool - StakingMock::set_bonded_balance(Pools::create_bonded_account(1), 5); + // because everyone else has left, the points + assert_eq!( + SubPoolsStorage::::get(&1).unwrap().with_era, + unbonding_pools_with_era! { 6 => UnbondPool { points: 5, balance: 5 }} + ); - // cannot unbond even 7, because the value of shares is now less. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 7), - Error::::MinimumBondNotMet - ); - }); - } + CurrentEra::set(CurrentEra::get() + 3); - #[test] - fn every_unbonding_triggers_payout() { - ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { - let initial_reward_account = Balances::free_balance(default_reward_account()); - assert_eq!(initial_reward_account, Balances::minimum_balance()); - assert_eq!(initial_reward_account, 5); + // set metadata to check that it's being removed on dissolve + assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); + assert!(Metadata::::contains_key(1)); - Balances::make_free_balance_be( - &default_reward_account(), - 4 * Balances::minimum_balance(), - ); + // when + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 2)); - assert_eq!( - pool_events_since_last_call(), - vec![ - // 2/3 of ed, which is 20's share. - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 20, pool_id: 1, payout: 10, commission: 0 }, - Event::Unbonded { member: 20, pool_id: 1, balance: 2, points: 2, era: 3 } - ] - ); + // then + assert_eq!(Balances::free_balance(&10), 10 + 35); + assert_eq!(Balances::free_balance(&default_bonded_account()), 0); - CurrentEra::set(1); - Balances::make_free_balance_be( - &default_reward_account(), - 4 * Balances::minimum_balance(), - ); + // in this test 10 also gets a fair share of the slash, because the slash was + // applied to the bonded account. + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 6 }, + Event::Withdrawn { member: 10, pool_id: 1, points: 5, balance: 5 }, + Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::Destroyed { pool_id: 1 } + ] + ); + assert!(!Metadata::::contains_key(1)); + assert_eq!( + balances_events_since_last_call(), + vec![ + BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, + BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } + ] + ); + }); + } - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 3)); + #[test] + fn withdraw_unbonded_handles_faulty_sub_pool_accounting() { + ExtBuilder::default().build_and_execute(|| { + // Given + assert_eq!(Balances::minimum_balance(), 5); + assert_eq!(Balances::free_balance(&10), 35); + assert_eq!(Balances::free_balance(&default_bonded_account()), 10); + unsafe_set_state(1, PoolState::Destroying); + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10)); + + // Simulate a slash that is not accounted for in the sub pools. + Balances::make_free_balance_be(&default_bonded_account(), 5); assert_eq!( - pool_events_since_last_call(), - vec![ - // 2/3 of ed, which is 20's share. - Event::PaidOut { member: 20, pool_id: 1, payout: 6, commission: 0 }, - Event::Unbonded { member: 20, pool_id: 1, points: 3, balance: 3, era: 4 } - ] + SubPoolsStorage::::get(1).unwrap().with_era, + //------------------------------balance decrease is not account for + unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 } } ); - CurrentEra::set(2); - Balances::make_free_balance_be( - &default_reward_account(), - 4 * Balances::minimum_balance(), - ); + CurrentEra::set(0 + 3); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 20, pool_id: 1, payout: 3, commission: 0 }, - Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 } - ] - ); + // When + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 2, 4 => 3, 5 => 5) - ); + // Then + assert_eq!(Balances::free_balance(10), 10 + 35); + assert_eq!(Balances::free_balance(&default_bonded_account()), 0); }); } -} - -mod pool_withdraw_unbonded { - use super::*; #[test] - fn pool_withdraw_unbonded_works() { - ExtBuilder::default().build_and_execute(|| { - // Given 10 unbond'ed directly against the pool account - assert_ok!(StakingMock::unbond(&default_bonded_account(), 5)); - // and the pool account only has 10 balance - assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5)); - assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(10)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 10); + fn withdraw_unbonded_errors_correctly() { + ExtBuilder::default().with_check(0).build_and_execute(|| { + // Insert the sub-pool + let sub_pools = SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 }}, + }; + SubPoolsStorage::::insert(1, sub_pools.clone()); - // When - assert_ok!(Pools::pool_withdraw_unbonded(RuntimeOrigin::signed(10), 1, 0)); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), + Error::::PoolMemberNotFound + ); - // Then there unbonding balance is no longer locked - assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(5)); - assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(5)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 10); + let mut member = PoolMember { pool_id: 1, points: 10, ..Default::default() }; + PoolMembers::::insert(11, member.clone()); + + // Simulate calling `unbond` + member.unbonding_eras = member_unbonding_eras!(3 + 0 => 10); + PoolMembers::::insert(11, member.clone()); + + // We are still in the bonding duration + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), + Error::::CannotWithdrawAny + ); + + // If we error the member does not get removed + assert_eq!(PoolMembers::::get(&11), Some(member)); + // and the sub pools do not get updated. + assert_eq!(SubPoolsStorage::::get(1).unwrap(), sub_pools) }); } -} - -mod withdraw_unbonded { - use super::*; - use frame_support::bounded_btree_map; #[test] - fn withdraw_unbonded_works_against_slashed_no_era_sub_pool() { + fn withdraw_unbonded_kick() { ExtBuilder::default() - .add_members(vec![(40, 40), (550, 550)]) + .add_members(vec![(100, 100), (200, 200)]) .build_and_execute(|| { - // reduce the noise a bit. - let _ = balances_events_since_last_call(); - // Given - assert_eq!(StakingMock::bonding_duration(), 3); - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(550), 550)); - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(40), 40)); - assert_eq!(Balances::free_balance(&default_bonded_account()), 600); - - let mut current_era = 1; - CurrentEra::set(current_era); - - let mut sub_pools = SubPoolsStorage::::get(1).unwrap(); - let unbond_pool = sub_pools.with_era.get_mut(&3).unwrap(); - // Sanity check - assert_eq!(*unbond_pool, UnbondPool { points: 550 + 40, balance: 550 + 40 }); - - // Simulate a slash to the pool with_era(current_era), decreasing the balance by - // half - { - unbond_pool.balance /= 2; // 295 - SubPoolsStorage::::insert(1, sub_pools); - // Update the equivalent of the unbonding chunks for the `StakingMock` - let mut x = UnbondingBalanceMap::get(); - *x.get_mut(&default_bonded_account()).unwrap() /= 5; - UnbondingBalanceMap::set(&x); - Balances::make_free_balance_be( - &default_bonded_account(), - Balances::free_balance(&default_bonded_account()) / 2, // 300 - ); - StakingMock::set_bonded_balance( - default_bonded_account(), - StakingMock::active_stake(&default_bonded_account()).unwrap() / 2, - ); - }; - - // Advance the current_era to ensure all `with_era` pools will be merged into - // `no_era` pool - current_era += TotalUnbondingPools::::get(); - CurrentEra::set(current_era); - - // Simulate some other call to unbond that would merge `with_era` pools into - // `no_era` - let sub_pools = - SubPoolsStorage::::get(1).unwrap().maybe_merge_pools(current_era); - SubPoolsStorage::::insert(1, sub_pools); - + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(100), 100)); + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(200), 200)); assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: UnbondPool { points: 550 + 40, balance: 275 + 20 }, - with_era: Default::default() + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 3, + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, + } } ); + CurrentEra::set(StakingMock::bonding_duration()); + + // Cannot kick when pool is open + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(902), 100, 0), + Error::::NotKickerOrDestroying + ); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, - Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, + Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, + Event::Bonded { member: 200, pool_id: 1, bonded: 200, joined: true }, Event::Unbonded { - member: 550, + member: 100, pool_id: 1, - points: 550, - balance: 550, + points: 100, + balance: 100, era: 3 }, - Event::Unbonded { member: 40, pool_id: 1, points: 40, balance: 40, era: 3 }, + Event::Unbonded { + member: 200, + pool_id: 1, + points: 200, + balance: 200, + era: 3 + } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::BalanceSet { - who: default_bonded_account(), - free: 300, - reserved: 0 - }] - ); - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); + // Given + unsafe_set_state(1, PoolState::Blocked); - // Then - assert_eq!( - SubPoolsStorage::::get(1).unwrap().no_era, - UnbondPool { points: 40, balance: 20 } + // Cannot kick as a nominator + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(901), 100, 0), + Error::::NotKickerOrDestroying ); + + // Can kick as root + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 100, 0)); + + // Can kick as state toggler + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 200, 0)); + + assert_eq!(Balances::free_balance(100), 100 + 100); + assert_eq!(Balances::free_balance(200), 200 + 200); + assert!(!PoolMembers::::contains_key(100)); + assert!(!PoolMembers::::contains_key(200)); + assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); assert_eq!( pool_events_since_last_call(), vec![ - Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 550 }, - Event::MemberRemoved { pool_id: 1, member: 550 } + Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, + Event::MemberRemoved { pool_id: 1, member: 100 }, + Event::Withdrawn { member: 200, pool_id: 1, points: 200, balance: 200 }, + Event::MemberRemoved { pool_id: 1, member: 200 } ] ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 }] - ); + }); + } - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); + #[test] + fn withdraw_unbonded_destroying_permissionless() { + ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { + // Given + assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(100), 100)); + assert_eq!( + BondedPool::::get(1).unwrap(), + BondedPool { + id: 1, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 2, + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, + } + } + ); + CurrentEra::set(StakingMock::bonding_duration()); + assert_eq!(Balances::free_balance(100), 100); - // Then - assert_eq!( - SubPoolsStorage::::get(1).unwrap().no_era, - UnbondPool { points: 0, balance: 0 } - ); - assert!(!PoolMembers::::contains_key(40)); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 }, - Event::MemberRemoved { pool_id: 1, member: 40 } - ] - ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 }] - ); + // Cannot permissionlessly withdraw + assert_noop!( + Pools::fully_unbond(RuntimeOrigin::signed(420), 100), + Error::::NotKickerOrDestroying + ); - // now, finally, the depositor can take out its share. - unsafe_set_state(1, PoolState::Destroying); - assert_ok!(fully_unbond_permissioned(10)); + // Given + unsafe_set_state(1, PoolState::Destroying); - current_era += 3; - CurrentEra::set(current_era); + // Can permissionlesly withdraw a member that is not the depositor + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(420), 100, 0)); - // when - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 }, - Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, - Event::Destroyed { pool_id: 1 } - ] - ); - assert!(!Metadata::::contains_key(1)); - assert_eq!( - balances_events_since_last_call(), - vec![ - BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, - BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } - ] - ); - }); + assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default(),); + assert_eq!(Balances::free_balance(100), 100 + 100); + assert!(!PoolMembers::::contains_key(100)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, + Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 }, + Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, + Event::MemberRemoved { pool_id: 1, member: 100 } + ] + ); + }); } #[test] - fn withdraw_unbonded_works_against_slashed_with_era_sub_pools() { - ExtBuilder::default() - .add_members(vec![(40, 40), (550, 550)]) - .build_and_execute(|| { - let _ = balances_events_since_last_call(); - - // Given - // current bond is 600, we slash it all to 300. - StakingMock::set_bonded_balance(default_bonded_account(), 300); - Balances::make_free_balance_be(&default_bonded_account(), 300); - assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(300)); - - assert_ok!(fully_unbond_permissioned(40)); - assert_ok!(fully_unbond_permissioned(550)); - - assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, - unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2 + 40 / 2, balance: 550 / 2 + 40 / 2 - }} - ); - - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 40, pool_id: 1, bonded: 40, joined: true }, - Event::Bonded { member: 550, pool_id: 1, bonded: 550, joined: true }, - Event::Unbonded { member: 40, pool_id: 1, balance: 20, points: 20, era: 3 }, - Event::Unbonded { - member: 550, - pool_id: 1, - balance: 275, - points: 275, - era: 3, - } - ] - ); - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::BalanceSet { - who: default_bonded_account(), - free: 300, - reserved: 0 - },] - ); + fn partial_withdraw_unbonded_depositor() { + ExtBuilder::default().ed(1).build_and_execute(|| { + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + unsafe_set_state(1, PoolState::Destroying); - CurrentEra::set(StakingMock::bonding_duration()); + // given + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 6)); + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 6, 4 => 1) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 6, balance: 6 }, + 4 => UnbondPool { points: 1, balance: 1 } + } + } + ); + assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 13); + assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 7); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, + Event::Unbonded { member: 10, pool_id: 1, points: 6, balance: 6, era: 3 }, + Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 4 } + ] + ); - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0)); + // when + CurrentEra::set(2); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0), + Error::::CannotWithdrawAny + ); - // Then - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 20 }, - Event::MemberRemoved { pool_id: 1, member: 40 } - ] - ); + // when + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, - unbonding_pools_with_era! { 3 => UnbondPool { points: 550 / 2, balance: 550 / 2 }} - ); + // then + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(4 => 1) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 4 => UnbondPool { points: 1, balance: 1 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 10, pool_id: 1, points: 6, balance: 6 }] + ); - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0)); + // when + CurrentEra::set(4); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - // Then - assert_eq!( - balances_events_since_last_call(), - vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 },] - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 550, pool_id: 1, balance: 275, points: 275 }, - Event::MemberRemoved { pool_id: 1, member: 550 } - ] - ); - assert!(SubPoolsStorage::::get(&1).unwrap().with_era.is_empty()); + // then + assert_eq!( + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!() + ); + assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 10, pool_id: 1, points: 1, balance: 1 },] + ); - // now, finally, the depositor can take out its share. - unsafe_set_state(1, PoolState::Destroying); - assert_ok!(fully_unbond_permissioned(10)); + // when repeating: + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0), + Error::::CannotWithdrawAny + ); + }); + } - // because everyone else has left, the points - assert_eq!( - SubPoolsStorage::::get(&1).unwrap().with_era, - unbonding_pools_with_era! { 6 => UnbondPool { points: 5, balance: 5 }} - ); - - CurrentEra::set(CurrentEra::get() + 3); - - // set metadata to check that it's being removed on dissolve - assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); - assert!(Metadata::::contains_key(1)); - - // when - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - - // then - assert_eq!(Balances::free_balance(&10), 10 + 35); - assert_eq!(Balances::free_balance(&default_bonded_account()), 0); + #[test] + fn partial_withdraw_unbonded_non_depositor() { + ExtBuilder::default().add_members(vec![(11, 10)]).build_and_execute(|| { + // given + assert_ok!(Pools::unbond(RuntimeOrigin::signed(11), 11, 6)); + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(11), 11, 1)); + assert_eq!( + PoolMembers::::get(11).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 6, 4 => 1) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 6, balance: 6 }, + 4 => UnbondPool { points: 1, balance: 1 } + } + } + ); + assert_eq!(PoolMembers::::get(11).unwrap().active_points(), 3); + assert_eq!(PoolMembers::::get(11).unwrap().unbonding_points(), 7); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 11, pool_id: 1, bonded: 10, joined: true }, + Event::Unbonded { member: 11, pool_id: 1, points: 6, balance: 6, era: 3 }, + Event::Unbonded { member: 11, pool_id: 1, points: 1, balance: 1, era: 4 } + ] + ); - // in this test 10 also gets a fair share of the slash, because the slash was - // applied to the bonded account. - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Unbonded { member: 10, pool_id: 1, points: 5, balance: 5, era: 6 }, - Event::Withdrawn { member: 10, pool_id: 1, points: 5, balance: 5 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, - Event::Destroyed { pool_id: 1 } - ] - ); - assert!(!Metadata::::contains_key(1)); - assert_eq!( - balances_events_since_last_call(), - vec![ - BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 }, - BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 } - ] - ); - }); - } + // when + CurrentEra::set(2); + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), + Error::::CannotWithdrawAny + ); - #[test] - fn withdraw_unbonded_handles_faulty_sub_pool_accounting() { - ExtBuilder::default().build_and_execute(|| { - // Given - assert_eq!(Balances::minimum_balance(), 5); - assert_eq!(Balances::free_balance(&10), 35); - assert_eq!(Balances::free_balance(&default_bonded_account()), 10); - unsafe_set_state(1, PoolState::Destroying); - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10)); + // when + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0)); - // Simulate a slash that is not accounted for in the sub pools. - Balances::make_free_balance_be(&default_bonded_account(), 5); + // then assert_eq!( - SubPoolsStorage::::get(1).unwrap().with_era, - //------------------------------balance decrease is not account for - unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 } } + PoolMembers::::get(11).unwrap().unbonding_eras, + member_unbonding_eras!(4 => 1) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 4 => UnbondPool { points: 1, balance: 1 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 11, pool_id: 1, points: 6, balance: 6 }] ); - CurrentEra::set(0 + 3); + // when + CurrentEra::set(4); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0)); - // When - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + // then + assert_eq!( + PoolMembers::::get(11).unwrap().unbonding_eras, + member_unbonding_eras!() + ); + assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 11, pool_id: 1, points: 1, balance: 1 }] + ); - // Then - assert_eq!(Balances::free_balance(10), 10 + 35); - assert_eq!(Balances::free_balance(&default_bonded_account()), 0); + // when repeating: + assert_noop!( + Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), + Error::::CannotWithdrawAny + ); }); } #[test] - fn withdraw_unbonded_errors_correctly() { - ExtBuilder::default().with_check(0).build_and_execute(|| { - // Insert the sub-pool - let sub_pools = SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { 0 + 3 => UnbondPool { points: 10, balance: 10 }}, - }; - SubPoolsStorage::::insert(1, sub_pools.clone()); - - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), - Error::::PoolMemberNotFound + fn full_multi_step_withdrawing_non_depositor() { + ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { + // given + assert_ok!(Pools::unbond(RuntimeOrigin::signed(100), 100, 75)); + assert_eq!( + PoolMembers::::get(100).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 75) ); - let mut member = PoolMember { pool_id: 1, points: 10, ..Default::default() }; - PoolMembers::::insert(11, member.clone()); - - // Simulate calling `unbond` - member.unbonding_eras = member_unbonding_eras!(3 + 0 => 10); - PoolMembers::::insert(11, member.clone()); + // progress one era and unbond the leftover. + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(100), 100, 25)); + assert_eq!( + PoolMembers::::get(100).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 75, 4 => 25) + ); - // We are still in the bonding duration assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), + Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0), Error::::CannotWithdrawAny ); - // If we error the member does not get removed - assert_eq!(PoolMembers::::get(&11), Some(member)); - // and the sub pools do not get updated. - assert_eq!(SubPoolsStorage::::get(1).unwrap(), sub_pools) - }); + // now the 75 should be free. + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, + Event::Unbonded { member: 100, pool_id: 1, points: 75, balance: 75, era: 3 }, + Event::Unbonded { member: 100, pool_id: 1, points: 25, balance: 25, era: 4 }, + Event::Withdrawn { member: 100, pool_id: 1, points: 75, balance: 75 }, + ] + ); + assert_eq!( + PoolMembers::::get(100).unwrap().unbonding_eras, + member_unbonding_eras!(4 => 25) + ); + + // the 25 should be free now, and the member removed. + CurrentEra::set(4); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0)); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Withdrawn { member: 100, pool_id: 1, points: 25, balance: 25 }, + Event::MemberRemoved { pool_id: 1, member: 100 } + ] + ); + }) } #[test] - fn withdraw_unbonded_kick() { + fn out_of_sync_unbonding_chunks() { + // the unbonding_eras in pool member are always fixed to the era at which they are unlocked, + // but the actual unbonding pools get pruned and might get combined in the no_era pool. + // Pools are only merged when one unbonds, so we unbond a little bit on every era to + // simulate this. ExtBuilder::default() - .add_members(vec![(100, 100), (200, 200)]) + .add_members(vec![(20, 100), (30, 100)]) .build_and_execute(|| { - // Given - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(100), 100)); - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(200), 200)); + System::reset_events(); + + // when + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 5)); + + // then member-local unbonding is pretty much in sync with the global pools. assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 3, - points: 10, - roles: DEFAULT_ROLES, - state: PoolState::Open, + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 5) + ); + assert_eq!( + PoolMembers::::get(30).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 5) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 10, balance: 10 } } } ); - CurrentEra::set(StakingMock::bonding_duration()); - - // Cannot kick when pool is open - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(902), 100, 0), - Error::::NotKickerOrDestroying - ); assert_eq!( pool_events_since_last_call(), vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, - Event::Bonded { member: 200, pool_id: 1, bonded: 200, joined: true }, - Event::Unbonded { - member: 100, - pool_id: 1, - points: 100, - balance: 100, - era: 3 - }, - Event::Unbonded { - member: 200, - pool_id: 1, - points: 200, - balance: 200, - era: 3 - } + Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 3 }, + Event::Unbonded { member: 30, pool_id: 1, points: 5, balance: 5, era: 3 }, ] ); - // Given - unsafe_set_state(1, PoolState::Blocked); + // when + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - // Cannot kick as a nominator - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(901), 100, 0), - Error::::NotKickerOrDestroying + // then still member-local unbonding is pretty much in sync with the global pools. + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 5, 4 => 5) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 10, balance: 10 }, + 4 => UnbondPool { points: 5, balance: 5 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 4 }] ); - // Can kick as root - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 100, 0)); + // when + CurrentEra::set(2); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - // Can kick as state toggler - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(900), 200, 0)); + // then still member-local unbonding is pretty much in sync with the global pools. + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 5, 4 => 5, 5 => 5) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 3 => UnbondPool { points: 10, balance: 10 }, + 4 => UnbondPool { points: 5, balance: 5 }, + 5 => UnbondPool { points: 5, balance: 5 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 }] + ); - assert_eq!(Balances::free_balance(100), 100 + 100); - assert_eq!(Balances::free_balance(200), 200 + 200); - assert!(!PoolMembers::::contains_key(100)); - assert!(!PoolMembers::::contains_key(200)); - assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); + // when + CurrentEra::set(5); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + + // then + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 5, 4 => 5, 5 => 5, 8 => 5) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + // era 3 is merged into no_era. + no_era: UnbondPool { points: 10, balance: 10 }, + with_era: unbonding_pools_with_era! { + 4 => UnbondPool { points: 5, balance: 5 }, + 5 => UnbondPool { points: 5, balance: 5 }, + 8 => UnbondPool { points: 5, balance: 5 } + } + } + ); assert_eq!( pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 }, - Event::Withdrawn { member: 200, pool_id: 1, points: 200, balance: 200 }, - Event::MemberRemoved { pool_id: 1, member: 200 } - ] + vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 8 }] ); - }); + + // now we start withdrawing unlocked bonds. + + // when + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); + // then + assert_eq!( + PoolMembers::::get(20).unwrap().unbonding_eras, + member_unbonding_eras!(8 => 5) + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + // era 3 is merged into no_era. + no_era: UnbondPool { points: 5, balance: 5 }, + with_era: unbonding_pools_with_era! { + 8 => UnbondPool { points: 5, balance: 5 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 20, pool_id: 1, points: 15, balance: 15 }] + ); + + // when + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(30), 30, 0)); + // then + assert_eq!( + PoolMembers::::get(30).unwrap().unbonding_eras, + member_unbonding_eras!() + ); + assert_eq!( + SubPoolsStorage::::get(1).unwrap(), + SubPools { + // era 3 is merged into no_era. + no_era: Default::default(), + with_era: unbonding_pools_with_era! { + 8 => UnbondPool { points: 5, balance: 5 } + } + } + ); + assert_eq!( + pool_events_since_last_call(), + vec![Event::Withdrawn { member: 30, pool_id: 1, points: 5, balance: 5 }] + ); + }) } #[test] - fn withdraw_unbonded_destroying_permissionless() { - ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { - // Given - assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(100), 100)); + fn full_multi_step_withdrawing_depositor() { + ExtBuilder::default().ed(1).build_and_execute(|| { + // depositor now has 20, they can unbond to 10. + assert_eq!(Pools::depositor_min_bond(), 10); + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + + // now they can. + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 7)); + + // progress one era and unbond the leftover. + CurrentEra::set(1); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); + assert_eq!( - BondedPool::::get(1).unwrap(), - BondedPool { - id: 1, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 2, - points: 10, - roles: DEFAULT_ROLES, - state: PoolState::Open, - } - } + PoolMembers::::get(10).unwrap().unbonding_eras, + member_unbonding_eras!(3 => 7, 4 => 3) ); - CurrentEra::set(StakingMock::bonding_duration()); - assert_eq!(Balances::free_balance(100), 100); - // Cannot permissionlessly withdraw + // they can't unbond to a value below 10 other than 0.. assert_noop!( - Pools::fully_unbond(RuntimeOrigin::signed(420), 100), - Error::::NotKickerOrDestroying + Pools::unbond(RuntimeOrigin::signed(10), 10, 5), + Error::::MinimumBondNotMet ); - // Given + // but not even full, because they pool is not yet destroying. + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 10), + Error::::MinimumBondNotMet + ); + + // but now they can. unsafe_set_state(1, PoolState::Destroying); + assert_noop!( + Pools::unbond(RuntimeOrigin::signed(10), 10, 5), + Error::::MinimumBondNotMet + ); + assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10)); - // Can permissionlesly withdraw a member that is not the depositor - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(420), 100, 0)); + // now the 7 should be free. + CurrentEra::set(3); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default(),); - assert_eq!(Balances::free_balance(100), 100 + 100); - assert!(!PoolMembers::::contains_key(100)); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, - Event::Unbonded { member: 100, pool_id: 1, points: 100, balance: 100, era: 3 }, - Event::Withdrawn { member: 100, pool_id: 1, points: 100, balance: 100 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, + Event::Unbonded { member: 10, pool_id: 1, balance: 7, points: 7, era: 3 }, + Event::Unbonded { member: 10, pool_id: 1, balance: 3, points: 3, era: 4 }, + Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 4 }, + Event::Withdrawn { member: 10, pool_id: 1, balance: 7, points: 7 } ] ); - }); - } - - #[test] - fn partial_withdraw_unbonded_depositor() { - ExtBuilder::default().ed(1).build_and_execute(|| { - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - unsafe_set_state(1, PoolState::Destroying); - - // given - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 6)); - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 1)); assert_eq!( PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 6, 4 => 1) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 6, balance: 6 }, - 4 => UnbondPool { points: 1, balance: 1 } - } - } + member_unbonding_eras!(4 => 13) ); - assert_eq!(PoolMembers::::get(10).unwrap().active_points(), 13); - assert_eq!(PoolMembers::::get(10).unwrap().unbonding_points(), 7); + + // the 13 should be free now, and the member removed. + CurrentEra::set(4); + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + assert_eq!( pool_events_since_last_call(), vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::Unbonded { member: 10, pool_id: 1, points: 6, balance: 6, era: 3 }, - Event::Unbonded { member: 10, pool_id: 1, points: 1, balance: 1, era: 4 } + Event::Withdrawn { member: 10, pool_id: 1, points: 13, balance: 13 }, + Event::MemberRemoved { pool_id: 1, member: 10 }, + Event::Destroyed { pool_id: 1 }, ] ); - - // when - CurrentEra::set(2); - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0), - Error::::CannotWithdrawAny + assert!(!Metadata::::contains_key(1)); + }) + } +} + +mod create { + use super::*; + + #[test] + fn create_works() { + ExtBuilder::default().build_and_execute(|| { + // next pool id is 2. + let next_pool_stash = Pools::create_bonded_account(2); + let ed = Balances::minimum_balance(); + + assert!(!BondedPools::::contains_key(2)); + assert!(!RewardPools::::contains_key(2)); + assert!(!PoolMembers::::contains_key(11)); + assert_err!( + StakingMock::active_stake(&next_pool_stash), + DispatchError::Other("balance not found") ); - // when - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); + assert_ok!(Pools::create( + RuntimeOrigin::signed(11), + StakingMock::minimum_nominator_bond(), + 123, + 456, + 789 + )); - // then + assert_eq!(Balances::free_balance(&11), 0); assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(4 => 1) + PoolMembers::::get(11).unwrap(), + PoolMember { + pool_id: 2, + points: StakingMock::minimum_nominator_bond(), + ..Default::default() + } ); assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 4 => UnbondPool { points: 1, balance: 1 } + BondedPool::::get(2).unwrap(), + BondedPool { + id: 2, + inner: BondedPoolInner { + commission: Commission::default(), + points: StakingMock::minimum_nominator_bond(), + member_counter: 1, + roles: PoolRoles { + depositor: 11, + root: Some(123), + nominator: Some(456), + state_toggler: Some(789) + }, + state: PoolState::Open, } } ); assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 10, pool_id: 1, points: 6, balance: 6 }] - ); - - // when - CurrentEra::set(4); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); - - // then - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!() + StakingMock::active_stake(&next_pool_stash).unwrap(), + StakingMock::minimum_nominator_bond() ); - assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 10, pool_id: 1, points: 1, balance: 1 },] - ); - - // when repeating: - assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0), - Error::::CannotWithdrawAny + RewardPools::::get(2).unwrap(), + RewardPool { ..Default::default() } ); - }); - } - #[test] - fn partial_withdraw_unbonded_non_depositor() { - ExtBuilder::default().add_members(vec![(11, 10)]).build_and_execute(|| { - // given - assert_ok!(Pools::unbond(RuntimeOrigin::signed(11), 11, 6)); - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(11), 11, 1)); - assert_eq!( - PoolMembers::::get(11).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 6, 4 => 1) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 6, balance: 6 }, - 4 => UnbondPool { points: 1, balance: 1 } - } - } - ); - assert_eq!(PoolMembers::::get(11).unwrap().active_points(), 3); - assert_eq!(PoolMembers::::get(11).unwrap().unbonding_points(), 7); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 11, pool_id: 1, bonded: 10, joined: true }, - Event::Unbonded { member: 11, pool_id: 1, points: 6, balance: 6, era: 3 }, - Event::Unbonded { member: 11, pool_id: 1, points: 1, balance: 1, era: 4 } + Event::Created { depositor: 11, pool_id: 2 }, + Event::Bonded { member: 11, pool_id: 2, bonded: 10, joined: true } ] ); + }); + } - // when - CurrentEra::set(2); + #[test] + fn create_errors_correctly() { + ExtBuilder::default().with_check(0).build_and_execute(|| { assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), - Error::::CannotWithdrawAny + Pools::create(RuntimeOrigin::signed(10), 420, 123, 456, 789), + Error::::AccountBelongsToOtherPool ); - // when - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0)); + // Given + assert_eq!(MinCreateBond::::get(), 2); + assert_eq!(StakingMock::minimum_nominator_bond(), 10); - // then - assert_eq!( - PoolMembers::::get(11).unwrap().unbonding_eras, - member_unbonding_eras!(4 => 1) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 4 => UnbondPool { points: 1, balance: 1 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 11, pool_id: 1, points: 6, balance: 6 }] + // Then + assert_noop!( + Pools::create(RuntimeOrigin::signed(11), 9, 123, 456, 789), + Error::::MinimumBondNotMet ); - // when - CurrentEra::set(4); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0)); + // Given + MinCreateBond::::put(20); - // then - assert_eq!( - PoolMembers::::get(11).unwrap().unbonding_eras, - member_unbonding_eras!() + // Then + assert_noop!( + Pools::create(RuntimeOrigin::signed(11), 19, 123, 456, 789), + Error::::MinimumBondNotMet ); - assert_eq!(SubPoolsStorage::::get(1).unwrap(), Default::default()); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 11, pool_id: 1, points: 1, balance: 1 }] + + // Given + BondedPool:: { + id: 2, + inner: BondedPoolInner { + commission: Commission::default(), + member_counter: 1, + points: 10, + roles: DEFAULT_ROLES, + state: PoolState::Open, + }, + } + .put(); + assert_eq!(MaxPools::::get(), Some(2)); + assert_eq!(BondedPools::::count(), 2); + + // Then + assert_noop!( + Pools::create(RuntimeOrigin::signed(11), 20, 123, 456, 789), + Error::::MaxPools ); - // when repeating: + // Given + assert_eq!(PoolMembers::::count(), 1); + MaxPools::::put(3); + MaxPoolMembers::::put(1); + Balances::make_free_balance_be(&11, 5 + 20); + + // Then + let create = RuntimeCall::Pools(crate::Call::::create { + amount: 20, + root: 11, + nominator: 11, + state_toggler: 11, + }); assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(11), 11, 0), - Error::::CannotWithdrawAny + create.dispatch(RuntimeOrigin::signed(11)), + Error::::MaxPoolMembers ); }); } #[test] - fn full_multi_step_withdrawing_non_depositor() { - ExtBuilder::default().add_members(vec![(100, 100)]).build_and_execute(|| { - // given - assert_ok!(Pools::unbond(RuntimeOrigin::signed(100), 100, 75)); - assert_eq!( - PoolMembers::::get(100).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 75) - ); + fn create_with_pool_id_works() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); - // progress one era and unbond the leftover. - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(100), 100, 25)); - assert_eq!( - PoolMembers::::get(100).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 75, 4 => 25) + Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); + assert_ok!(Pools::create( + RuntimeOrigin::signed(11), + StakingMock::minimum_nominator_bond(), + 123, + 456, + 789 + )); + + assert_eq!(Balances::free_balance(&11), 0); + // delete the initial pool created, then pool_Id `1` will be free + + assert_noop!( + Pools::create_with_pool_id(RuntimeOrigin::signed(12), 20, 234, 654, 783, 1), + Error::::PoolIdInUse ); assert_noop!( - Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0), - Error::::CannotWithdrawAny + Pools::create_with_pool_id(RuntimeOrigin::signed(12), 20, 234, 654, 783, 3), + Error::::InvalidPoolId ); - // now the 75 should be free. + // start dismantling the pool. + assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); + assert_ok!(fully_unbond_permissioned(10)); + CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0)); - assert_eq!( + assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 10)); + + assert_ok!(Pools::create_with_pool_id(RuntimeOrigin::signed(10), 20, 234, 654, 783, 1)); + }); + } +} + +mod nominate { + use super::*; + + #[test] + fn nominate_works() { + ExtBuilder::default().build_and_execute(|| { + // Depositor can't nominate + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(10), 1, vec![21]), + Error::::NotNominator + ); + + // State toggler can't nominate + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(902), 1, vec![21]), + Error::::NotNominator + ); + + // Root can nominate + assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21])); + assert_eq!(Nominations::get().unwrap(), vec![21]); + + // Nominator can nominate + assert_ok!(Pools::nominate(RuntimeOrigin::signed(901), 1, vec![31])); + assert_eq!(Nominations::get().unwrap(), vec![31]); + + // Can't nominate for a pool that doesn't exist + assert_noop!( + Pools::nominate(RuntimeOrigin::signed(902), 123, vec![21]), + Error::::PoolNotFound + ); + }); + } +} + +mod set_state { + use super::*; + + #[test] + fn set_state_works() { + ExtBuilder::default().build_and_execute(|| { + // Given + assert_ok!(BondedPool::::get(1).unwrap().ok_to_be_open()); + + // Only the root and state toggler can change the state when the pool is ok to be open. + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked), + Error::::CanNotChangeState + ); + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(901), 1, PoolState::Blocked), + Error::::CanNotChangeState + ); + + // Root can change state + assert_ok!(Pools::set_state(RuntimeOrigin::signed(900), 1, PoolState::Blocked)); + + assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 100, pool_id: 1, bonded: 100, joined: true }, - Event::Unbonded { member: 100, pool_id: 1, points: 75, balance: 75, era: 3 }, - Event::Unbonded { member: 100, pool_id: 1, points: 25, balance: 25, era: 4 }, - Event::Withdrawn { member: 100, pool_id: 1, points: 75, balance: 75 }, + Event::StateChanged { pool_id: 1, new_state: PoolState::Blocked } ] ); - assert_eq!( - PoolMembers::::get(100).unwrap().unbonding_eras, - member_unbonding_eras!(4 => 25) + + assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Blocked); + + // State toggler can change state + assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); + assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); + + // If the pool is destroying, then no one can set state + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(900), 1, PoolState::Blocked), + Error::::CanNotChangeState + ); + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Blocked), + Error::::CanNotChangeState + ); + + // If the pool is not ok to be open, then anyone can set it to destroying + + // Given + unsafe_set_state(1, PoolState::Open); + let mut bonded_pool = BondedPool::::get(1).unwrap(); + bonded_pool.points = 100; + bonded_pool.put(); + // When + assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying)); + // Then + assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); + + // Given + Balances::make_free_balance_be(&default_bonded_account(), Balance::max_value() / 10); + unsafe_set_state(1, PoolState::Open); + // When + assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying)); + // Then + assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); + + // If the pool is not ok to be open, it cannot be permissionlessly set to a state that + // isn't destroying + unsafe_set_state(1, PoolState::Open); + assert_noop!( + Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Blocked), + Error::::CanNotChangeState ); - // the 25 should be free now, and the member removed. - CurrentEra::set(4); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(100), 100, 0)); assert_eq!( pool_events_since_last_call(), vec![ - Event::Withdrawn { member: 100, pool_id: 1, points: 25, balance: 25 }, - Event::MemberRemoved { pool_id: 1, member: 100 } + Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, + Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying } ] ); - }) + }); } +} - #[test] - fn out_of_sync_unbonding_chunks() { - // the unbonding_eras in pool member are always fixed to the era at which they are unlocked, - // but the actual unbonding pools get pruned and might get combined in the no_era pool. - // Pools are only merged when one unbonds, so we unbond a little bit on every era to - // simulate this. - ExtBuilder::default() - .add_members(vec![(20, 100), (30, 100)]) - .build_and_execute(|| { - System::reset_events(); - - // when - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(30), 30, 5)); +mod set_metadata { + use super::*; - // then member-local unbonding is pretty much in sync with the global pools. - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 5) - ); - assert_eq!( - PoolMembers::::get(30).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 5) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 10, balance: 10 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 3 }, - Event::Unbonded { member: 30, pool_id: 1, points: 5, balance: 5, era: 3 }, - ] - ); + #[test] + fn set_metadata_works() { + ExtBuilder::default().build_and_execute(|| { + // Root can set metadata + assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); + assert_eq!(Metadata::::get(1), vec![1, 1]); - // when - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + // State toggler can set metadata + assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(902), 1, vec![2, 2])); + assert_eq!(Metadata::::get(1), vec![2, 2]); - // then still member-local unbonding is pretty much in sync with the global pools. - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 5, 4 => 5) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 10, balance: 10 }, - 4 => UnbondPool { points: 5, balance: 5 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 4 }] - ); + // Depositor can't set metadata + assert_noop!( + Pools::set_metadata(RuntimeOrigin::signed(10), 1, vec![3, 3]), + Error::::DoesNotHavePermission + ); - // when - CurrentEra::set(2); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); + // Nominator can't set metadata + assert_noop!( + Pools::set_metadata(RuntimeOrigin::signed(901), 1, vec![3, 3]), + Error::::DoesNotHavePermission + ); - // then still member-local unbonding is pretty much in sync with the global pools. - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 5, 4 => 5, 5 => 5) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 3 => UnbondPool { points: 10, balance: 10 }, - 4 => UnbondPool { points: 5, balance: 5 }, - 5 => UnbondPool { points: 5, balance: 5 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 5 }] - ); + // Metadata cannot be longer than `MaxMetadataLen` + assert_noop!( + Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1, 1]), + Error::::MetadataExceedsMaxLen + ); + }); + } +} - // when - CurrentEra::set(5); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 5)); - - // then - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 5, 4 => 5, 5 => 5, 8 => 5) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - // era 3 is merged into no_era. - no_era: UnbondPool { points: 10, balance: 10 }, - with_era: unbonding_pools_with_era! { - 4 => UnbondPool { points: 5, balance: 5 }, - 5 => UnbondPool { points: 5, balance: 5 }, - 8 => UnbondPool { points: 5, balance: 5 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Unbonded { member: 20, pool_id: 1, points: 5, balance: 5, era: 8 }] - ); - - // now we start withdrawing unlocked bonds. - - // when - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0)); - // then - assert_eq!( - PoolMembers::::get(20).unwrap().unbonding_eras, - member_unbonding_eras!(8 => 5) - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - // era 3 is merged into no_era. - no_era: UnbondPool { points: 5, balance: 5 }, - with_era: unbonding_pools_with_era! { - 8 => UnbondPool { points: 5, balance: 5 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 20, pool_id: 1, points: 15, balance: 15 }] - ); - - // when - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(30), 30, 0)); - // then - assert_eq!( - PoolMembers::::get(30).unwrap().unbonding_eras, - member_unbonding_eras!() - ); - assert_eq!( - SubPoolsStorage::::get(1).unwrap(), - SubPools { - // era 3 is merged into no_era. - no_era: Default::default(), - with_era: unbonding_pools_with_era! { - 8 => UnbondPool { points: 5, balance: 5 } - } - } - ); - assert_eq!( - pool_events_since_last_call(), - vec![Event::Withdrawn { member: 30, pool_id: 1, points: 5, balance: 5 }] - ); - }) - } +mod set_configs { + use super::*; #[test] - fn full_multi_step_withdrawing_depositor() { - ExtBuilder::default().ed(1).build_and_execute(|| { - // depositor now has 20, they can unbond to 10. - assert_eq!(Pools::depositor_min_bond(), 10); - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); + fn set_configs_works() { + ExtBuilder::default().build_and_execute(|| { + // Setting works + assert_ok!(Pools::set_configs( + RuntimeOrigin::root(), + ConfigOp::Set(1 as Balance), + ConfigOp::Set(2 as Balance), + ConfigOp::Set(3u32), + ConfigOp::Set(4u32), + ConfigOp::Set(5u32), + ConfigOp::Set(Perbill::from_percent(6)) + )); + assert_eq!(MinJoinBond::::get(), 1); + assert_eq!(MinCreateBond::::get(), 2); + assert_eq!(MaxPools::::get(), Some(3)); + assert_eq!(MaxPoolMembers::::get(), Some(4)); + assert_eq!(MaxPoolMembersPerPool::::get(), Some(5)); + assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::from_percent(6))); - // now they can. - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 7)); + // Noop does nothing + assert_storage_noop!(assert_ok!(Pools::set_configs( + RuntimeOrigin::root(), + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ConfigOp::Noop, + ))); - // progress one era and unbond the leftover. - CurrentEra::set(1); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 3)); + // Removing works + assert_ok!(Pools::set_configs( + RuntimeOrigin::root(), + ConfigOp::Remove, + ConfigOp::Remove, + ConfigOp::Remove, + ConfigOp::Remove, + ConfigOp::Remove, + ConfigOp::Remove, + )); + assert_eq!(MinJoinBond::::get(), 0); + assert_eq!(MinCreateBond::::get(), 0); + assert_eq!(MaxPools::::get(), None); + assert_eq!(MaxPoolMembers::::get(), None); + assert_eq!(MaxPoolMembersPerPool::::get(), None); + assert_eq!(GlobalMaxCommission::::get(), None); + }); + } +} - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(3 => 7, 4 => 3) - ); +mod bond_extra { + use super::*; + use crate::Event; - // they can't unbond to a value below 10 other than 0.. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 5), - Error::::MinimumBondNotMet - ); + #[test] + fn bond_extra_from_free_balance_creator() { + ExtBuilder::default().build_and_execute(|| { + // 10 is the owner and a member in pool 1, give them some more funds. + Balances::make_free_balance_be(&10, 100); - // but not even full, because they pool is not yet destroying. - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 10), - Error::::MinimumBondNotMet - ); + // given + assert_eq!(PoolMembers::::get(10).unwrap().points, 10); + assert_eq!(BondedPools::::get(1).unwrap().points, 10); + assert_eq!(Balances::free_balance(10), 100); - // but now they can. - unsafe_set_state(1, PoolState::Destroying); - assert_noop!( - Pools::unbond(RuntimeOrigin::signed(10), 10, 5), - Error::::MinimumBondNotMet - ); - assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10)); + // when + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - // now the 7 should be free. - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + // then + assert_eq!(Balances::free_balance(10), 90); + assert_eq!(PoolMembers::::get(10).unwrap().points, 20); + assert_eq!(BondedPools::::get(1).unwrap().points, 20); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false }, - Event::Unbonded { member: 10, pool_id: 1, balance: 7, points: 7, era: 3 }, - Event::Unbonded { member: 10, pool_id: 1, balance: 3, points: 3, era: 4 }, - Event::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 4 }, - Event::Withdrawn { member: 10, pool_id: 1, balance: 7, points: 7 } + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false } ] ); - assert_eq!( - PoolMembers::::get(10).unwrap().unbonding_eras, - member_unbonding_eras!(4 => 13) - ); - // the 13 should be free now, and the member removed. - CurrentEra::set(4); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0)); + // when + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(20))); + + // then + assert_eq!(Balances::free_balance(10), 70); + assert_eq!(PoolMembers::::get(10).unwrap().points, 40); + assert_eq!(BondedPools::::get(1).unwrap().points, 40); assert_eq!( pool_events_since_last_call(), - vec![ - Event::Withdrawn { member: 10, pool_id: 1, points: 13, balance: 13 }, - Event::MemberRemoved { pool_id: 1, member: 10 }, - Event::Destroyed { pool_id: 1 }, - ] + vec![Event::Bonded { member: 10, pool_id: 1, bonded: 20, joined: false }] ); - assert!(!Metadata::::contains_key(1)); }) } -} - -mod create { - use super::*; #[test] - fn create_works() { + fn bond_extra_from_rewards_creator() { ExtBuilder::default().build_and_execute(|| { - // next pool id is 2. - let next_pool_stash = Pools::create_bonded_account(2); - let ed = Balances::minimum_balance(); + // put some money in the reward account, all of which will belong to 10 as the only + // member of the pool. + Balances::make_free_balance_be(&default_reward_account(), 7); + // ... if which only 2 is claimable to make sure the reward account does not die. + let claimable_reward = 7 - ExistentialDeposit::get(); - assert!(!BondedPools::::contains_key(2)); - assert!(!RewardPools::::contains_key(2)); - assert!(!PoolMembers::::contains_key(11)); - assert_err!( - StakingMock::active_stake(&next_pool_stash), - DispatchError::Other("balance not found") - ); + // given + assert_eq!(PoolMembers::::get(10).unwrap().points, 10); + assert_eq!(BondedPools::::get(1).unwrap().points, 10); + assert_eq!(Balances::free_balance(10), 35); - Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); - assert_ok!(Pools::create( - RuntimeOrigin::signed(11), - StakingMock::minimum_nominator_bond(), - 123, - 456, - 789 - )); + // when + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); + + // then + assert_eq!(Balances::free_balance(10), 35); + assert_eq!(PoolMembers::::get(10).unwrap().points, 10 + claimable_reward); + assert_eq!(BondedPools::::get(1).unwrap().points, 10 + claimable_reward); - assert_eq!(Balances::free_balance(&11), 0); assert_eq!( - PoolMembers::::get(11).unwrap(), - PoolMember { - pool_id: 2, - points: StakingMock::minimum_nominator_bond(), - ..Default::default() - } - ); - assert_eq!( - BondedPool::::get(2).unwrap(), - BondedPool { - id: 2, - inner: BondedPoolInner { - commission: Commission::default(), - points: StakingMock::minimum_nominator_bond(), - member_counter: 1, - roles: PoolRoles { - depositor: 11, - root: Some(123), - nominator: Some(456), - state_toggler: Some(789) - }, - state: PoolState::Open, - } - } - ); - assert_eq!( - StakingMock::active_stake(&next_pool_stash).unwrap(), - StakingMock::minimum_nominator_bond() - ); - assert_eq!( - RewardPools::::get(2).unwrap(), - RewardPool { ..Default::default() } + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: claimable_reward, + commission: 0 + }, + Event::Bonded { + member: 10, + pool_id: 1, + bonded: claimable_reward, + joined: false + } + ] ); + }) + } + + #[test] + fn bond_extra_from_rewards_joiner() { + ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { + // put some money in the reward account, all of which will belong to 10 as the only + // member of the pool. + Balances::make_free_balance_be(&default_reward_account(), 8); + // ... if which only 3 is claimable to make sure the reward account does not die. + let claimable_reward = 8 - ExistentialDeposit::get(); + // NOTE: easier to read of we use 3, so let's use the number instead of variable. + assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3"); + + // given + assert_eq!(PoolMembers::::get(10).unwrap().points, 10); + assert_eq!(PoolMembers::::get(20).unwrap().points, 20); + assert_eq!(BondedPools::::get(1).unwrap().points, 30); + assert_eq!(Balances::free_balance(10), 35); + assert_eq!(Balances::free_balance(20), 20); + + // when + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); + + // then + assert_eq!(Balances::free_balance(10), 35); + // 10's share of the reward is 1/3, since they gave 10/30 of the total shares. + assert_eq!(PoolMembers::::get(10).unwrap().points, 10 + 1); + assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 1); + + // when + assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards)); + + // then + assert_eq!(Balances::free_balance(20), 20); + // 20's share of the rewards is the other 2/3 of the rewards, since they have 20/30 of + // the shares + assert_eq!(PoolMembers::::get(20).unwrap().points, 20 + 2); + assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 3); assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Created { depositor: 11, pool_id: 2 }, - Event::Bonded { member: 11, pool_id: 2, bonded: 10, joined: true } + Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, + Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 0 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 1, joined: false }, + Event::PaidOut { member: 20, pool_id: 1, payout: 2, commission: 0 }, + Event::Bonded { member: 20, pool_id: 1, bonded: 2, joined: false } ] ); - }); + }) } +} + +mod update_roles { + use super::*; #[test] - fn create_errors_correctly() { - ExtBuilder::default().with_check(0).build_and_execute(|| { - assert_noop!( - Pools::create(RuntimeOrigin::signed(10), 420, 123, 456, 789), - Error::::AccountBelongsToOtherPool + fn update_roles_works() { + ExtBuilder::default().build_and_execute(|| { + assert_eq!( + BondedPools::::get(1).unwrap().roles, + PoolRoles { + depositor: 10, + root: Some(900), + nominator: Some(901), + state_toggler: Some(902) + }, ); - // Given - assert_eq!(MinCreateBond::::get(), 2); - assert_eq!(StakingMock::minimum_nominator_bond(), 10); - - // Then + // non-existent pools assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 9, 123, 456, 789), - Error::::MinimumBondNotMet + Pools::update_roles( + RuntimeOrigin::signed(1), + 2, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + Error::::PoolNotFound, ); - // Given - MinCreateBond::::put(20); - - // Then + // depositor cannot change roles. assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 19, 123, 456, 789), - Error::::MinimumBondNotMet + Pools::update_roles( + RuntimeOrigin::signed(1), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + Error::::DoesNotHavePermission, ); - // Given - BondedPool:: { - id: 2, - inner: BondedPoolInner { - commission: Commission::default(), - member_counter: 1, - points: 10, - roles: DEFAULT_ROLES, - state: PoolState::Open, - }, - } - .put(); - assert_eq!(MaxPools::::get(), Some(2)); - assert_eq!(BondedPools::::count(), 2); - - // Then + // nominator cannot change roles. assert_noop!( - Pools::create(RuntimeOrigin::signed(11), 20, 123, 456, 789), - Error::::MaxPools + Pools::update_roles( + RuntimeOrigin::signed(901), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + Error::::DoesNotHavePermission, ); - - // Given - assert_eq!(PoolMembers::::count(), 1); - MaxPools::::put(3); - MaxPoolMembers::::put(1); - Balances::make_free_balance_be(&11, 5 + 20); - - // Then - let create = RuntimeCall::Pools(crate::Call::::create { - amount: 20, - root: 11, - nominator: 11, - state_toggler: 11, - }); + // state-toggler assert_noop!( - create.dispatch(RuntimeOrigin::signed(11)), - Error::::MaxPoolMembers + Pools::update_roles( + RuntimeOrigin::signed(902), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) + ), + Error::::DoesNotHavePermission, ); - }); - } - - #[test] - fn create_with_pool_id_works() { - ExtBuilder::default().build_and_execute(|| { - let ed = Balances::minimum_balance(); - Balances::make_free_balance_be(&11, StakingMock::minimum_nominator_bond() + ed); - assert_ok!(Pools::create( - RuntimeOrigin::signed(11), - StakingMock::minimum_nominator_bond(), - 123, - 456, - 789 + // but root can + assert_ok!(Pools::update_roles( + RuntimeOrigin::signed(900), + 1, + ConfigOp::Set(5), + ConfigOp::Set(6), + ConfigOp::Set(7) )); - assert_eq!(Balances::free_balance(&11), 0); - // delete the initial pool created, then pool_Id `1` will be free - - assert_noop!( - Pools::create_with_pool_id(RuntimeOrigin::signed(12), 20, 234, 654, 783, 1), - Error::::PoolIdInUse + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::RolesUpdated { + root: Some(5), + state_toggler: Some(7), + nominator: Some(6) + } + ] ); - - assert_noop!( - Pools::create_with_pool_id(RuntimeOrigin::signed(12), 20, 234, 654, 783, 3), - Error::::InvalidPoolId + assert_eq!( + BondedPools::::get(1).unwrap().roles, + PoolRoles { + depositor: 10, + root: Some(5), + nominator: Some(6), + state_toggler: Some(7) + }, ); - // start dismantling the pool. - assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); - assert_ok!(fully_unbond_permissioned(10)); - - CurrentEra::set(3); - assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 10)); - - assert_ok!(Pools::create_with_pool_id(RuntimeOrigin::signed(10), 20, 234, 654, 783, 1)); - }); - } -} - -mod nominate { - use super::*; + // also root origin can + assert_ok!(Pools::update_roles( + RuntimeOrigin::root(), + 1, + ConfigOp::Set(1), + ConfigOp::Set(2), + ConfigOp::Set(3) + )); - #[test] - fn nominate_works() { - ExtBuilder::default().build_and_execute(|| { - // Depositor can't nominate - assert_noop!( - Pools::nominate(RuntimeOrigin::signed(10), 1, vec![21]), - Error::::NotNominator + assert_eq!( + pool_events_since_last_call(), + vec![Event::RolesUpdated { + root: Some(1), + state_toggler: Some(3), + nominator: Some(2) + }] ); - - // State toggler can't nominate - assert_noop!( - Pools::nominate(RuntimeOrigin::signed(902), 1, vec![21]), - Error::::NotNominator + assert_eq!( + BondedPools::::get(1).unwrap().roles, + PoolRoles { + depositor: 10, + root: Some(1), + nominator: Some(2), + state_toggler: Some(3) + }, ); - // Root can nominate - assert_ok!(Pools::nominate(RuntimeOrigin::signed(900), 1, vec![21])); - assert_eq!(Nominations::get().unwrap(), vec![21]); + // Noop works + assert_ok!(Pools::update_roles( + RuntimeOrigin::root(), + 1, + ConfigOp::Set(11), + ConfigOp::Noop, + ConfigOp::Noop + )); - // Nominator can nominate - assert_ok!(Pools::nominate(RuntimeOrigin::signed(901), 1, vec![31])); - assert_eq!(Nominations::get().unwrap(), vec![31]); + assert_eq!( + pool_events_since_last_call(), + vec![Event::RolesUpdated { + root: Some(11), + state_toggler: Some(3), + nominator: Some(2) + }] + ); - // Can't nominate for a pool that doesn't exist - assert_noop!( - Pools::nominate(RuntimeOrigin::signed(902), 123, vec![21]), - Error::::PoolNotFound + assert_eq!( + BondedPools::::get(1).unwrap().roles, + PoolRoles { + depositor: 10, + root: Some(11), + nominator: Some(2), + state_toggler: Some(3) + }, ); - }); + + // Remove works + assert_ok!(Pools::update_roles( + RuntimeOrigin::root(), + 1, + ConfigOp::Set(69), + ConfigOp::Remove, + ConfigOp::Remove + )); + + assert_eq!( + pool_events_since_last_call(), + vec![Event::RolesUpdated { root: Some(69), state_toggler: None, nominator: None }] + ); + + assert_eq!( + BondedPools::::get(1).unwrap().roles, + PoolRoles { depositor: 10, root: Some(69), nominator: None, state_toggler: None }, + ); + }) } } -mod set_state { +mod reward_counter_precision { + use sp_runtime::FixedU128; + use super::*; - #[test] - fn set_state_works() { - ExtBuilder::default().build_and_execute(|| { - // Given - assert_ok!(BondedPool::::get(1).unwrap().ok_to_be_open()); + const DOT: Balance = 10u128.pow(10u32); + const POLKADOT_TOTAL_ISSUANCE_GENESIS: Balance = DOT * 10u128.pow(9u32); - // Only the root and state toggler can change the state when the pool is ok to be open. - assert_noop!( - Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Blocked), - Error::::CanNotChangeState - ); - assert_noop!( - Pools::set_state(RuntimeOrigin::signed(901), 1, PoolState::Blocked), - Error::::CanNotChangeState - ); + const fn inflation(years: u128) -> u128 { + let mut i = 0; + let mut start = POLKADOT_TOTAL_ISSUANCE_GENESIS; + while i < years { + start = start + start / 10; + i += 1 + } + start + } - // Root can change state - assert_ok!(Pools::set_state(RuntimeOrigin::signed(900), 1, PoolState::Blocked)); + fn default_pool_reward_counter() -> FixedU128 { + RewardPools::::get(1) + .unwrap() + .current_reward_counter(1, BondedPools::::get(1).unwrap().points) + .unwrap() + } + fn pending_rewards(of: AccountId) -> Option> { + let member = PoolMembers::::get(of).unwrap(); + assert_eq!(member.pool_id, 1); + let rc = default_pool_reward_counter(); + member.pending_rewards(rc).ok() + } + + #[test] + fn smallest_claimable_reward() { + // create a pool that has all of the polkadot issuance in 50 years. + let pool_bond = inflation(50); + ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::StateChanged { pool_id: 1, new_state: PoolState::Blocked } + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 1173908528796953165005, + joined: true, + } ] ); - assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Blocked); - - // State toggler can change state - assert_ok!(Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Destroying)); - assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); - - // If the pool is destroying, then no one can set state - assert_noop!( - Pools::set_state(RuntimeOrigin::signed(900), 1, PoolState::Blocked), - Error::::CanNotChangeState - ); - assert_noop!( - Pools::set_state(RuntimeOrigin::signed(902), 1, PoolState::Blocked), - Error::::CanNotChangeState - ); - - // If the pool is not ok to be open, then anyone can set it to destroying + // the smallest reward that this pool can handle is + let expected_smallest_reward = inflation(50) / 10u128.pow(18); - // Given - unsafe_set_state(1, PoolState::Open); - let mut bonded_pool = BondedPool::::get(1).unwrap(); - bonded_pool.points = 100; - bonded_pool.put(); - // When - assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying)); - // Then - assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); + // tad bit less. cannot be paid out. + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += + expected_smallest_reward - 1)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!(pool_events_since_last_call(), vec![]); + // revert it. - // Given - Balances::make_free_balance_be(&default_bonded_account(), Balance::max_value() / 10); - unsafe_set_state(1, PoolState::Open); - // When - assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying)); - // Then - assert_eq!(BondedPool::::get(1).unwrap().state, PoolState::Destroying); + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free -= + expected_smallest_reward - 1)); - // If the pool is not ok to be open, it cannot be permissionlessly set to a state that - // isn't destroying - unsafe_set_state(1, PoolState::Open); - assert_noop!( - Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Blocked), - Error::::CanNotChangeState + // tad bit more. can be claimed. + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += + expected_smallest_reward + 1)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173, commission: 0 }] ); + }) + } + #[test] + fn massive_reward_in_small_pool() { + let tiny_bond = 1000 * DOT; + ExtBuilder::default().ed(DOT).min_bond(tiny_bond).build_and_execute(|| { assert_eq!( pool_events_since_last_call(), vec![ - Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying }, - Event::StateChanged { pool_id: 1, new_state: PoolState::Destroying } + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10000000000000, joined: true } ] ); - }); - } -} - -mod set_metadata { - use super::*; - - #[test] - fn set_metadata_works() { - ExtBuilder::default().build_and_execute(|| { - // Root can set metadata - assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1])); - assert_eq!(Metadata::::get(1), vec![1, 1]); - // State toggler can set metadata - assert_ok!(Pools::set_metadata(RuntimeOrigin::signed(902), 1, vec![2, 2])); - assert_eq!(Metadata::::get(1), vec![2, 2]); + Balances::make_free_balance_be(&20, tiny_bond); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), tiny_bond / 2, 1)); - // Depositor can't set metadata - assert_noop!( - Pools::set_metadata(RuntimeOrigin::signed(10), 1, vec![3, 3]), - Error::::DoesNotHavePermission + // Suddenly, add a shit ton of rewards. + assert_ok!( + Balances::mutate_account(&default_reward_account(), |a| a.free += inflation(1)) ); - // Nominator can't set metadata - assert_noop!( - Pools::set_metadata(RuntimeOrigin::signed(901), 1, vec![3, 3]), - Error::::DoesNotHavePermission - ); + // now claim. + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - // Metadata cannot be longer than `MaxMetadataLen` - assert_noop!( - Pools::set_metadata(RuntimeOrigin::signed(900), 1, vec![1, 1, 1]), - Error::::MetadataExceedsMaxLen + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Bonded { member: 20, pool_id: 1, bonded: 5000000000000, joined: true }, + Event::PaidOut { + member: 10, + pool_id: 1, + payout: 7333333333333333333, + commission: 0 + }, + Event::PaidOut { + member: 20, + pool_id: 1, + payout: 3666666666666666666, + commission: 0 + } + ] ); - }); + }) } -} - -mod set_configs { - use super::*; #[test] - fn set_configs_works() { - ExtBuilder::default().build_and_execute(|| { - // Setting works - assert_ok!(Pools::set_configs( - RuntimeOrigin::root(), - ConfigOp::Set(1 as Balance), - ConfigOp::Set(2 as Balance), - ConfigOp::Set(3u32), - ConfigOp::Set(4u32), - ConfigOp::Set(5u32), - ConfigOp::Set(Perbill::from_percent(6)) - )); - assert_eq!(MinJoinBond::::get(), 1); - assert_eq!(MinCreateBond::::get(), 2); - assert_eq!(MaxPools::::get(), Some(3)); - assert_eq!(MaxPoolMembers::::get(), Some(4)); - assert_eq!(MaxPoolMembersPerPool::::get(), Some(5)); - assert_eq!(GlobalMaxCommission::::get(), Some(Perbill::from_percent(6))); - - // Noop does nothing - assert_storage_noop!(assert_ok!(Pools::set_configs( - RuntimeOrigin::root(), - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ConfigOp::Noop, - ))); - - // Removing works - assert_ok!(Pools::set_configs( - RuntimeOrigin::root(), - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - ConfigOp::Remove, - )); - assert_eq!(MinJoinBond::::get(), 0); - assert_eq!(MinCreateBond::::get(), 0); - assert_eq!(MaxPools::::get(), None); - assert_eq!(MaxPoolMembers::::get(), None); - assert_eq!(MaxPoolMembersPerPool::::get(), None); - assert_eq!(GlobalMaxCommission::::get(), None); - }); - } -} - -mod bond_extra { - use super::*; - use crate::Event; - - #[test] - fn bond_extra_from_free_balance_creator() { - ExtBuilder::default().build_and_execute(|| { - // 10 is the owner and a member in pool 1, give them some more funds. - Balances::make_free_balance_be(&10, 100); - - // given - assert_eq!(PoolMembers::::get(10).unwrap().points, 10); - assert_eq!(BondedPools::::get(1).unwrap().points, 10); - assert_eq!(Balances::free_balance(10), 100); - - // when - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10))); - - // then - assert_eq!(Balances::free_balance(10), 90); - assert_eq!(PoolMembers::::get(10).unwrap().points, 20); - assert_eq!(BondedPools::::get(1).unwrap().points, 20); - + fn reward_counter_calc_wont_fail_in_normal_polkadot_future() { + // create a pool that has roughly half of the polkadot issuance in 10 years. + let pool_bond = inflation(10) / 2; + ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: false } + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 12_968_712_300_500_000_000, + joined: true, + } ] ); - // when - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(20))); + // in 10 years, the total claimed rewards are large values as well. assuming that a pool + // is earning all of the inflation per year (which is really unrealistic, but worse + // case), that will be: + let pool_total_earnings_10_years = inflation(10) - POLKADOT_TOTAL_ISSUANCE_GENESIS; + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += + pool_total_earnings_10_years)); - // then - assert_eq!(Balances::free_balance(10), 70); - assert_eq!(PoolMembers::::get(10).unwrap().points, 40); - assert_eq!(BondedPools::::get(1).unwrap().points, 40); + // some whale now joins with the other half ot the total issuance. This will bloat all + // the calculation regarding current reward counter. + Balances::make_free_balance_be(&20, pool_bond * 2); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), pool_bond, 1)); assert_eq!( pool_events_since_last_call(), - vec![Event::Bonded { member: 10, pool_id: 1, bonded: 20, joined: false }] + vec![Event::Bonded { + member: 20, + pool_id: 1, + bonded: 12_968_712_300_500_000_000, + joined: true + }] ); - }) - } - #[test] - fn bond_extra_from_rewards_creator() { - ExtBuilder::default().build_and_execute(|| { - // put some money in the reward account, all of which will belong to 10 as the only - // member of the pool. - Balances::make_free_balance_be(&default_reward_account(), 7); - // ... if which only 2 is claimable to make sure the reward account does not die. - let claimable_reward = 7 - ExistentialDeposit::get(); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - // given - assert_eq!(PoolMembers::::get(10).unwrap().points, 10); - assert_eq!(BondedPools::::get(1).unwrap().points, 10); - assert_eq!(Balances::free_balance(10), 35); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 15937424600999999996, + commission: 0 + }] + ); - // when - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); + // now let a small member join with 10 DOTs. + Balances::make_free_balance_be(&30, 20 * DOT); + assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10 * DOT, 1)); - // then - assert_eq!(Balances::free_balance(10), 35); - assert_eq!(PoolMembers::::get(10).unwrap().points, 10 + claimable_reward); - assert_eq!(BondedPools::::get(1).unwrap().points, 10 + claimable_reward); + // and give a reasonably small reward to the pool. + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += DOT)); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); assert_eq!( pool_events_since_last_call(), vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PaidOut { - member: 10, - pool_id: 1, - payout: claimable_reward, - commission: 0 - }, - Event::Bonded { - member: 10, - pool_id: 1, - bonded: claimable_reward, - joined: false - } + Event::Bonded { member: 30, pool_id: 1, bonded: 100000000000, joined: true }, + // quite small, but working fine. + Event::PaidOut { member: 30, pool_id: 1, payout: 38, commission: 0 } ] ); }) } #[test] - fn bond_extra_from_rewards_joiner() { - ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| { - // put some money in the reward account, all of which will belong to 10 as the only - // member of the pool. - Balances::make_free_balance_be(&default_reward_account(), 8); - // ... if which only 3 is claimable to make sure the reward account does not die. - let claimable_reward = 8 - ExistentialDeposit::get(); - // NOTE: easier to read of we use 3, so let's use the number instead of variable. - assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3"); - - // given - assert_eq!(PoolMembers::::get(10).unwrap().points, 10); - assert_eq!(PoolMembers::::get(20).unwrap().points, 20); - assert_eq!(BondedPools::::get(1).unwrap().points, 30); - assert_eq!(Balances::free_balance(10), 35); - assert_eq!(Balances::free_balance(20), 20); - - // when - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards)); - - // then - assert_eq!(Balances::free_balance(10), 35); - // 10's share of the reward is 1/3, since they gave 10/30 of the total shares. - assert_eq!(PoolMembers::::get(10).unwrap().points, 10 + 1); - assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 1); - - // when - assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards)); - - // then - assert_eq!(Balances::free_balance(20), 20); - // 20's share of the rewards is the other 2/3 of the rewards, since they have 20/30 of - // the shares - assert_eq!(PoolMembers::::get(20).unwrap().points, 20 + 2); - assert_eq!(BondedPools::::get(1).unwrap().points, 30 + 3); - + fn reward_counter_update_can_fail_if_pool_is_highly_slashed() { + // create a pool that has roughly half of the polkadot issuance in 10 years. + let pool_bond = inflation(10) / 2; + ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true }, - Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 0 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 1, joined: false }, - Event::PaidOut { member: 20, pool_id: 1, payout: 2, commission: 0 }, - Event::Bonded { member: 20, pool_id: 1, bonded: 2, joined: false } + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 12_968_712_300_500_000_000, + joined: true, + } ] ); + + // slash this pool by 99% of that. + StakingMock::set_bonded_balance(default_bonded_account(), DOT + pool_bond / 100); + + // some whale now joins with the other half ot the total issuance. This will trigger an + // overflow. This test is actually a bit too lenient because all the reward counters are + // set to zero. In other tests that we want to assert a scenario won't fail, we should + // also set the reward counters to some large value. + Balances::make_free_balance_be(&20, pool_bond * 2); + assert_err!( + Pools::join(RuntimeOrigin::signed(20), pool_bond, 1), + Error::::OverflowRisk + ); }) } -} - -mod update_roles { - use super::*; #[test] - fn update_roles_works() { - ExtBuilder::default().build_and_execute(|| { - assert_eq!( - BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(900), - nominator: Some(901), - state_toggler: Some(902) - }, - ); + fn if_small_member_waits_long_enough_they_will_earn_rewards() { + // create a pool that has a quarter of the current polkadot issuance + ExtBuilder::default() + .ed(DOT) + .min_bond(POLKADOT_TOTAL_ISSUANCE_GENESIS / 4) + .build_and_execute(|| { + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 2500000000000000000, + joined: true, + } + ] + ); - // non-existent pools - assert_noop!( - Pools::update_roles( - RuntimeOrigin::signed(1), - 2, - ConfigOp::Set(5), - ConfigOp::Set(6), - ConfigOp::Set(7) - ), - Error::::PoolNotFound, - ); + // and have a tiny fish join the pool as well.. + Balances::make_free_balance_be(&20, 20 * DOT); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); - // depositor cannot change roles. - assert_noop!( - Pools::update_roles( - RuntimeOrigin::signed(1), - 1, - ConfigOp::Set(5), - ConfigOp::Set(6), - ConfigOp::Set(7) - ), - Error::::DoesNotHavePermission, - ); + // earn some small rewards + assert_ok!( + Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) + ); - // nominator cannot change roles. - assert_noop!( - Pools::update_roles( - RuntimeOrigin::signed(901), - 1, - ConfigOp::Set(5), - ConfigOp::Set(6), - ConfigOp::Set(7) - ), - Error::::DoesNotHavePermission, - ); - // state-toggler - assert_noop!( - Pools::update_roles( - RuntimeOrigin::signed(902), - 1, - ConfigOp::Set(5), - ConfigOp::Set(6), - ConfigOp::Set(7) - ), - Error::::DoesNotHavePermission, - ); + // no point in claiming for 20 (nonetheless, it should be harmless) + assert!(pending_rewards(20).unwrap().is_zero()); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Bonded { + member: 20, + pool_id: 1, + bonded: 100000000000, + joined: true + }, + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } + ] + ); - // but root can - assert_ok!(Pools::update_roles( + // earn some small more, still nothing can be claimed for 20, but 10 claims their + // share. + assert_ok!( + Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) + ); + assert!(pending_rewards(20).unwrap().is_zero()); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { + member: 10, + pool_id: 1, + payout: 10000000, + commission: 0 + }] + ); + + // earn some more rewards, this time 20 can also claim. + assert_ok!( + Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) + ); + assert_eq!(pending_rewards(20).unwrap(), 1); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }, + Event::PaidOut { member: 20, pool_id: 1, payout: 1, commission: 0 } + ] + ); + }); + } + + #[test] + fn zero_reward_claim_does_not_update_reward_counter() { + // create a pool that has a quarter of the current polkadot issuance + ExtBuilder::default() + .ed(DOT) + .min_bond(POLKADOT_TOTAL_ISSUANCE_GENESIS / 4) + .build_and_execute(|| { + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { + member: 10, + pool_id: 1, + bonded: 2500000000000000000, + joined: true, + } + ] + ); + + // and have a tiny fish join the pool as well.. + Balances::make_free_balance_be(&20, 20 * DOT); + assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); + + // earn some small rewards + assert_ok!( + Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) + ); + + // if 20 claims now, their reward counter should stay the same, so that they have a + // chance of claiming this if they let it accumulate. Also see + // `if_small_member_waits_long_enough_they_will_earn_rewards` + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Bonded { + member: 20, + pool_id: 1, + bonded: 100000000000, + joined: true + }, + Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } + ] + ); + + let current_reward_counter = default_pool_reward_counter(); + // has been updated, because they actually claimed something. + assert_eq!( + PoolMembers::::get(10).unwrap().last_recorded_reward_counter, + current_reward_counter + ); + // has not be updated, even though the claim transaction went through okay. + assert_eq!( + PoolMembers::::get(20).unwrap().last_recorded_reward_counter, + Default::default() + ); + }); + } +} + +mod commission { + use super::*; + + #[test] + fn set_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Set a commission for pool 1. + assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - ConfigOp::Set(5), - ConfigOp::Set(6), - ConfigOp::Set(7) + Some((Perbill::from_percent(50), 900)) + )); + + let commission = BondedPool::::get(1).unwrap().commission; + assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); + + // update commission only. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(25), 900)) )); + // update payee only. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(25), 901)) + )); + + // remove the commission for pool 1. + assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None)); + + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::RolesUpdated { - root: Some(5), - state_toggler: Some(7), - nominator: Some(6) - } + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(50), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(25), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(25), 901)) + }, + Event::PoolCommissionUpdated { pool_id: 1, current: None } ] ); - assert_eq!( - BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(5), - nominator: Some(6), - state_toggler: Some(7) - }, - ); + }); + } - // also root origin can - assert_ok!(Pools::update_roles( - RuntimeOrigin::root(), + #[test] + fn global_max_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // Remove 90% global maximum commission + GlobalMaxCommission::::set(None); + + // With global maximum commission removed, we can now set a 100% + // commission. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), 1, - ConfigOp::Set(1), - ConfigOp::Set(2), - ConfigOp::Set(3) + Some((Perbill::from_percent(100), 900)) )); - assert_eq!( - pool_events_since_last_call(), - vec![Event::RolesUpdated { - root: Some(1), - state_toggler: Some(3), - nominator: Some(2) - }] - ); - assert_eq!( - BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(1), - nominator: Some(2), - state_toggler: Some(3) - }, - ); - - // Noop works - assert_ok!(Pools::update_roles( - RuntimeOrigin::root(), + // Set a more restrictive commission of 95% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), 1, - ConfigOp::Set(11), - ConfigOp::Noop, - ConfigOp::Noop + Some((Perbill::from_percent(95), 900)) )); - assert_eq!( - pool_events_since_last_call(), - vec![Event::RolesUpdated { - root: Some(11), - state_toggler: Some(3), - nominator: Some(2) - }] - ); + // Bring back global max commission, this time of 80% + GlobalMaxCommission::::set(Some(Perbill::from_percent(80))); - assert_eq!( - BondedPools::::get(1).unwrap().roles, - PoolRoles { - depositor: 10, - root: Some(11), - nominator: Some(2), - state_toggler: Some(3) - }, + // Attempting to set a commission of 85% will now fail, even though it is + // more restrictive than the current commission of 95% + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(85), 900)) + ), + Error::::GlobalMaxCommissionExceeded ); - // Remove works - assert_ok!(Pools::update_roles( - RuntimeOrigin::root(), + // Succesfully set max commission of 75% + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), 1, - ConfigOp::Set(69), - ConfigOp::Remove, - ConfigOp::Remove + Perbill::from_percent(75) )); - assert_eq!( - pool_events_since_last_call(), - vec![Event::RolesUpdated { root: Some(69), state_toggler: None, nominator: None }] - ); - - assert_eq!( - BondedPools::::get(1).unwrap().roles, - PoolRoles { depositor: 10, root: Some(69), nominator: None, state_toggler: None }, - ); - }) - } -} - -mod reward_counter_precision { - use sp_runtime::FixedU128; - - use super::*; - - const DOT: Balance = 10u128.pow(10u32); - const POLKADOT_TOTAL_ISSUANCE_GENESIS: Balance = DOT * 10u128.pow(9u32); - - const fn inflation(years: u128) -> u128 { - let mut i = 0; - let mut start = POLKADOT_TOTAL_ISSUANCE_GENESIS; - while i < years { - start = start + start / 10; - i += 1 - } - start - } - - fn default_pool_reward_counter() -> FixedU128 { - RewardPools::::get(1) - .unwrap() - .current_reward_counter(1, BondedPools::::get(1).unwrap().points) - .unwrap() - } - - fn pending_rewards(of: AccountId) -> Option> { - let member = PoolMembers::::get(of).unwrap(); - assert_eq!(member.pool_id, 1); - let rc = default_pool_reward_counter(); - member.pending_rewards(rc).ok() - } + // Succesfully update commission to 75% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(75), 900)) + )); - #[test] - fn smallest_claimable_reward() { - // create a pool that has all of the polkadot issuance in 50 years. - let pool_bond = inflation(50); - ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { pool_id: 1, - bonded: 1173908528796953165005, - joined: true, + current: Some((Perbill::from_percent(100), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(95), 900)) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(75) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 900)) } ] ); + }); + } - // the smallest reward that this pool can handle is - let expected_smallest_reward = inflation(50) / 10u128.pow(18); + #[test] + fn set_commission_handles_errors() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 9999, + Some((Perbill::from_percent(1), 900)), + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(1), + 1, + Some((Perbill::from_percent(5), 900)), + ), + Error::::DoesNotHavePermission + ); - // tad bit less. cannot be paid out. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - expected_smallest_reward - 1)); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_eq!(pool_events_since_last_call(), vec![]); - // revert it. + // We attempt to increase the commission to 100%, which is disallowed due + // to global max commission of 90%. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(100), 900)) + ), + Error::::GlobalMaxCommissionExceeded + ); - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free -= - expected_smallest_reward - 1)); + // Set the initial commission to 5%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)), + )); - // tad bit more. can be claimed. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - expected_smallest_reward + 1)); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); + // Throttle test. We will throttle commission to be a +1% commission increase every 2 + // blocks. + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } + )); assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1173, commission: 0 }] + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: None, + throttle: Some(CommissionThrottle { + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }, + previous_set_at: None, + }) + } ); - }) - } - #[test] - fn massive_reward_in_small_pool() { - let tiny_bond = 1000 * DOT; - ExtBuilder::default().ed(DOT).min_bond(tiny_bond).build_and_execute(|| { - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10000000000000, joined: true } - ] + // We now try to increase commission to 10% (5% increase): this should be throttled. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + ), + Error::::CommissionChangeThrottled ); - Balances::make_free_balance_be(&20, tiny_bond); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), tiny_bond / 2, 1)); + // Run to block 3 + run_blocks(2); - // Suddenly, add a shit ton of rewards. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += inflation(1)) + // We now try to increase commission by 1%, and provide an initial payee. + // This should work, and set the `previous_set_at` field. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(6), 900)) + )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(6), 900)), + max: None, + throttle: Some(CommissionThrottle { + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 + }, + previous_set_at: Some(3_u64), + }) + } ); - // now claim. - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Bonded { member: 20, pool_id: 1, bonded: 5000000000000, joined: true }, - Event::PaidOut { - member: 10, - pool_id: 1, - payout: 7333333333333333333, - commission: 0 - }, - Event::PaidOut { - member: 20, - pool_id: 1, - payout: 3666666666666666666, - commission: 0 - } - ] + // Attempt to increase the commission an additional 1% (now 2%) again immediately. + // this will fail as `previous_set_at` is now the current block, and at least 2 + // blocks need to pass before we can set commission again. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(2), 900)) + ), + Error::::CommissionChangeThrottled ); - }) - } - #[test] - fn reward_counter_calc_wont_fail_in_normal_polkadot_future() { - // create a pool that has roughly half of the polkadot issuance in 10 years. - let pool_bond = inflation(10) / 2; - ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { + // Run 2 blocks into the future, to block 3. + run_blocks(2); + + // We can now successfully increase the commission again, to 2%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(2), 900)), + )); + + // Run 2 blocks into the future, to block 5. + run_blocks(2); + + // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is + // still at play. An attempted commission change now to 4% (+2% increase) should fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(4), 900)), + ), + Error::::CommissionChangeThrottled + ); + + // We will now set a max commission to the current 2%. This will also + // update commission.current to 2%. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(2) + )); + + // Run 2 blocks into the future so we are eligible to update commission again. + run_blocks(2); + + // We will now attempt again to increase the commission by 1%, to 3%. This is within the + // change rate allowance, but the max_commission will now prevent us from going any + // higher. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)), + ), + Error::::CommissionExceedsMaximum + ); + + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { pool_id: 1, - bonded: 12_968_712_300_500_000_000, - joined: true, + current: Some((Perbill::from_percent(5), 900)), + }, + Event::PoolCommissionThrottleUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(6), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(2), 900)) + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(2) } ] ); + }); + } - // in 10 years, the total claimed rewards are large values as well. assuming that a pool - // is earning all of the inflation per year (which is really unrealistic, but worse - // case), that will be: - let pool_total_earnings_10_years = inflation(10) - POLKADOT_TOTAL_ISSUANCE_GENESIS; - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += - pool_total_earnings_10_years)); - - // some whale now joins with the other half ot the total issuance. This will bloat all - // the calculation regarding current reward counter. - Balances::make_free_balance_be(&20, pool_bond * 2); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), pool_bond, 1)); - - assert_eq!( - pool_events_since_last_call(), - vec![Event::Bonded { - member: 20, - pool_id: 1, - bonded: 12_968_712_300_500_000_000, - joined: true - }] + #[test] + fn set_commission_max_works_with_error_tests() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_max( + RuntimeOrigin::signed(900), + 9999, + Perbill::from_percent(1) + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(1), 1, Perbill::from_percent(5)), + Error::::DoesNotHavePermission ); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); + // Remove global maximum commission + GlobalMaxCommission::::set(None); + // Set a max commission commission pool 1 to 80% + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(80) + )); assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { - member: 10, - pool_id: 1, - payout: 15937424600999999996, - commission: 0 - }] + BondedPools::::get(1).unwrap().commission.max, + Some(Perbill::from_percent(80)) ); - // now let a small member join with 10 DOTs. - Balances::make_free_balance_be(&30, 20 * DOT); - assert_ok!(Pools::join(RuntimeOrigin::signed(30), 10 * DOT, 1)); - - // and give a reasonably small reward to the pool. - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += DOT)); + // We attempt to increase the max commission to 90%, but increasing is + // disallowed due to pool's max commission. + assert_noop!( + Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Perbill::from_percent(90)), + Error::::MaxCommissionRestricted + ); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(30))); + // We will now set a commission to 75% and then amend the max commission + // to 50%. The max commission change should decrease the current + // commission to 50%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(75), 900)) + )); + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(50) + )); assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Bonded { member: 30, pool_id: 1, bonded: 100000000000, joined: true }, - // quite small, but working fine. - Event::PaidOut { member: 30, pool_id: 1, payout: 38, commission: 0 } - ] + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(50), 900)), + max: Some(Perbill::from_percent(50)), + throttle: None + } ); - }) - } - #[test] - fn reward_counter_update_can_fail_if_pool_is_highly_slashed() { - // create a pool that has roughly half of the polkadot issuance in 10 years. - let pool_bond = inflation(10) / 2; - ExtBuilder::default().ed(DOT).min_bond(pool_bond).build_and_execute(|| { + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolMaxCommissionUpdated { pool_id: 1, - bonded: 12_968_712_300_500_000_000, - joined: true, + max_commission: Perbill::from_percent(80) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 900)), + }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(50) } ] ); + }); + } - // slash this pool by 99% of that. - StakingMock::set_bonded_balance(default_bonded_account(), DOT + pool_bond / 100); + #[test] + fn max_commission_after_current_commission_works() { + ExtBuilder::default().build_and_execute(|| { + // set pool commission to 50% first. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(50), 900)), + )); - // some whale now joins with the other half ot the total issuance. This will trigger an - // overflow. This test is actually a bit too lenient because all the reward counters are - // set to zero. In other tests that we want to assert a scenario won't fail, we should - // also set the reward counters to some large value. - Balances::make_free_balance_be(&20, pool_bond * 2); - assert_err!( - Pools::join(RuntimeOrigin::signed(20), pool_bond, 1), - Error::::OverflowRisk + // now set the max commission to something less than the current + // commission. + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(25) + )); + + // the current commission should now be 25%. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(25), 900)), + max: Some(Perbill::from_percent(25)), + throttle: None + } ); }) } #[test] - fn if_small_member_waits_long_enough_they_will_earn_rewards() { - // create a pool that has a quarter of the current polkadot issuance - ExtBuilder::default() - .ed(DOT) - .min_bond(POLKADOT_TOTAL_ISSUANCE_GENESIS / 4) - .build_and_execute(|| { - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, - pool_id: 1, - bonded: 2500000000000000000, - joined: true, - } - ] - ); - - // and have a tiny fish join the pool as well.. - Balances::make_free_balance_be(&20, 20 * DOT); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); + fn set_commission_throttle_works_with_error_tests() { + ExtBuilder::default().build_and_execute(|| { + // Provided pool does not exist + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 9999, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::PoolNotFound + ); + // Sender does not have permission to set commission + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(1), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 1000_u64 + } + ), + Error::::DoesNotHavePermission + ); - // earn some small rewards - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + // Set a commission throttle for pool 1 + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.throttle, + Some(CommissionThrottle { + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10_u64 + }, + previous_set_at: None + }) + ); - // no point in claiming for 20 (nonetheless, it should be harmless) - assert!(pending_rewards(20).unwrap().is_zero()); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Bonded { - member: 20, - pool_id: 1, - bonded: 100000000000, - joined: true - }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } - ] - ); + // We now try to half the min_delay - this will be disallowed. + // A greater delay between commission changes is seen as more restrictive. + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 5_u64 + } + ), + Error::::CommissionThrottleNotAllowed + ); - // earn some small more, still nothing can be claimed for 20, but 10 claims their - // share. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); - assert!(pending_rewards(20).unwrap().is_zero()); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { - member: 10, - pool_id: 1, - payout: 10000000, - commission: 0 - }] - ); + // We now try to increase the allowed max_increase - this will fail. + // A smaller allowed commission change is seen as more restrictive. + assert_noop!( + Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { + max_increase: Perbill::from_percent(10), + min_delay: 10_u64 + } + ), + Error::::CommissionThrottleNotAllowed + ); - // earn some more rewards, this time 20 can also claim. - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); - assert_eq!(pending_rewards(20).unwrap(), 1); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::PaidOut { member: 10, pool_id: 1, payout: 10000000, commission: 0 }, - Event::PaidOut { member: 20, pool_id: 1, payout: 1, commission: 0 } - ] - ); - }); - } + // Successful more restrictive change of min_delay with the current max_increase + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } + )); - #[test] - fn zero_reward_claim_does_not_update_reward_counter() { - // create a pool that has a quarter of the current polkadot issuance - ExtBuilder::default() - .ed(DOT) - .min_bond(POLKADOT_TOTAL_ISSUANCE_GENESIS / 4) - .build_and_execute(|| { - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { - member: 10, - pool_id: 1, - bonded: 2500000000000000000, - joined: true, - } - ] - ); + // Successful more restrictive change of max_increase with the current min_delay + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } + )); - // and have a tiny fish join the pool as well.. - Balances::make_free_balance_be(&20, 20 * DOT); - assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10 * DOT, 1)); + // Successful more restrictive change of both max_increase and min_delay + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } + )); - // earn some small rewards - assert_ok!( - Balances::mutate_account(&default_reward_account(), |a| a.free += DOT / 1000) - ); + // multi duration `min_delay` test. + // + // set the commission throttle change_rate of 1% per 3000 blocks. + assert_ok!(Pools::set_commission_throttle( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } + )); + // pre-requisite: set the commission to 1%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)), + )); + // Run 90 blocks into the future so we are eligible to update commission + // with 3 `min_delay` durations passed. + run_blocks(91); - // if 20 claims now, their reward counter should stay the same, so that they have a - // chance of claiming this if they let it accumulate. Also see - // `if_small_member_waits_long_enough_they_will_earn_rewards` - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(10))); - assert_ok!(Pools::claim_payout(RuntimeOrigin::signed(20))); - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Bonded { - member: 20, - pool_id: 1, - bonded: 100000000000, - joined: true - }, - Event::PaidOut { member: 10, pool_id: 1, payout: 9999997, commission: 0 } - ] - ); + // we should not be able to increase the commission to 5%: 1% beyond the throttle limit. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)) + ), + Error::::CommissionChangeThrottled + ); - let current_reward_counter = default_pool_reward_counter(); - // has been updated, because they actually claimed something. - assert_eq!( - PoolMembers::::get(10).unwrap().last_recorded_reward_counter, - current_reward_counter - ); - // has not be updated, even though the claim transaction went through okay. - assert_eq!( - PoolMembers::::get(20).unwrap().last_recorded_reward_counter, - Default::default() - ); - }); + // we should however be able to increase the commission to 4%: 1% + (3*1%). + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(4), 900)) + )); + }); } } From 61e0b1261c88ebd09370f78f8695fa2c4269b928 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 18:27:54 +0700 Subject: [PATCH 135/221] fix benchmarks --- .../nomination-pools/benchmarking/src/lib.rs | 25 ++- frame/nomination-pools/src/weights.rs | 162 +++++++++--------- 2 files changed, 93 insertions(+), 94 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 1e3a02a462b6e..eec4da91bdac7 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -29,9 +29,9 @@ use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, - CommissionThrottle, ConfigOp, GlobalMaxCommission, MaxPoolMembers, MaxPoolMembersPerPool, - MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, - PoolState, RewardPools, SubPoolsStorage, + CommissionThrottle, ConfigOp, GlobalMaxCommission, LastPoolId, MaxPoolMembers, + MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, + PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, }; use sp_runtime::{ traits::{Bounded, StaticLookup, Zero}, @@ -88,16 +88,13 @@ fn create_pool_account( .unwrap(); if let Some(c) = commission { - let _ = pallet_nomination_pools::BondedPools::::iter() - .find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator) - .map(|(pool_id, _)| { - Pools::::set_commission( - RuntimeOrigin::Signed(pool_creator.clone()).into(), - pool_id, - Some((c, pool_creator.clone())), - ) - .expect("pool commission has been set"); - }); + let pool_id = LastPoolId::::get(); + Pools::::set_commission( + RuntimeOrigin::Signed(pool_creator.clone()).into(), + pool_id, + Some((c, pool_creator.clone())), + ) + .expect("pool just created, commission can be set by root; qed"); } let pool_account = pallet_nomination_pools::BondedPools::::iter() @@ -692,7 +689,7 @@ frame_benchmarking::benchmarks! { min_delay: 0u32.into(), }).unwrap(); - }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(Perbill::from_percent(20)), Some(depositor.clone())) + }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone()))) verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(20), depositor)), diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 9a5b028121ebb..a151140700025 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -2,8 +2,8 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-24, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `192.168.1.33`, CPU: `` +//! DATE: 2022-12-26, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Rosss-MacBook-Pro-2.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 146_000 nanoseconds. - Weight::from_ref_time(202_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(149_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 138_000 nanoseconds. - Weight::from_ref_time(192_000_000) + // Minimum execution time: 140_000 nanoseconds. + Weight::from_ref_time(141_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 165_000 nanoseconds. - Weight::from_ref_time(166_000_000) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(174_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(52_000_000) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(55_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(145_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -146,10 +146,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(53_610_228) - // Standard Error: 1_132 - .saturating_add(Weight::from_ref_time(14_777).saturating_mul(s.into())) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_527_998) + // Standard Error: 1_975 + .saturating_add(Weight::from_ref_time(33_319).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -164,10 +164,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 94_000 nanoseconds. - Weight::from_ref_time(96_737_116) - // Standard Error: 1_756 - .saturating_add(Weight::from_ref_time(29_702).saturating_mul(s.into())) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(98_753_346) + // Standard Error: 2_435 + .saturating_add(Weight::from_ref_time(21_176).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -193,10 +193,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(150_336_111) - // Standard Error: 5_051 - .saturating_add(Weight::from_ref_time(69_545).saturating_mul(s.into())) + // Minimum execution time: 149_000 nanoseconds. + Weight::from_ref_time(153_962_225) + // Standard Error: 3_236 + .saturating_add(Weight::from_ref_time(7_144).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -223,7 +223,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { // Minimum execution time: 132_000 nanoseconds. - Weight::from_ref_time(138_000_000) + Weight::from_ref_time(133_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -241,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(59_991_725) - // Standard Error: 19_231 - .saturating_add(Weight::from_ref_time(1_360_632).saturating_mul(n.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(58_320_810) + // Standard Error: 7_479 + .saturating_add(Weight::from_ref_time(1_470_472).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -253,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(36_000_000) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -262,9 +262,11 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. - fn set_metadata(_n: u32, ) -> Weight { + fn set_metadata(n: u32, ) -> Weight { // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_880_959) + Weight::from_ref_time(13_295_489) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(925).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -281,8 +283,8 @@ impl WeightInfo for SubstrateWeight { } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -296,8 +298,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(59_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(61_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -305,16 +307,15 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(23_000_000) + Weight::from_ref_time(24_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. + // Minimum execution time: 23_000 nanoseconds. Weight::from_ref_time(23_000_000) - .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) @@ -342,8 +343,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 146_000 nanoseconds. - Weight::from_ref_time(202_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(149_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -357,8 +358,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 138_000 nanoseconds. - Weight::from_ref_time(192_000_000) + // Minimum execution time: 140_000 nanoseconds. + Weight::from_ref_time(141_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -372,8 +373,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 165_000 nanoseconds. - Weight::from_ref_time(166_000_000) + // Minimum execution time: 168_000 nanoseconds. + Weight::from_ref_time(174_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -382,8 +383,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(52_000_000) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(55_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -402,8 +403,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(145_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -414,10 +415,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(53_610_228) - // Standard Error: 1_132 - .saturating_add(Weight::from_ref_time(14_777).saturating_mul(s.into())) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_527_998) + // Standard Error: 1_975 + .saturating_add(Weight::from_ref_time(33_319).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -432,10 +433,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForPoolMembers (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 94_000 nanoseconds. - Weight::from_ref_time(96_737_116) - // Standard Error: 1_756 - .saturating_add(Weight::from_ref_time(29_702).saturating_mul(s.into())) + // Minimum execution time: 95_000 nanoseconds. + Weight::from_ref_time(98_753_346) + // Standard Error: 2_435 + .saturating_add(Weight::from_ref_time(21_176).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -461,10 +462,10 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(150_336_111) - // Standard Error: 5_051 - .saturating_add(Weight::from_ref_time(69_545).saturating_mul(s.into())) + // Minimum execution time: 149_000 nanoseconds. + Weight::from_ref_time(153_962_225) + // Standard Error: 3_236 + .saturating_add(Weight::from_ref_time(7_144).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -491,7 +492,7 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { // Minimum execution time: 132_000 nanoseconds. - Weight::from_ref_time(138_000_000) + Weight::from_ref_time(133_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -509,10 +510,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(59_991_725) - // Standard Error: 19_231 - .saturating_add(Weight::from_ref_time(1_360_632).saturating_mul(n.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(58_320_810) + // Standard Error: 7_479 + .saturating_add(Weight::from_ref_time(1_470_472).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -521,8 +522,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(36_000_000) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -530,9 +531,11 @@ impl WeightInfo for () { // Storage: NominationPools Metadata (r:1 w:1) // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. - fn set_metadata(_n: u32, ) -> Weight { + fn set_metadata(n: u32, ) -> Weight { // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_880_959) + Weight::from_ref_time(13_295_489) + // Standard Error: 296 + .saturating_add(Weight::from_ref_time(925).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -549,8 +552,8 @@ impl WeightInfo for () { } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(23_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -564,8 +567,8 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(59_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(61_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } @@ -573,16 +576,15 @@ impl WeightInfo for () { // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(23_000_000) + Weight::from_ref_time(24_000_000) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. + // Minimum execution time: 23_000 nanoseconds. Weight::from_ref_time(23_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) From 474265eaed173711f2cf3f15946ac98b159fa6f5 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 20:00:45 +0700 Subject: [PATCH 136/221] simplify last_updated & change_rate --- .../nomination-pools/benchmarking/src/lib.rs | 33 ++-- frame/nomination-pools/src/lib.rs | 157 +++++++++-------- frame/nomination-pools/src/migration.rs | 3 +- frame/nomination-pools/src/tests.rs | 84 +++++----- frame/nomination-pools/src/weights.rs | 158 +++++++++--------- 5 files changed, 214 insertions(+), 221 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index eec4da91bdac7..528c8e755483d 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -28,10 +28,10 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{assert_ok, ensure, traits::Get}; use frame_system::RawOrigin as RuntimeOrigin; use pallet_nomination_pools::{ - BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, - CommissionThrottle, ConfigOp, GlobalMaxCommission, LastPoolId, MaxPoolMembers, - MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond, Pallet as Pools, - PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage, + BalanceOf, BondExtra, BondedPoolInner, BondedPools, Commission, CommissionChangeRate, ConfigOp, + GlobalMaxCommission, LastPoolId, MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, + MinCreateBond, MinJoinBond, Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, + SubPoolsStorage, }; use sp_runtime::{ traits::{Bounded, StaticLookup, Zero}, @@ -683,8 +683,8 @@ frame_benchmarking::benchmarks! { let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); // set a max commission Pools::::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap(); - // set a commission throttle - Pools::::set_commission_throttle(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate { + // set a change rate + Pools::::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate { max_increase: Perbill::from_percent(20), min_delay: 0u32.into(), }).unwrap(); @@ -693,13 +693,11 @@ frame_benchmarking::benchmarks! { verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(20), depositor)), + last_updated: Some(1u32.into()), max: Some(Perbill::from_percent(50)), - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { + change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(20), min_delay: 0u32.into() - }, - previous_set_at: Some(1u32.into()) }), }); } @@ -712,12 +710,13 @@ frame_benchmarking::benchmarks! { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(50), depositor)), + last_updated: Some(0u32.into()), max: Some(Perbill::from_percent(50)), - throttle: None, + change_rate: None, }); } - set_commission_throttle { + set_commission_change_rate { // Create a pool let (depositor, pool_account) = create_pool_account::(0, Pools::::depositor_min_bond() * 2u32.into(), None); }:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate { @@ -728,13 +727,11 @@ frame_benchmarking::benchmarks! { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { current: None, + last_updated: None, max: None, - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(50), - min_delay: 1000u32.into(), - }, - previous_set_at: None + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(50), + min_delay: 1000u32.into(), }), }); } diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 3974cc46f5d6d..ff03d9e68f883 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -586,16 +586,16 @@ pub struct PoolRoles { /// Pool commission. /// /// The pool `root` can set commission configuration after pool creation. By default, all commission -/// values are `None`. Pool `root`s can set `max` and `throttle` configurations before setting an +/// values are `None`. Pool `root`s can set `max` and `change_rate` configurations before setting an /// initial commission `current` value - the commission percentage and payee of commission. /// /// The `max` commission value can only be decreased after the initial value is set, to prevent /// commission from repeatedly increasing. /// -/// An optional commission throttle allows the pool to set strict limits to how much commission can -/// change in each update, and how often updates can take place. If a `throttle` is set before a -/// commission `current` is set, the initial commission `current` value will not be subject to -/// throttling. Subsequent commission updates will be. +/// An optional commission change_rate allows the pool to set strict limits to how much commission +/// can change in each update, and how often updates can take place. If a `change_rate` is set +/// before a commission `current` is set, the initial commission `current` value will not be subject +/// to throttling. Subsequent commission updates will be. #[derive( Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, )] @@ -604,12 +604,14 @@ pub struct PoolRoles { pub struct Commission { /// Optional commission rate of the pool along with the account commission is paid to. pub current: Option<(Perbill, T::AccountId)>, + /// The block the previous commission update took place. + pub last_updated: Option, /// Optional maximum commission that can be set by the pool `root`. Once set, this value can /// only be updated to a decreased value. pub max: Option, /// Optional configuration around how often commission can be updated, and when the last /// commission update took place. - pub throttle: Option>, + pub change_rate: Option>, } impl Commission { @@ -620,7 +622,8 @@ impl Commission { self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) } - /// Returns true if a commission percentage updating to `to` would exhaust the throttle limit. + /// Returns true if a commission percentage updating to `to` would exhaust the change_rate + /// limit. /// /// A commission update will be throttled (disallowed) if: /// 1. not enough blocks have passed since the previous commission update took place, and @@ -628,17 +631,15 @@ impl Commission { /// /// Throttling is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { - if let Some(t) = self.throttle.as_ref() { + if let Some(t) = self.change_rate.as_ref() { // factor previously updated block into whether user is throttled. - if t.previous_set_at.map_or( - // if no `previous_set_at` is set, throttled if the attempted increase in + if self.last_updated.map_or( + // if no `last_updated` is set, throttled if the attempted increase in // commission is greater than `max_increase`. - (*to).saturating_sub(self.commission_or_zero()) > t.change_rate.max_increase, + (*to).saturating_sub(self.commission_or_zero()) > t.max_increase, |p| { // `min_delay` blocks must have been surpassed since last update. - if >::block_number().saturating_sub(p) < - t.change_rate.min_delay - { + if >::block_number().saturating_sub(p) < t.min_delay { return true } // ensure the `max_increase` durations surpassed since the previous commission @@ -650,19 +651,18 @@ impl Commission { // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(p); - // calculate intervals passed since last `previous_set_at` + // calculate intervals passed since last `last_updated` let intervals_passed = if blocks_passed == Zero::zero() { Zero::zero() } else { - blocks_passed.div(t.change_rate.min_delay).saturated_into::() + blocks_passed.div(t.min_delay).saturated_into::() }; // the maximum allowed increase, where the current `max_increase` Perbill is // converted into a u32 by multiplying itself with 100_u32 and multiplied by // durations passed, before being converted back into a Perbill. - let max_allowed_increase = Perbill::from_percent( - (t.change_rate.max_increase * 100_u32) * intervals_passed, - ); + let max_allowed_increase = + Perbill::from_percent((t.max_increase * 100_u32) * intervals_passed); // throttled (true) if attempted increase is greater than the maximum allowed // increase, as a percentage. @@ -679,10 +679,13 @@ impl Commission { /// /// Update commission based on `current`. If a `None` current value is supplied, allow the /// commission to be removed in all cases, without any throttling restrictions. Do not allow a - /// commission above global maximum if set. If throttle is present, record the current block as - /// the previously updated commission. If the supplied commission is zero, `None` will be - /// inserted and `payee` will be ignored. - fn update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { + /// commission above global maximum if set. If `change_rate` is present, record the current + /// block as the previously updated commission. If the supplied commission is zero, `None` will + /// be inserted and `payee` will be ignored. + fn maybe_update_current( + &mut self, + current: &Option<(Perbill, T::AccountId)>, + ) -> DispatchResult { if current.is_none() { self.current = None; } else { @@ -700,12 +703,7 @@ impl Commission { ); self.current = Some((*commission, payee.clone())); } - - // update `throttle.previous_set_at` if a throttle has been configured. - let _ = self - .throttle - .as_mut() - .map(|t| t.register_change(>::block_number())); + let _ = self.register_update(>::block_number()); Ok(()) } @@ -716,7 +714,8 @@ impl Commission { /// return a dispatch error. /// /// If `current.0` is larger than an updated max commission value, then `current.0` will also be - /// updated to the new maximum. + /// updated to the new maximum. This will also register a `last_updated` update to the + /// commission. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { @@ -726,28 +725,33 @@ impl Commission { } else { self.max = Some(new_max) }; - let _ = self.current.as_mut().map(|(x, _)| *x = (*x).min(new_max)); + let updated_current = self + .current + .as_mut() + .map(|(x, _)| { + let u = *x > new_max; + *x = (*x).min(new_max); + u + }) + .unwrap_or(false); + + if updated_current { + self.register_update(>::block_number()); + } Ok(()) } - /// Set the pool's commission throttle settings. + /// Set the pool's commission `change_rate` settings. /// - /// Once a throttle configuration has been set, only more restrictive values can be set + /// Once a change rate configuration has been set, only more restrictive values can be set /// thereafter. These restrictions translate to increased `min_delay` values and decreased /// `max_increase` values. - fn maybe_update_throttle( + fn maybe_update_change_rate( &mut self, change_rate: CommissionChangeRate, ) -> DispatchResult { - ensure!( - &self.throttle.as_ref().map_or(true, |t| t.less_restrictive(&change_rate)), - Error::::CommissionThrottleNotAllowed - ); - - self.throttle = Some(CommissionThrottle { - change_rate, - previous_set_at: self.throttle.as_ref().map_or(None, |t| t.previous_set_at), - }); + ensure!(&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); + self.change_rate = Some(change_rate); Ok(()) } @@ -765,37 +769,29 @@ impl Commission { .as_ref() .map(|(commission, payee)| (*commission * *pending_rewards, payee.clone())) } -} -/// The pool root is able to set a commission throttle for their pool. -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone)] -#[codec(mel_bound(T: Config))] -#[scale_info(skip_type_params(T))] -pub struct CommissionThrottle { - /// The change rate dictates how often and by how much commission can be updated. - pub change_rate: CommissionChangeRate, - /// The block the previous commission update took place. - pub previous_set_at: Option, -} - -impl CommissionThrottle { - /// Returns `true` if `change_rate` is less restrictive than `self`. - fn less_restrictive(&self, change_rate: &CommissionChangeRate) -> bool { - change_rate.max_increase <= self.change_rate.max_increase && - change_rate.min_delay >= self.change_rate.min_delay + /// Updates a commission's `last_updated` field. + fn register_update(&mut self, now: T::BlockNumber) { + self.last_updated = Some(now); } - fn register_change(&mut self, now: T::BlockNumber) { - self.previous_set_at = Some(now); + /// Returns `true` if `change_rate` is less restrictive than the currently set change rate, if + /// present, or true otherwise. + fn less_restrictive(&self, change_rate: &CommissionChangeRate) -> bool { + self.change_rate + .as_ref() + .map(|t| { + change_rate.max_increase <= t.max_increase && change_rate.min_delay >= t.min_delay + }) + .unwrap_or(true) } } /// Pool commission change rate preferences. /// -/// A commission change rate consists of 2 values; (1) the maximum allowed commission change, and -/// (2) the minimum amount of blocks that must elapse before commission updates are allowed again. -/// -/// This struct is used in the `set_commission_throttle` call as well as in CommissionThrottle. +/// The pool root is able to set a commission change rate for their pool. A commission change rate +/// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of +/// blocks that must elapse before commission updates are allowed again. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionChangeRate { /// The maximum amount the commission can be updated by per `min_delay` period. @@ -1660,8 +1656,8 @@ pub mod pallet { PoolCommissionUpdated { pool_id: PoolId, current: Option<(Perbill, T::AccountId)> }, /// A pool's maximum commission setting has been changed. PoolMaxCommissionUpdated { pool_id: PoolId, max_commission: Perbill }, - /// A pool's commission throttle has been changed. - PoolCommissionThrottleUpdated { + /// A pool's commission `change_rate` has been changed. + PoolCommissionChangeRateUpdated { pool_id: PoolId, change_rate: CommissionChangeRate, }, @@ -1732,8 +1728,8 @@ pub mod pallet { CommissionExceedsMaximum, /// Not enough blocks have surpassed since the last commission update. CommissionChangeThrottled, - /// The submitted changes to commission throttle are not allowed. - CommissionThrottleNotAllowed, + /// The submitted changes to commission change rate are not allowed. + CommissionChangeRateNotAllowed, /// Pool id currently in use. PoolIdInUse, /// Pool id provided is not correct/usable. @@ -2407,7 +2403,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.update_current(¤t)?; + bonded_pool.commission.maybe_update_current(¤t)?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, current }); Ok(()) @@ -2439,16 +2435,16 @@ pub mod pallet { Ok(()) } - /// Set the commission throttle for a pool. + /// Set the commission change rate for a pool. /// /// The dispatch origin of this call must be signed by the `root` role of the pool. If a - /// throttle is already present, this call will only succeed if a more restrictive throttle - /// configuration is given. + /// change rate is already present, this call will only succeed if a more restrictive change + /// rate configuration is given. /// - /// If a throttle configuration does not yet exist, the provided values are set. + /// If a change rate configuration does not yet exist, the provided values are set. #[pallet::call_index(16)] - #[pallet::weight(T::WeightInfo::set_commission_throttle())] - pub fn set_commission_throttle( + #[pallet::weight(T::WeightInfo::set_commission_change_rate())] + pub fn set_commission_change_rate( origin: OriginFor, pool_id: PoolId, change_rate: CommissionChangeRate, @@ -2457,10 +2453,13 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_throttle(change_rate)?; + bonded_pool.commission.maybe_update_change_rate(change_rate)?; bonded_pool.put(); - Self::deposit_event(Event::::PoolCommissionThrottleUpdated { pool_id, change_rate }); + Self::deposit_event(Event::::PoolCommissionChangeRateUpdated { + pool_id, + change_rate, + }); Ok(()) } } diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index b24c50f0a9e00..6af3a91c43b65 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -525,7 +525,8 @@ pub mod v4 { ensure!( BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && inner.commission.max.is_none() && - inner.commission.throttle.is_none()), + inner.commission.last_updated.is_none() && + inner.commission.change_rate.is_none()), "a commission value has been incorrectly set" ); ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 823bb593a4798..bbe0ff117f811 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5426,9 +5426,9 @@ mod commission { Some((Perbill::from_percent(5), 900)), )); - // Throttle test. We will throttle commission to be a +1% commission increase every 2 + // Change rate test. We will set a change rate to be a +1% commission increase every 2 // blocks. - assert_ok!(Pools::set_commission_throttle( + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 } @@ -5437,13 +5437,11 @@ mod commission { BondedPool::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(5), 900)), + last_updated: Some(1), max: None, - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2_u64 - }, - previous_set_at: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 }) } ); @@ -5461,8 +5459,8 @@ mod commission { // Run to block 3 run_blocks(2); - // We now try to increase commission by 1%, and provide an initial payee. - // This should work, and set the `previous_set_at` field. + // We now try to increase commission by 1%, and provide an initial payee. This should + // work, and set the `last_updated` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, @@ -5472,20 +5470,18 @@ mod commission { BondedPool::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(6), 900)), + last_updated: Some(3_u64), max: None, - throttle: Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 2_u64 - }, - previous_set_at: Some(3_u64), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2_u64 }) } ); - // Attempt to increase the commission an additional 1% (now 2%) again immediately. - // this will fail as `previous_set_at` is now the current block, and at least 2 - // blocks need to pass before we can set commission again. + // Attempt to increase the commission an additional 1% (now 2%) again immediately. this + // will fail as `last_updated` is now the current block, and at least 2 blocks need to + // pass before we can set commission again. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), @@ -5552,7 +5548,7 @@ mod commission { pool_id: 1, current: Some((Perbill::from_percent(5), 900)), }, - Event::PoolCommissionThrottleUpdated { + Event::PoolCommissionChangeRateUpdated { pool_id: 1, change_rate: CommissionChangeRate { max_increase: Perbill::from_percent(1), @@ -5632,8 +5628,9 @@ mod commission { BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(50), 900)), + last_updated: Some(1), max: Some(Perbill::from_percent(50)), - throttle: None + change_rate: None } ); @@ -5683,19 +5680,20 @@ mod commission { BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(25), 900)), + last_updated: Some(1), max: Some(Perbill::from_percent(25)), - throttle: None + change_rate: None } ); }) } #[test] - fn set_commission_throttle_works_with_error_tests() { + fn set_commission_change_rate_works_with_error_tests() { ExtBuilder::default().build_and_execute(|| { // Provided pool does not exist assert_noop!( - Pools::set_commission_throttle( + Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 9999, CommissionChangeRate { @@ -5707,7 +5705,7 @@ mod commission { ); // Sender does not have permission to set commission assert_noop!( - Pools::set_commission_throttle( + Pools::set_commission_change_rate( RuntimeOrigin::signed(1), 1, CommissionChangeRate { @@ -5718,27 +5716,24 @@ mod commission { Error::::DoesNotHavePermission ); - // Set a commission throttle for pool 1 - assert_ok!(Pools::set_commission_throttle( + // Set a commission change rate for pool 1 + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 10_u64 } )); assert_eq!( - BondedPools::::get(1).unwrap().commission.throttle, - Some(CommissionThrottle { - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(5), - min_delay: 10_u64 - }, - previous_set_at: None + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10_u64 }) ); // We now try to half the min_delay - this will be disallowed. // A greater delay between commission changes is seen as more restrictive. assert_noop!( - Pools::set_commission_throttle( + Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { @@ -5746,13 +5741,13 @@ mod commission { min_delay: 5_u64 } ), - Error::::CommissionThrottleNotAllowed + Error::::CommissionChangeRateNotAllowed ); // We now try to increase the allowed max_increase - this will fail. // A smaller allowed commission change is seen as more restrictive. assert_noop!( - Pools::set_commission_throttle( + Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { @@ -5760,25 +5755,25 @@ mod commission { min_delay: 10_u64 } ), - Error::::CommissionThrottleNotAllowed + Error::::CommissionChangeRateNotAllowed ); // Successful more restrictive change of min_delay with the current max_increase - assert_ok!(Pools::set_commission_throttle( + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } )); // Successful more restrictive change of max_increase with the current min_delay - assert_ok!(Pools::set_commission_throttle( + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } )); // Successful more restrictive change of both max_increase and min_delay - assert_ok!(Pools::set_commission_throttle( + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } @@ -5786,8 +5781,8 @@ mod commission { // multi duration `min_delay` test. // - // set the commission throttle change_rate of 1% per 3000 blocks. - assert_ok!(Pools::set_commission_throttle( + // set the commission change_rate of 1% per 3000 blocks. + assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } @@ -5802,7 +5797,8 @@ mod commission { // with 3 `min_delay` durations passed. run_blocks(91); - // we should not be able to increase the commission to 5%: 1% beyond the throttle limit. + // we should not be able to increase the commission to 5%: 1% beyond the change rate + // limit. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index a151140700025..39d216f963784 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -54,7 +54,7 @@ pub trait WeightInfo { fn chill() -> Weight; fn set_commission() -> Weight; fn set_commission_max() -> Weight; - fn set_commission_throttle() -> Weight; + fn set_commission_change_rate() -> Weight; } /// Weights for pallet_nomination_pools using the Substrate node and recommended hardware. @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(149_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(146_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 140_000 nanoseconds. - Weight::from_ref_time(141_000_000) + // Minimum execution time: 139_000 nanoseconds. + Weight::from_ref_time(142_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(174_000_000) + // Minimum execution time: 167_000 nanoseconds. + Weight::from_ref_time(169_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(55_000_000) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(53_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(147_000_000) + // Minimum execution time: 145_000 nanoseconds. + Weight::from_ref_time(148_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -147,9 +147,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_527_998) - // Standard Error: 1_975 - .saturating_add(Weight::from_ref_time(33_319).saturating_mul(s.into())) + Weight::from_ref_time(53_892_229) + // Standard Error: 1_465 + .saturating_add(Weight::from_ref_time(18_250).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -165,9 +165,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(98_753_346) - // Standard Error: 2_435 - .saturating_add(Weight::from_ref_time(21_176).saturating_mul(s.into())) + Weight::from_ref_time(97_892_396) + // Standard Error: 2_321 + .saturating_add(Weight::from_ref_time(29_273).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -193,10 +193,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 149_000 nanoseconds. - Weight::from_ref_time(153_962_225) - // Standard Error: 3_236 - .saturating_add(Weight::from_ref_time(7_144).saturating_mul(s.into())) + // Minimum execution time: 150_000 nanoseconds. + Weight::from_ref_time(153_983_716) + // Standard Error: 2_835 + .saturating_add(Weight::from_ref_time(490).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -222,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 132_000 nanoseconds. - Weight::from_ref_time(133_000_000) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(134_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -241,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(58_320_810) - // Standard Error: 7_479 - .saturating_add(Weight::from_ref_time(1_470_472).saturating_mul(n.into())) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(58_876_667) + // Standard Error: 7_934 + .saturating_add(Weight::from_ref_time(1_425_602).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -263,10 +263,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_295_489) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(925).saturating_mul(n.into())) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(13_464_783) + // Standard Error: 319 + .saturating_add(Weight::from_ref_time(1_790).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -278,13 +278,13 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(7_000_000) + Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000) + Weight::from_ref_time(22_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -298,8 +298,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(61_000_000) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(60_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -313,15 +313,15 @@ impl WeightInfo for SubstrateWeight { } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(23_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) - fn set_commission_throttle() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000) + fn set_commission_change_rate() -> Weight { + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -343,8 +343,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(149_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(146_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -358,8 +358,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 140_000 nanoseconds. - Weight::from_ref_time(141_000_000) + // Minimum execution time: 139_000 nanoseconds. + Weight::from_ref_time(142_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -373,8 +373,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 168_000 nanoseconds. - Weight::from_ref_time(174_000_000) + // Minimum execution time: 167_000 nanoseconds. + Weight::from_ref_time(169_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -383,8 +383,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(55_000_000) + // Minimum execution time: 53_000 nanoseconds. + Weight::from_ref_time(53_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -403,8 +403,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(147_000_000) + // Minimum execution time: 145_000 nanoseconds. + Weight::from_ref_time(148_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -416,9 +416,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_527_998) - // Standard Error: 1_975 - .saturating_add(Weight::from_ref_time(33_319).saturating_mul(s.into())) + Weight::from_ref_time(53_892_229) + // Standard Error: 1_465 + .saturating_add(Weight::from_ref_time(18_250).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -434,9 +434,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(98_753_346) - // Standard Error: 2_435 - .saturating_add(Weight::from_ref_time(21_176).saturating_mul(s.into())) + Weight::from_ref_time(97_892_396) + // Standard Error: 2_321 + .saturating_add(Weight::from_ref_time(29_273).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -462,10 +462,10 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 149_000 nanoseconds. - Weight::from_ref_time(153_962_225) - // Standard Error: 3_236 - .saturating_add(Weight::from_ref_time(7_144).saturating_mul(s.into())) + // Minimum execution time: 150_000 nanoseconds. + Weight::from_ref_time(153_983_716) + // Standard Error: 2_835 + .saturating_add(Weight::from_ref_time(490).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -491,8 +491,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 132_000 nanoseconds. - Weight::from_ref_time(133_000_000) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(134_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -510,10 +510,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(58_320_810) - // Standard Error: 7_479 - .saturating_add(Weight::from_ref_time(1_470_472).saturating_mul(n.into())) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(58_876_667) + // Standard Error: 7_934 + .saturating_add(Weight::from_ref_time(1_425_602).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -532,10 +532,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_295_489) - // Standard Error: 296 - .saturating_add(Weight::from_ref_time(925).saturating_mul(n.into())) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(13_464_783) + // Standard Error: 319 + .saturating_add(Weight::from_ref_time(1_790).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -547,13 +547,13 @@ impl WeightInfo for () { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(7_000_000) + Weight::from_ref_time(8_000_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000) + Weight::from_ref_time(22_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -567,8 +567,8 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(61_000_000) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(60_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } @@ -582,15 +582,15 @@ impl WeightInfo for () { } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(23_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) - fn set_commission_throttle() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(23_000_000) + fn set_commission_change_rate() -> Weight { + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } From fc79f9302a8fc9f25e1ef3ddc6687a12bfc9bc80 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 20:04:54 +0700 Subject: [PATCH 137/221] clippy fix --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index ff03d9e68f883..186762564a92b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -690,7 +690,7 @@ impl Commission { self.current = None; } else { let (commission, payee) = - current.as_ref().map(|c| c).ok_or(Error::::NoCommissionSet)?; + current.as_ref().ok_or(Error::::NoCommissionSet)?; ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); ensure!( From 1106d26feacdb3f982cba1da1e05285d85730650 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 20:05:36 +0700 Subject: [PATCH 138/221] fmt --- frame/nomination-pools/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 186762564a92b..cbaa890142197 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -689,8 +689,7 @@ impl Commission { if current.is_none() { self.current = None; } else { - let (commission, payee) = - current.as_ref().ok_or(Error::::NoCommissionSet)?; + let (commission, payee) = current.as_ref().ok_or(Error::::NoCommissionSet)?; ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); ensure!( From ba4f4da06fa1429309d46bfd716746c4f41c06b4 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Mon, 26 Dec 2022 20:25:24 +0700 Subject: [PATCH 139/221] + commission --- .../nomination-pools/benchmarking/src/lib.rs | 2 +- frame/nomination-pools/src/weights.rs | 132 +++++++++--------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 528c8e755483d..fe4060ec8815a 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -293,7 +293,7 @@ frame_benchmarking::benchmarks! { claim_payout { let origin_weight = Pools::::depositor_min_bond() * 2u32.into(); let ed = CurrencyOf::::minimum_balance(); - let (depositor, pool_account) = create_pool_account::(0, origin_weight, None); + let (depositor, pool_account) = create_pool_account::(0, origin_weight, Some(Perbill::from_percent(50))); let reward_account = Pools::::create_reward_account(1); // Send funds to the reward account of the pool diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 39d216f963784..42315f56f690b 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(146_000_000) + // Minimum execution time: 141_000 nanoseconds. + Weight::from_ref_time(145_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -90,7 +90,7 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(142_000_000) + Weight::from_ref_time(144_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -104,8 +104,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 167_000 nanoseconds. - Weight::from_ref_time(169_000_000) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(173_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -114,8 +114,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(53_000_000) + // Minimum execution time: 63_000 nanoseconds. + Weight::from_ref_time(64_000_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -134,8 +134,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 145_000 nanoseconds. - Weight::from_ref_time(148_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(161_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -147,9 +147,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_892_229) - // Standard Error: 1_465 - .saturating_add(Weight::from_ref_time(18_250).saturating_mul(s.into())) + Weight::from_ref_time(53_719_370) + // Standard Error: 1_380 + .saturating_add(Weight::from_ref_time(21_902).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -165,9 +165,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(97_892_396) - // Standard Error: 2_321 - .saturating_add(Weight::from_ref_time(29_273).saturating_mul(s.into())) + Weight::from_ref_time(98_451_730) + // Standard Error: 2_703 + .saturating_add(Weight::from_ref_time(15_998).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -192,11 +192,9 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 150_000 nanoseconds. - Weight::from_ref_time(153_983_716) - // Standard Error: 2_835 - .saturating_add(Weight::from_ref_time(490).saturating_mul(s.into())) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_617_213) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -222,8 +220,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 131_000 nanoseconds. - Weight::from_ref_time(134_000_000) + // Minimum execution time: 130_000 nanoseconds. + Weight::from_ref_time(132_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -242,9 +240,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_876_667) - // Standard Error: 7_934 - .saturating_add(Weight::from_ref_time(1_425_602).saturating_mul(n.into())) + Weight::from_ref_time(58_747_903) + // Standard Error: 7_069 + .saturating_add(Weight::from_ref_time(1_343_154).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -263,10 +261,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(13_464_783) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(1_790).saturating_mul(n.into())) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_136_421) + // Standard Error: 259 + .saturating_add(Weight::from_ref_time(1_012).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -278,7 +276,7 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000) + Weight::from_ref_time(7_000_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) @@ -298,8 +296,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(60_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(61_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -307,7 +305,7 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(24_000_000) + Weight::from_ref_time(27_000_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -321,7 +319,7 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_change_rate() -> Weight { // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + Weight::from_ref_time(23_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -343,8 +341,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(146_000_000) + // Minimum execution time: 141_000 nanoseconds. + Weight::from_ref_time(145_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -359,7 +357,7 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(142_000_000) + Weight::from_ref_time(144_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -373,8 +371,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 167_000 nanoseconds. - Weight::from_ref_time(169_000_000) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(173_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -383,8 +381,8 @@ impl WeightInfo for () { // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) fn claim_payout() -> Weight { - // Minimum execution time: 53_000 nanoseconds. - Weight::from_ref_time(53_000_000) + // Minimum execution time: 63_000 nanoseconds. + Weight::from_ref_time(64_000_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -403,8 +401,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 145_000 nanoseconds. - Weight::from_ref_time(148_000_000) + // Minimum execution time: 144_000 nanoseconds. + Weight::from_ref_time(161_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -416,9 +414,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_892_229) - // Standard Error: 1_465 - .saturating_add(Weight::from_ref_time(18_250).saturating_mul(s.into())) + Weight::from_ref_time(53_719_370) + // Standard Error: 1_380 + .saturating_add(Weight::from_ref_time(21_902).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -434,9 +432,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(97_892_396) - // Standard Error: 2_321 - .saturating_add(Weight::from_ref_time(29_273).saturating_mul(s.into())) + Weight::from_ref_time(98_451_730) + // Standard Error: 2_703 + .saturating_add(Weight::from_ref_time(15_998).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -461,11 +459,9 @@ impl WeightInfo for () { // Storage: NominationPools CounterForBondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. - fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 150_000 nanoseconds. - Weight::from_ref_time(153_983_716) - // Standard Error: 2_835 - .saturating_add(Weight::from_ref_time(490).saturating_mul(s.into())) + fn withdraw_unbonded_kill(_s: u32, ) -> Weight { + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_617_213) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -491,8 +487,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 131_000 nanoseconds. - Weight::from_ref_time(134_000_000) + // Minimum execution time: 130_000 nanoseconds. + Weight::from_ref_time(132_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -511,9 +507,9 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_876_667) - // Standard Error: 7_934 - .saturating_add(Weight::from_ref_time(1_425_602).saturating_mul(n.into())) + Weight::from_ref_time(58_747_903) + // Standard Error: 7_069 + .saturating_add(Weight::from_ref_time(1_343_154).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -532,10 +528,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(13_464_783) - // Standard Error: 319 - .saturating_add(Weight::from_ref_time(1_790).saturating_mul(n.into())) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_136_421) + // Standard Error: 259 + .saturating_add(Weight::from_ref_time(1_012).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -547,7 +543,7 @@ impl WeightInfo for () { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000) + Weight::from_ref_time(7_000_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) @@ -567,8 +563,8 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 57_000 nanoseconds. - Weight::from_ref_time(60_000_000) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(61_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } @@ -576,7 +572,7 @@ impl WeightInfo for () { // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(24_000_000) + Weight::from_ref_time(27_000_000) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -590,7 +586,7 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_change_rate() -> Weight { // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + Weight::from_ref_time(23_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } From 8d04143a53aa3be5de625d2dc6ee4c847e1b46da Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 11:22:23 +0700 Subject: [PATCH 140/221] bound comm. to GlobalMax on payout --- frame/nomination-pools/src/lib.rs | 24 ++++++++----- frame/nomination-pools/src/tests.rs | 53 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cbaa890142197..07f5efad8a1fe 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -334,8 +334,8 @@ use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, CheckedAdd, CheckedSub, Convert, SaturatedConversion, Saturating, - StaticLookup, Zero, + AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, SaturatedConversion, + Saturating, StaticLookup, Zero, }, FixedPointNumber, Perbill, }; @@ -757,16 +757,20 @@ impl Commission { /// Gets the current commission (if any) and payee to be paid. /// - /// A zero commission along with a `None` payee is returned in the event a commission has not - /// been configured to the pool. - /// `None` is returned in the event a commission has not been configured to the pool. + /// Commission cannot go beyond `GlobalMaxCommission`. A zero commission along with a `None` + /// payee is returned in the event a commission has not been configured to the pool. `None` is + /// returned in the event a commission has not been configured to the pool. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, ) -> Option<(BalanceOf, T::AccountId)> { - self.current - .as_ref() - .map(|(commission, payee)| (*commission * *pending_rewards, payee.clone())) + self.current.as_ref().map(|(commission, payee)| { + ( + *commission.min(&GlobalMaxCommission::::get().unwrap_or(Bounded::max_value())) * + *pending_rewards, + payee.clone(), + ) + }) } /// Updates a commission's `last_updated` field. @@ -1501,7 +1505,9 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; - /// Maximum commission that can be set for all pools. + /// The maximum commission that can be charged. Checked on pool commission updates. Also applied + /// to payouts as to bound commissions that are > GlobalMaxCommission, necessary if a future + /// `GlobalMaxCommission` is lower than some current pool commissions. #[pallet::storage] pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index bbe0ff117f811..85e7d682e9ae2 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -897,6 +897,59 @@ mod claim_payout { }); } + #[test] + fn claim_payout_bounds_commission_above_global() { + ExtBuilder::default().build_and_execute(|| { + // temporarily remove global maximum so a higher commission can be set. + GlobalMaxCommission::::set(None); + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 75%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(75), 2)), + )); + + // re-introduce the global maximum to 50% - 25% lower than the current commission of the + // pool. + GlobalMaxCommission::::set(Some(Perbill::from_percent(50))); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(75), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // commission applied is 50%, not 75%. Has been bounded by `GlobalMaxCommission`. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 5, commission: 5 },] + ); + }) + } + #[test] fn do_reward_payout_with_various_commissions() { ExtBuilder::default().build_and_execute(|| { From 181ff0ffe6770884b65f178ab409ca5cb3969d82 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 11:23:37 +0700 Subject: [PATCH 141/221] rm commission_or_zero, use inline --- frame/nomination-pools/src/lib.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 07f5efad8a1fe..18b1990c6d8d5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -615,13 +615,6 @@ pub struct Commission { } impl Commission { - /// Get the current commission percentage of this pool. - /// - /// Returns zero if commission has not yet been configured. - fn commission_or_zero(&self) -> Perbill { - self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()) - } - /// Returns true if a commission percentage updating to `to` would exhaust the change_rate /// limit. /// @@ -632,11 +625,14 @@ impl Commission { /// Throttling is not applied to commission updates if `current` is still `None`. fn throttling(&self, to: &Perbill) -> bool { if let Some(t) = self.change_rate.as_ref() { + let commission_as_percent = + self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); + // factor previously updated block into whether user is throttled. if self.last_updated.map_or( // if no `last_updated` is set, throttled if the attempted increase in // commission is greater than `max_increase`. - (*to).saturating_sub(self.commission_or_zero()) > t.max_increase, + (*to).saturating_sub(commission_as_percent) > t.max_increase, |p| { // `min_delay` blocks must have been surpassed since last update. if >::block_number().saturating_sub(p) < t.min_delay { @@ -646,7 +642,7 @@ impl Commission { // update allow the attempted commission increase. // // the attempted increase in commission relative to the current commission. - let attempted_increase = (*to).saturating_sub(self.commission_or_zero()); + let attempted_increase = (*to).saturating_sub(commission_as_percent); // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(p); From 58ee938bb5e1cdf218fa4ae901f675ec70958280 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 11:24:47 +0700 Subject: [PATCH 142/221] fix tests --- frame/nomination-pools/src/tests.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 85e7d682e9ae2..7478b80993465 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5320,8 +5320,14 @@ mod commission { Some((Perbill::from_percent(50), 900)) )); - let commission = BondedPool::::get(1).unwrap().commission; - assert_eq!(commission.commission_or_zero(), Perbill::from_percent(50)); + let commission_as_percent = BondedPool::::get(1) + .unwrap() + .commission + .current + .as_ref() + .map(|(x, _)| *x) + .unwrap_or(Perbill::zero()); + assert_eq!(commission_as_percent, Perbill::from_percent(50)); // update commission only. assert_ok!(Pools::set_commission( From 92b2c9ca0f14be19d5ed0d5f14e346b09ea20160 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 11:35:00 +0700 Subject: [PATCH 143/221] only use global max on payout --- frame/nomination-pools/src/lib.rs | 20 +-- frame/nomination-pools/src/tests.rs | 31 ----- frame/nomination-pools/src/weights.rs | 176 +++++++++++++------------- 3 files changed, 95 insertions(+), 132 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 18b1990c6d8d5..2e023ed288949 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -674,10 +674,9 @@ impl Commission { /// Set the pool's commission. /// /// Update commission based on `current`. If a `None` current value is supplied, allow the - /// commission to be removed in all cases, without any throttling restrictions. Do not allow a - /// commission above global maximum if set. If `change_rate` is present, record the current - /// block as the previously updated commission. If the supplied commission is zero, `None` will - /// be inserted and `payee` will be ignored. + /// commission to be removed in all cases, without any throttling restrictions. If `change_rate` + /// is present, record the current block as the previously updated commission. If the supplied + /// commission is zero, `None` will be inserted and `payee` will be ignored. fn maybe_update_current( &mut self, current: &Option<(Perbill, T::AccountId)>, @@ -686,12 +685,7 @@ impl Commission { self.current = None; } else { let (commission, payee) = current.as_ref().ok_or(Error::::NoCommissionSet)?; - ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); - ensure!( - GlobalMaxCommission::::get().map_or(true, |g| commission <= &g), - Error::::GlobalMaxCommissionExceeded - ); ensure!( self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum @@ -1501,9 +1495,9 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; - /// The maximum commission that can be charged. Checked on pool commission updates. Also applied - /// to payouts as to bound commissions that are > GlobalMaxCommission, necessary if a future - /// `GlobalMaxCommission` is lower than some current pool commissions. + /// The maximum commission that can be charged. Used on commissions on payouts to bound pool + /// commissions that are > GlobalMaxCommission, necessary if a future `GlobalMaxCommission` is + /// lower than some current pool commissions. #[pallet::storage] pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; @@ -1721,8 +1715,6 @@ pub mod pallet { NoCommissionSet, /// No account has been set to receive commission. NoCommissionPayeeSet, - /// The supplied commission exceeds the global maximum commission. - GlobalMaxCommissionExceeded, /// The pool's max commission cannot be set higher than the existing value. MaxCommissionRestricted, /// The supplied commission exceeds the max allowed commission. diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 7478b80993465..bb7a06f285f44 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5373,9 +5373,6 @@ mod commission { #[test] fn global_max_commission_works() { ExtBuilder::default().build_and_execute(|| { - // Remove 90% global maximum commission - GlobalMaxCommission::::set(None); - // With global maximum commission removed, we can now set a 100% // commission. assert_ok!(Pools::set_commission( @@ -5391,20 +5388,6 @@ mod commission { Some((Perbill::from_percent(95), 900)) )); - // Bring back global max commission, this time of 80% - GlobalMaxCommission::::set(Some(Perbill::from_percent(80))); - - // Attempting to set a commission of 85% will now fail, even though it is - // more restrictive than the current commission of 95% - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(85), 900)) - ), - Error::::GlobalMaxCommissionExceeded - ); - // Succesfully set max commission of 75% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), @@ -5467,17 +5450,6 @@ mod commission { Error::::DoesNotHavePermission ); - // We attempt to increase the commission to 100%, which is disallowed due - // to global max commission of 90%. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(100), 900)) - ), - Error::::GlobalMaxCommissionExceeded - ); - // Set the initial commission to 5%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -5649,9 +5621,6 @@ mod commission { Error::::DoesNotHavePermission ); - // Remove global maximum commission - GlobalMaxCommission::::set(None); - // Set a max commission commission pool 1 to 80% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index 42315f56f690b..ceeaf07d12355 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -2,7 +2,7 @@ //! Autogenerated weights for pallet_nomination_pools //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-26, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-27, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `Rosss-MacBook-Pro-2.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 @@ -74,8 +74,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 141_000 nanoseconds. - Weight::from_ref_time(145_000_000) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(144_000_000) + // Minimum execution time: 138_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -98,25 +98,27 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 166_000 nanoseconds. - Weight::from_ref_time(173_000_000) - .saturating_add(T::DbWeight::get().reads(14)) + // Minimum execution time: 169_000 nanoseconds. + Weight::from_ref_time(171_000_000) + .saturating_add(T::DbWeight::get().reads(15)) .saturating_add(T::DbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 63_000 nanoseconds. - Weight::from_ref_time(64_000_000) - .saturating_add(T::DbWeight::get().reads(4)) + // Minimum execution time: 66_000 nanoseconds. + Weight::from_ref_time(67_000_000) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) @@ -134,8 +136,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(161_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -146,10 +148,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_719_370) - // Standard Error: 1_380 - .saturating_add(Weight::from_ref_time(21_902).saturating_mul(s.into())) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(54_325_335) + // Standard Error: 2_222 + .saturating_add(Weight::from_ref_time(22_226).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -165,9 +167,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(98_451_730) - // Standard Error: 2_703 - .saturating_add(Weight::from_ref_time(15_998).saturating_mul(s.into())) + Weight::from_ref_time(97_110_497) + // Standard Error: 8_292 + .saturating_add(Weight::from_ref_time(160_167).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -193,8 +195,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(153_617_213) + // Minimum execution time: 149_000 nanoseconds. + Weight::from_ref_time(160_286_818) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -220,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 130_000 nanoseconds. - Weight::from_ref_time(132_000_000) + // Minimum execution time: 133_000 nanoseconds. + Weight::from_ref_time(138_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -239,10 +241,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_747_903) - // Standard Error: 7_069 - .saturating_add(Weight::from_ref_time(1_343_154).saturating_mul(n.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(59_977_422) + // Standard Error: 31_619 + .saturating_add(Weight::from_ref_time(1_673_638).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -251,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 34_000 nanoseconds. - Weight::from_ref_time(35_000_000) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(39_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -261,10 +263,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_136_421) - // Standard Error: 259 - .saturating_add(Weight::from_ref_time(1_012).saturating_mul(n.into())) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_467_497) + // Standard Error: 560 + .saturating_add(Weight::from_ref_time(300).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -276,13 +278,13 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(7_000_000) + Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(22_000_000) + Weight::from_ref_time(23_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -296,23 +298,22 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(61_000_000) + // Minimum execution time: 63_000 nanoseconds. + Weight::from_ref_time(70_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(27_000_000) - .saturating_add(T::DbWeight::get().reads(2)) + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(24_000_000) + .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(24_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -341,8 +342,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { - // Minimum execution time: 141_000 nanoseconds. - Weight::from_ref_time(145_000_000) + // Minimum execution time: 142_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -356,8 +357,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 139_000 nanoseconds. - Weight::from_ref_time(144_000_000) + // Minimum execution time: 138_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -365,25 +366,27 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:3 w:3) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 166_000 nanoseconds. - Weight::from_ref_time(173_000_000) - .saturating_add(RocksDbWeight::get().reads(14)) + // Minimum execution time: 169_000 nanoseconds. + Weight::from_ref_time(171_000_000) + .saturating_add(RocksDbWeight::get().reads(15)) .saturating_add(RocksDbWeight::get().writes(13)) } // Storage: NominationPools PoolMembers (r:1 w:1) // Storage: NominationPools BondedPools (r:1 w:1) // Storage: NominationPools RewardPools (r:1 w:1) // Storage: System Account (r:1 w:1) + // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 63_000 nanoseconds. - Weight::from_ref_time(64_000_000) - .saturating_add(RocksDbWeight::get().reads(4)) + // Minimum execution time: 66_000 nanoseconds. + Weight::from_ref_time(67_000_000) + .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(4)) } // Storage: NominationPools PoolMembers (r:1 w:1) @@ -401,8 +404,8 @@ impl WeightInfo for () { // Storage: NominationPools SubPoolsStorage (r:1 w:1) // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { - // Minimum execution time: 144_000 nanoseconds. - Weight::from_ref_time(161_000_000) + // Minimum execution time: 143_000 nanoseconds. + Weight::from_ref_time(147_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -413,10 +416,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 52_000 nanoseconds. - Weight::from_ref_time(53_719_370) - // Standard Error: 1_380 - .saturating_add(Weight::from_ref_time(21_902).saturating_mul(s.into())) + // Minimum execution time: 51_000 nanoseconds. + Weight::from_ref_time(54_325_335) + // Standard Error: 2_222 + .saturating_add(Weight::from_ref_time(22_226).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -432,9 +435,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(98_451_730) - // Standard Error: 2_703 - .saturating_add(Weight::from_ref_time(15_998).saturating_mul(s.into())) + Weight::from_ref_time(97_110_497) + // Standard Error: 8_292 + .saturating_add(Weight::from_ref_time(160_167).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -460,8 +463,8 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 148_000 nanoseconds. - Weight::from_ref_time(153_617_213) + // Minimum execution time: 149_000 nanoseconds. + Weight::from_ref_time(160_286_818) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -487,8 +490,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 130_000 nanoseconds. - Weight::from_ref_time(132_000_000) + // Minimum execution time: 133_000 nanoseconds. + Weight::from_ref_time(138_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -506,10 +509,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 58_000 nanoseconds. - Weight::from_ref_time(58_747_903) - // Standard Error: 7_069 - .saturating_add(Weight::from_ref_time(1_343_154).saturating_mul(n.into())) + // Minimum execution time: 59_000 nanoseconds. + Weight::from_ref_time(59_977_422) + // Standard Error: 31_619 + .saturating_add(Weight::from_ref_time(1_673_638).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -518,8 +521,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 34_000 nanoseconds. - Weight::from_ref_time(35_000_000) + // Minimum execution time: 35_000 nanoseconds. + Weight::from_ref_time(39_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -528,10 +531,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 12_000 nanoseconds. - Weight::from_ref_time(13_136_421) - // Standard Error: 259 - .saturating_add(Weight::from_ref_time(1_012).saturating_mul(n.into())) + // Minimum execution time: 13_000 nanoseconds. + Weight::from_ref_time(14_467_497) + // Standard Error: 560 + .saturating_add(Weight::from_ref_time(300).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -543,13 +546,13 @@ impl WeightInfo for () { // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(7_000_000) + Weight::from_ref_time(8_000_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) fn update_roles() -> Weight { // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(22_000_000) + Weight::from_ref_time(23_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -563,23 +566,22 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(61_000_000) + // Minimum execution time: 63_000 nanoseconds. + Weight::from_ref_time(70_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } // Storage: NominationPools BondedPools (r:1 w:1) - // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn set_commission() -> Weight { - // Minimum execution time: 23_000 nanoseconds. - Weight::from_ref_time(27_000_000) - .saturating_add(RocksDbWeight::get().reads(2)) + // Minimum execution time: 24_000 nanoseconds. + Weight::from_ref_time(24_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 21_000 nanoseconds. - Weight::from_ref_time(22_000_000) + // Minimum execution time: 22_000 nanoseconds. + Weight::from_ref_time(24_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } From 4307da08b2bd502d21bbf4d57dc6d73359e2afad Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 14:02:39 +0700 Subject: [PATCH 144/221] correct migration reads + writes --- frame/nomination-pools/src/migration.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 6af3a91c43b65..0aecc144579c0 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -493,7 +493,7 @@ pub mod v4 { ); if current == 4 && onchain == 3 { - GlobalMaxCommission::::set(Some(Perbill::zero())); + GlobalMaxCommission::::set(Some(Zero::zero())); log!(info, "Set initial global max commission to 0%"); let mut translated = 0u64; @@ -504,9 +504,12 @@ pub mod v4 { current.put::>(); log!(info, "Upgraded {} pools, storage to version {:?}", translated, current); - T::DbWeight::get().reads_writes(translated + 1, translated + 1) + + // reads: translated + onchain version. + // writes: translated + current.put + initial global commission. + T::DbWeight::get().reads_writes(translated + 1, translated + 2) } else { - log!(info, "Migration did not executed. This probably should be removed"); + log!(info, "Migration did not execute. This probably should be removed"); T::DbWeight::get().reads(1) } } @@ -522,6 +525,7 @@ pub mod v4 { #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), &'static str> { + // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && inner.commission.max.is_none() && From c8ac28d67450585370fb4a4af94e9bdcb441bfc9 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 14:11:50 +0700 Subject: [PATCH 145/221] set last_updated on set initial change_rate --- frame/nomination-pools/src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 2e023ed288949..b8cd1224007a4 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -586,13 +586,14 @@ pub struct PoolRoles { /// Pool commission. /// /// The pool `root` can set commission configuration after pool creation. By default, all commission -/// values are `None`. Pool `root`s can set `max` and `change_rate` configurations before setting an -/// initial commission `current` value - the commission percentage and payee of commission. +/// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before +/// setting an initial `current` commission. /// -/// The `max` commission value can only be decreased after the initial value is set, to prevent -/// commission from repeatedly increasing. +/// `current` is a tuple of the commission percentage and payee of commission. `last_updated` keeps +/// track of which block `current` was last updated. A `max` commission value can only be decreased +/// after the initial value is set, to prevent commission from repeatedly increasing. /// -/// An optional commission change_rate allows the pool to set strict limits to how much commission +/// An optional commission `change_rate` allows the pool to set strict limits to how much commission /// can change in each update, and how often updates can take place. If a `change_rate` is set /// before a commission `current` is set, the initial commission `current` value will not be subject /// to throttling. Subsequent commission updates will be. @@ -740,8 +741,14 @@ impl Commission { change_rate: CommissionChangeRate, ) -> DispatchResult { ensure!(&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); - self.change_rate = Some(change_rate); + // if this is the first time a change rate is being set, update `last_updated` to the current + // block so we can check throttling from this initial block. + if self.change_rate.is_none() { + self.last_updated = Some(>::block_number()); + } + + self.change_rate = Some(change_rate); Ok(()) } From 3edb47e804c49aba25126083c4a59a9934862711 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 14:29:52 +0700 Subject: [PATCH 146/221] last_updated -> throttle_from --- .../nomination-pools/benchmarking/src/lib.rs | 6 +-- frame/nomination-pools/src/lib.rs | 34 ++++++++-------- frame/nomination-pools/src/migration.rs | 4 +- frame/nomination-pools/src/tests.rs | 40 ++++++++++++++----- 4 files changed, 52 insertions(+), 32 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index fe4060ec8815a..39c3ee8f89d02 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -693,12 +693,12 @@ frame_benchmarking::benchmarks! { verify { assert_eq!(BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(20), depositor)), - last_updated: Some(1u32.into()), max: Some(Perbill::from_percent(50)), change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(20), min_delay: 0u32.into() }), + throttle_from: Some(1u32.into()), }); } @@ -710,9 +710,9 @@ frame_benchmarking::benchmarks! { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(50), depositor)), - last_updated: Some(0u32.into()), max: Some(Perbill::from_percent(50)), change_rate: None, + throttle_from: Some(0u32.into()), }); } @@ -727,12 +727,12 @@ frame_benchmarking::benchmarks! { assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { current: None, - last_updated: None, max: None, change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), }), + throttle_from: None, }); } diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index b8cd1224007a4..cd77884415eb4 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -589,9 +589,9 @@ pub struct PoolRoles { /// values are `None`. Pool `root` can also set `max` and `change_rate` configurations before /// setting an initial `current` commission. /// -/// `current` is a tuple of the commission percentage and payee of commission. `last_updated` keeps -/// track of which block `current` was last updated. A `max` commission value can only be decreased -/// after the initial value is set, to prevent commission from repeatedly increasing. +/// `current` is a tuple of the commission percentage and payee of commission. `throttle_from` +/// keeps track of which block `current` was last updated. A `max` commission value can only be +/// decreased after the initial value is set, to prevent commission from repeatedly increasing. /// /// An optional commission `change_rate` allows the pool to set strict limits to how much commission /// can change in each update, and how often updates can take place. If a `change_rate` is set @@ -605,14 +605,15 @@ pub struct PoolRoles { pub struct Commission { /// Optional commission rate of the pool along with the account commission is paid to. pub current: Option<(Perbill, T::AccountId)>, - /// The block the previous commission update took place. - pub last_updated: Option, /// Optional maximum commission that can be set by the pool `root`. Once set, this value can /// only be updated to a decreased value. pub max: Option, /// Optional configuration around how often commission can be updated, and when the last /// commission update took place. pub change_rate: Option>, + /// The block throttling should be checked from. This value will be updated on all commission + /// updates and when setting an initial `change_rate`. + pub throttle_from: Option, } impl Commission { @@ -629,10 +630,11 @@ impl Commission { let commission_as_percent = self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); - // factor previously updated block into whether user is throttled. - if self.last_updated.map_or( - // if no `last_updated` is set, throttled if the attempted increase in - // commission is greater than `max_increase`. + if self.throttle_from.map_or( + // if no `throttle_from` exists, commission update is throttled via `max_increase`. + // + // Note: defensive only. `throttle_from` should always exist where `change_rate` + // has already been set, so this scenario should never happen. (*to).saturating_sub(commission_as_percent) > t.max_increase, |p| { // `min_delay` blocks must have been surpassed since last update. @@ -648,7 +650,7 @@ impl Commission { // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(p); - // calculate intervals passed since last `last_updated` + // calculate intervals passed since last `throttle_from`. let intervals_passed = if blocks_passed == Zero::zero() { Zero::zero() } else { @@ -704,7 +706,7 @@ impl Commission { /// return a dispatch error. /// /// If `current.0` is larger than an updated max commission value, then `current.0` will also be - /// updated to the new maximum. This will also register a `last_updated` update to the + /// updated to the new maximum. This will also register a `throttle_from` update to the /// commission. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { @@ -742,10 +744,10 @@ impl Commission { ) -> DispatchResult { ensure!(&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); - // if this is the first time a change rate is being set, update `last_updated` to the current - // block so we can check throttling from this initial block. + // if this is the first time a change rate is being set, update `throttle_from` to the + // current block so we can check throttling from this initial block. if self.change_rate.is_none() { - self.last_updated = Some(>::block_number()); + self.throttle_from = Some(>::block_number()); } self.change_rate = Some(change_rate); @@ -770,9 +772,9 @@ impl Commission { }) } - /// Updates a commission's `last_updated` field. + /// Updates a commission's `throttle_from` field. fn register_update(&mut self, now: T::BlockNumber) { - self.last_updated = Some(now); + self.throttle_from = Some(now); } /// Returns `true` if `change_rate` is less restrictive than the currently set change rate, if diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 0aecc144579c0..599fdffc2bf61 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -529,8 +529,8 @@ pub mod v4 { ensure!( BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && inner.commission.max.is_none() && - inner.commission.last_updated.is_none() && - inner.commission.change_rate.is_none()), + inner.commission.change_rate.is_none()) && + inner.commission.throttle_from.is_none(), "a commission value has been incorrectly set" ); ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index bb7a06f285f44..a9037eb1fc60f 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5468,12 +5468,12 @@ mod commission { BondedPool::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(5), 900)), - last_updated: Some(1), max: None, change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 - }) + }), + throttle_from: Some(1), } ); @@ -5491,7 +5491,7 @@ mod commission { run_blocks(2); // We now try to increase commission by 1%, and provide an initial payee. This should - // work, and set the `last_updated` field. + // work, and set the `throttle_from` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, @@ -5501,18 +5501,18 @@ mod commission { BondedPool::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(6), 900)), - last_updated: Some(3_u64), max: None, change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2_u64 - }) + }), + throttle_from: Some(3_u64), } ); // Attempt to increase the commission an additional 1% (now 2%) again immediately. this - // will fail as `last_updated` is now the current block, and at least 2 blocks need to - // pass before we can set commission again. + // will fail as `throttle_from` is now the current block, and at least 2 blocks + // need to pass before we can set commission again. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), @@ -5656,9 +5656,9 @@ mod commission { BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(50), 900)), - last_updated: Some(1), max: Some(Perbill::from_percent(50)), - change_rate: None + change_rate: None, + throttle_from: Some(1), } ); @@ -5708,9 +5708,9 @@ mod commission { BondedPools::::get(1).unwrap().commission, Commission { current: Some((Perbill::from_percent(25), 900)), - last_updated: Some(1), max: Some(Perbill::from_percent(25)), - change_rate: None + change_rate: None, + throttle_from: Some(1), } ); }) @@ -5815,6 +5815,24 @@ mod commission { 1, CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } )); + + // sanity check: the current pool Commission. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 30 + }), + throttle_from: Some(1), + } + ); + + // run `min_delay` blocks so commission can be set again. + run_blocks(30); + // pre-requisite: set the commission to 1%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), From 3a6150d326f236308e292cfecd67ce44beb0826f Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 14:39:42 +0700 Subject: [PATCH 147/221] throttling() tidy up --- frame/nomination-pools/src/lib.rs | 43 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cd77884415eb4..6429ae0364c37 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -617,54 +617,51 @@ pub struct Commission { } impl Commission { - /// Returns true if a commission percentage updating to `to` would exhaust the change_rate - /// limit. + /// Returns true if the current commission updating to `to` would exhaust the change rate + /// limits. /// /// A commission update will be throttled (disallowed) if: - /// 1. not enough blocks have passed since the previous commission update took place, and - /// 2. the new commission is larger than the maximum allowed increase. - /// - /// Throttling is not applied to commission updates if `current` is still `None`. + /// 1. not enough blocks have passed since the `throttle_from` block, if exists, or + /// 2. the new commission is greater than the maximum allowed increase. fn throttling(&self, to: &Perbill) -> bool { if let Some(t) = self.change_rate.as_ref() { let commission_as_percent = self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); + // the attempted increase in commission relative to the current commission. + let attempted_increase = (*to).saturating_sub(commission_as_percent); + if self.throttle_from.map_or( - // if no `throttle_from` exists, commission update is throttled via `max_increase`. - // // Note: defensive only. `throttle_from` should always exist where `change_rate` // has already been set, so this scenario should never happen. - (*to).saturating_sub(commission_as_percent) > t.max_increase, - |p| { - // `min_delay` blocks must have been surpassed since last update. - if >::block_number().saturating_sub(p) < t.min_delay { + // + // if no `throttle_from` exists. Therefore, the commission update is throttled via + // `max_increase` only. + attempted_increase > t.max_increase, + |f| { + // `min_delay` blocks must have been passed since `throttle_from`. + if >::block_number().saturating_sub(f) < t.min_delay { return true } - // ensure the `max_increase` durations surpassed since the previous commission + // ensure the `max_increase` durations passed since the previous commission // update allow the attempted commission increase. // - // the attempted increase in commission relative to the current commission. - let attempted_increase = (*to).saturating_sub(commission_as_percent); - // the total durations passed since the last commission update. - let blocks_passed = >::block_number().saturating_sub(p); + let blocks_passed = >::block_number().saturating_sub(f); - // calculate intervals passed since last `throttle_from`. + // calculate intervals passed since `throttle_from`. let intervals_passed = if blocks_passed == Zero::zero() { Zero::zero() } else { blocks_passed.div(t.min_delay).saturated_into::() }; - // the maximum allowed increase, where the current `max_increase` Perbill is - // converted into a u32 by multiplying itself with 100_u32 and multiplied by - // durations passed, before being converted back into a Perbill. + // calculate maximum allowed increase, where `max_increase` is converted into a + // u32 by multiplying itself with 100_u32, then multiplied by durations passed, + // before being converted back into a Perbill. let max_allowed_increase = Perbill::from_percent((t.max_increase * 100_u32) * intervals_passed); - // throttled (true) if attempted increase is greater than the maximum allowed - // increase, as a percentage. attempted_increase > max_allowed_increase }, ) { From ac853fa7f44c2329a80156c95d2d02672ec5c552 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 14:48:47 +0700 Subject: [PATCH 148/221] set `None` current on 0% + tests --- frame/nomination-pools/src/lib.rs | 13 +++++++----- frame/nomination-pools/src/tests.rs | 31 ++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6429ae0364c37..a01e65d743ffc 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -673,10 +673,10 @@ impl Commission { /// Set the pool's commission. /// - /// Update commission based on `current`. If a `None` current value is supplied, allow the - /// commission to be removed in all cases, without any throttling restrictions. If `change_rate` - /// is present, record the current block as the previously updated commission. If the supplied - /// commission is zero, `None` will be inserted and `payee` will be ignored. + /// Update commission based on `current`. If a `None` is supplied, allow the commission to be + /// removed without any change rate restrictions. If `change_rate` is present, update + /// `throttle_from` to the current block. If the supplied commission is zero, `None` will be + /// inserted and `payee` will be ignored. fn maybe_update_current( &mut self, current: &Option<(Perbill, T::AccountId)>, @@ -690,8 +690,11 @@ impl Commission { self.max.map_or(true, |m| commission <= &m), Error::::CommissionExceedsMaximum ); - self.current = Some((*commission, payee.clone())); + + self.current = + if commission == &Zero::zero() { None } else { Some((*commission, payee.clone())) }; } + let _ = self.register_update(>::block_number()); Ok(()) } diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a9037eb1fc60f..8c6cc9bbf7b3e 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5346,6 +5346,27 @@ mod commission { // remove the commission for pool 1. assert_ok!(Pools::set_commission(RuntimeOrigin::signed(900), 1, None)); + // test whether supplying a 0% commission along with a payee results in a None `current` + // being inserted. + // + // set an initial commission of 10% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + )); + // set the commission again to 0% + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(0), 900)) + )); + // commssion current should now be None, and `throttle_from` the current block. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { current: None, max: None, change_rate: None, throttle_from: Some(1) } + ); + // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), @@ -5364,7 +5385,15 @@ mod commission { pool_id: 1, current: Some((Perbill::from_percent(25), 901)) }, - Event::PoolCommissionUpdated { pool_id: 1, current: None } + Event::PoolCommissionUpdated { pool_id: 1, current: None }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(0), 900)) + } ] ); }); From 1532c027e125ad36aa8e3bf0b1409bd9e16d1e89 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 15:02:49 +0700 Subject: [PATCH 149/221] tidy up Commission impl --- frame/nomination-pools/src/lib.rs | 47 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a01e65d743ffc..101985fe56351 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -695,19 +695,17 @@ impl Commission { if commission == &Zero::zero() { None } else { Some((*commission, payee.clone())) }; } - let _ = self.register_update(>::block_number()); + let _ = self.register_update(); Ok(()) } /// Set the pool's maximum commission. /// - /// The pool's maximum commission can initially be set to any value, below global maximum if - /// set, and only smaller values thereafter. If larger values are attempted, this function will - /// return a dispatch error. + /// The pool's maximum commission can initially be set to any value, and only smaller values + /// thereafter. If larger values are attempted, this function will return a dispatch error. /// - /// If `current.0` is larger than an updated max commission value, then `current.0` will also be - /// updated to the new maximum. This will also register a `throttle_from` update to the - /// commission. + /// If `current.0` is larger than the updated max commission value, `current.0` will also be + /// updated to the new maximum. This will also register a `throttle_from` update. fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { @@ -728,37 +726,36 @@ impl Commission { .unwrap_or(false); if updated_current { - self.register_update(>::block_number()); + self.register_update(); } Ok(()) } - /// Set the pool's commission `change_rate` settings. + /// Set the pool's commission `change_rate`. /// /// Once a change rate configuration has been set, only more restrictive values can be set /// thereafter. These restrictions translate to increased `min_delay` values and decreased /// `max_increase` values. + /// + /// Update `throttle_from` to the current block upon setting change rate for the first time, so + /// throttling can be checked from this block. fn maybe_update_change_rate( &mut self, change_rate: CommissionChangeRate, ) -> DispatchResult { - ensure!(&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); + ensure!(&self.more_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); - // if this is the first time a change rate is being set, update `throttle_from` to the - // current block so we can check throttling from this initial block. if self.change_rate.is_none() { self.throttle_from = Some(>::block_number()); } - self.change_rate = Some(change_rate); Ok(()) } /// Gets the current commission (if any) and payee to be paid. /// - /// Commission cannot go beyond `GlobalMaxCommission`. A zero commission along with a `None` - /// payee is returned in the event a commission has not been configured to the pool. `None` is - /// returned in the event a commission has not been configured to the pool. + /// A zero commission along with a `None` payee is returned in the event a commission has not + /// been configured to the pool. Commission is bounded to `GlobalMaxCommission`. fn get_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -772,19 +769,19 @@ impl Commission { }) } - /// Updates a commission's `throttle_from` field. - fn register_update(&mut self, now: T::BlockNumber) { - self.throttle_from = Some(now); + /// Updates a commission's `throttle_from` field to the current block. + fn register_update(&mut self) { + self.throttle_from = Some(>::block_number()); } - /// Returns `true` if `change_rate` is less restrictive than the currently set change rate, if - /// present, or true otherwise. - fn less_restrictive(&self, change_rate: &CommissionChangeRate) -> bool { + /// Checks whether a change rate is less restrictive than the current change rate, if any. + /// + /// Any change rate is more restrictive than no change rate at all, so where no `change_rate` is + /// currently set, `true` is returned. + fn more_restrictive(&self, new: &CommissionChangeRate) -> bool { self.change_rate .as_ref() - .map(|t| { - change_rate.max_increase <= t.max_increase && change_rate.min_delay >= t.min_delay - }) + .map(|c| new.max_increase <= c.max_increase && new.min_delay >= c.min_delay) .unwrap_or(true) } } From 48f0ca61dc1784e0dbeb37ac2e9d26515f47b6e0 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 15:14:00 +0700 Subject: [PATCH 150/221] comments + tidy up --- frame/nomination-pools/src/lib.rs | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 101985fe56351..fb734b05b807f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1501,7 +1501,7 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; - /// The maximum commission that can be charged. Used on commissions on payouts to bound pool + /// The maximum commission that can be charged by a pool. Used on commission payouts to bound pool /// commissions that are > GlobalMaxCommission, necessary if a future `GlobalMaxCommission` is /// lower than some current pool commissions. #[pallet::storage] @@ -2383,14 +2383,9 @@ pub mod pallet { /// Set the commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. If the - /// pool has a max commission set, the commission supplied must be less or equal to that - /// value. - /// - /// If the max commission has not yet been set, then the commission range is not bounded. A - /// `payee` must be provided if commission has not yet been set (still `None`). Once - /// commission has been set, the `payee` can be omitted in further calls. If a `payee` - /// update is desired, the commission must still be passed into the call. + /// The dispatch origin of this call must be signed by the `root` role of the pool. Both a + /// commission percentage and a commission payee must be provided in the `current` tuple. Where + /// a `current` of `None` is provided, any current commission will be removed. #[pallet::call_index(14)] #[pallet::weight(T::WeightInfo::set_commission())] pub fn set_commission( @@ -2410,12 +2405,7 @@ pub mod pallet { /// Set the maximum commission of a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. If a - /// maximum commission already exists prior to this call, then the updated max commission - /// must be lower, otherwise this call will fail. - /// - /// This call also updates the pool's current commission to the new maximum if the current - /// commission is higher than the maximum supplied. + /// The dispatch origin of this call must be signed by the `root` role of the pool. #[pallet::call_index(15)] #[pallet::weight(T::WeightInfo::set_commission_max())] pub fn set_commission_max( @@ -2436,11 +2426,7 @@ pub mod pallet { /// Set the commission change rate for a pool. /// - /// The dispatch origin of this call must be signed by the `root` role of the pool. If a - /// change rate is already present, this call will only succeed if a more restrictive change - /// rate configuration is given. - /// - /// If a change rate configuration does not yet exist, the provided values are set. + /// The dispatch origin of this call must be signed by the `root` role of the pool. #[pallet::call_index(16)] #[pallet::weight(T::WeightInfo::set_commission_change_rate())] pub fn set_commission_change_rate( @@ -2674,8 +2660,8 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // Gets the commission percentage and payee to be paid if commission has - // been set. Otherwise, `None` is returned. + // Gets the commission percentage and payee to be paid if commission has been set. Otherwise, + // `None` is returned. let maybe_commission = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); if let Some((pool_commission, payee)) = maybe_commission { From a0a1a56ab0b313483a2c9b2531167319e8ddc023 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 15:14:14 +0700 Subject: [PATCH 151/221] fmt --- frame/nomination-pools/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index fb734b05b807f..17fe4dc0ce9e3 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1501,9 +1501,9 @@ pub mod pallet { #[pallet::storage] pub type MaxPoolMembersPerPool = StorageValue<_, u32, OptionQuery>; - /// The maximum commission that can be charged by a pool. Used on commission payouts to bound pool - /// commissions that are > GlobalMaxCommission, necessary if a future `GlobalMaxCommission` is - /// lower than some current pool commissions. + /// The maximum commission that can be charged by a pool. Used on commission payouts to bound + /// pool commissions that are > GlobalMaxCommission, necessary if a future `GlobalMaxCommission` + /// is lower than some current pool commissions. #[pallet::storage] pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; @@ -2384,8 +2384,8 @@ pub mod pallet { /// Set the commission of a pool. /// /// The dispatch origin of this call must be signed by the `root` role of the pool. Both a - /// commission percentage and a commission payee must be provided in the `current` tuple. Where - /// a `current` of `None` is provided, any current commission will be removed. + /// commission percentage and a commission payee must be provided in the `current` tuple. + /// Where a `current` of `None` is provided, any current commission will be removed. #[pallet::call_index(14)] #[pallet::weight(T::WeightInfo::set_commission())] pub fn set_commission( @@ -2660,8 +2660,8 @@ impl Pallet { member.last_recorded_reward_counter = current_reward_counter; reward_pool.register_claimed_reward(pending_rewards); - // Gets the commission percentage and payee to be paid if commission has been set. Otherwise, - // `None` is returned. + // Gets the commission percentage and payee to be paid if commission has been set. + // Otherwise, `None` is returned. let maybe_commission = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); if let Some((pool_commission, payee)) = maybe_commission { From 992ab1fec7a9a63ecc7af06e5eb66bf540ee528b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 15:32:29 +0700 Subject: [PATCH 152/221] fix min_delay of zero scenario --- .../nomination-pools/benchmarking/src/lib.rs | 2 +- frame/nomination-pools/src/lib.rs | 9 +- frame/nomination-pools/src/weights.rs | 140 +++++++++--------- 3 files changed, 78 insertions(+), 73 deletions(-) diff --git a/frame/nomination-pools/benchmarking/src/lib.rs b/frame/nomination-pools/benchmarking/src/lib.rs index 39c3ee8f89d02..7b0062b9c9231 100644 --- a/frame/nomination-pools/benchmarking/src/lib.rs +++ b/frame/nomination-pools/benchmarking/src/lib.rs @@ -732,7 +732,7 @@ frame_benchmarking::benchmarks! { max_increase: Perbill::from_percent(50), min_delay: 1000u32.into(), }), - throttle_from: None, + throttle_from: Some(1_u32.into()), }); } diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 17fe4dc0ce9e3..aab980955c27c 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -649,11 +649,16 @@ impl Commission { // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(f); - // calculate intervals passed since `throttle_from`. + // calculate intervals passed since `throttle_from`. If minimum delay is set to 0, + // infinite intervals have passed. let intervals_passed = if blocks_passed == Zero::zero() { Zero::zero() } else { - blocks_passed.div(t.min_delay).saturated_into::() + if t.min_delay == Zero::zero() { + Bounded::max_value() + } else { + blocks_passed.div(t.min_delay).saturated_into::() + } }; // calculate maximum allowed increase, where `max_increase` is converted into a diff --git a/frame/nomination-pools/src/weights.rs b/frame/nomination-pools/src/weights.rs index ceeaf07d12355..bc5471b6cf2b6 100644 --- a/frame/nomination-pools/src/weights.rs +++ b/frame/nomination-pools/src/weights.rs @@ -75,7 +75,7 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(147_000_000) + Weight::from_ref_time(145_000_000) .saturating_add(T::DbWeight::get().reads(17)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -89,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 138_000 nanoseconds. - Weight::from_ref_time(147_000_000) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(142_000_000) .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(12)) } @@ -105,8 +105,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 169_000 nanoseconds. - Weight::from_ref_time(171_000_000) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(169_000_000) .saturating_add(T::DbWeight::get().reads(15)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -116,8 +116,8 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 66_000 nanoseconds. - Weight::from_ref_time(67_000_000) + // Minimum execution time: 64_000 nanoseconds. + Weight::from_ref_time(65_000_000) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -137,7 +137,7 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(147_000_000) + Weight::from_ref_time(150_000_000) .saturating_add(T::DbWeight::get().reads(18)) .saturating_add(T::DbWeight::get().writes(13)) } @@ -148,10 +148,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(54_325_335) - // Standard Error: 2_222 - .saturating_add(Weight::from_ref_time(22_226).saturating_mul(s.into())) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_724_243) + // Standard Error: 1_649 + .saturating_add(Weight::from_ref_time(23_177).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -167,9 +167,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(97_110_497) - // Standard Error: 8_292 - .saturating_add(Weight::from_ref_time(160_167).saturating_mul(s.into())) + Weight::from_ref_time(99_545_463) + // Standard Error: 5_079 + .saturating_add(Weight::from_ref_time(31_957).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -195,8 +195,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 149_000 nanoseconds. - Weight::from_ref_time(160_286_818) + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_276_873) .saturating_add(T::DbWeight::get().reads(20)) .saturating_add(T::DbWeight::get().writes(17)) } @@ -222,8 +222,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 133_000 nanoseconds. - Weight::from_ref_time(138_000_000) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(132_000_000) .saturating_add(T::DbWeight::get().reads(21)) .saturating_add(T::DbWeight::get().writes(15)) } @@ -242,9 +242,9 @@ impl WeightInfo for SubstrateWeight { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(59_977_422) - // Standard Error: 31_619 - .saturating_add(Weight::from_ref_time(1_673_638).saturating_mul(n.into())) + Weight::from_ref_time(58_398_812) + // Standard Error: 7_443 + .saturating_add(Weight::from_ref_time(1_436_557).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(5)) @@ -253,8 +253,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(39_000_000) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -263,10 +263,10 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_467_497) - // Standard Error: 560 - .saturating_add(Weight::from_ref_time(300).saturating_mul(n.into())) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_360_093) + // Standard Error: 294 + .saturating_add(Weight::from_ref_time(514).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -277,8 +277,8 @@ impl WeightInfo for SubstrateWeight { // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) @@ -298,8 +298,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 63_000 nanoseconds. - Weight::from_ref_time(70_000_000) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(60_000_000) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -312,14 +312,14 @@ impl WeightInfo for SubstrateWeight { } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(24_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_change_rate() -> Weight { - // Minimum execution time: 21_000 nanoseconds. + // Minimum execution time: 22_000 nanoseconds. Weight::from_ref_time(23_000_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -343,7 +343,7 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:2 w:2) fn join() -> Weight { // Minimum execution time: 142_000 nanoseconds. - Weight::from_ref_time(147_000_000) + Weight::from_ref_time(145_000_000) .saturating_add(RocksDbWeight::get().reads(17)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -357,8 +357,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_transfer() -> Weight { - // Minimum execution time: 138_000 nanoseconds. - Weight::from_ref_time(147_000_000) + // Minimum execution time: 137_000 nanoseconds. + Weight::from_ref_time(142_000_000) .saturating_add(RocksDbWeight::get().reads(14)) .saturating_add(RocksDbWeight::get().writes(12)) } @@ -373,8 +373,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra_reward() -> Weight { - // Minimum execution time: 169_000 nanoseconds. - Weight::from_ref_time(171_000_000) + // Minimum execution time: 166_000 nanoseconds. + Weight::from_ref_time(169_000_000) .saturating_add(RocksDbWeight::get().reads(15)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -384,8 +384,8 @@ impl WeightInfo for () { // Storage: System Account (r:1 w:1) // Storage: NominationPools GlobalMaxCommission (r:1 w:0) fn claim_payout() -> Weight { - // Minimum execution time: 66_000 nanoseconds. - Weight::from_ref_time(67_000_000) + // Minimum execution time: 64_000 nanoseconds. + Weight::from_ref_time(65_000_000) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -405,7 +405,7 @@ impl WeightInfo for () { // Storage: NominationPools CounterForSubPoolsStorage (r:1 w:1) fn unbond() -> Weight { // Minimum execution time: 143_000 nanoseconds. - Weight::from_ref_time(147_000_000) + Weight::from_ref_time(150_000_000) .saturating_add(RocksDbWeight::get().reads(18)) .saturating_add(RocksDbWeight::get().writes(13)) } @@ -416,10 +416,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn pool_withdraw_unbonded(s: u32, ) -> Weight { - // Minimum execution time: 51_000 nanoseconds. - Weight::from_ref_time(54_325_335) - // Standard Error: 2_222 - .saturating_add(Weight::from_ref_time(22_226).saturating_mul(s.into())) + // Minimum execution time: 52_000 nanoseconds. + Weight::from_ref_time(53_724_243) + // Standard Error: 1_649 + .saturating_add(Weight::from_ref_time(23_177).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(5)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -435,9 +435,9 @@ impl WeightInfo for () { /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { // Minimum execution time: 95_000 nanoseconds. - Weight::from_ref_time(97_110_497) - // Standard Error: 8_292 - .saturating_add(Weight::from_ref_time(160_167).saturating_mul(s.into())) + Weight::from_ref_time(99_545_463) + // Standard Error: 5_079 + .saturating_add(Weight::from_ref_time(31_957).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -463,8 +463,8 @@ impl WeightInfo for () { // Storage: Staking Payee (r:0 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(_s: u32, ) -> Weight { - // Minimum execution time: 149_000 nanoseconds. - Weight::from_ref_time(160_286_818) + // Minimum execution time: 148_000 nanoseconds. + Weight::from_ref_time(153_276_873) .saturating_add(RocksDbWeight::get().reads(20)) .saturating_add(RocksDbWeight::get().writes(17)) } @@ -490,8 +490,8 @@ impl WeightInfo for () { // Storage: NominationPools BondedPools (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn create() -> Weight { - // Minimum execution time: 133_000 nanoseconds. - Weight::from_ref_time(138_000_000) + // Minimum execution time: 131_000 nanoseconds. + Weight::from_ref_time(132_000_000) .saturating_add(RocksDbWeight::get().reads(21)) .saturating_add(RocksDbWeight::get().writes(15)) } @@ -510,9 +510,9 @@ impl WeightInfo for () { /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { // Minimum execution time: 59_000 nanoseconds. - Weight::from_ref_time(59_977_422) - // Standard Error: 31_619 - .saturating_add(Weight::from_ref_time(1_673_638).saturating_mul(n.into())) + Weight::from_ref_time(58_398_812) + // Standard Error: 7_443 + .saturating_add(Weight::from_ref_time(1_436_557).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(5)) @@ -521,8 +521,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: Staking Ledger (r:1 w:0) fn set_state() -> Weight { - // Minimum execution time: 35_000 nanoseconds. - Weight::from_ref_time(39_000_000) + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(35_000_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -531,10 +531,10 @@ impl WeightInfo for () { // Storage: NominationPools CounterForMetadata (r:1 w:1) /// The range of component `n` is `[1, 256]`. fn set_metadata(n: u32, ) -> Weight { - // Minimum execution time: 13_000 nanoseconds. - Weight::from_ref_time(14_467_497) - // Standard Error: 560 - .saturating_add(Weight::from_ref_time(300).saturating_mul(n.into())) + // Minimum execution time: 12_000 nanoseconds. + Weight::from_ref_time(13_360_093) + // Standard Error: 294 + .saturating_add(Weight::from_ref_time(514).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(2)) } @@ -545,8 +545,8 @@ impl WeightInfo for () { // Storage: NominationPools GlobalMaxCommission (r:0 w:1) // Storage: NominationPools MaxPools (r:0 w:1) fn set_configs() -> Weight { - // Minimum execution time: 7_000 nanoseconds. - Weight::from_ref_time(8_000_000) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: NominationPools BondedPools (r:1 w:1) @@ -566,8 +566,8 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 63_000 nanoseconds. - Weight::from_ref_time(70_000_000) + // Minimum execution time: 58_000 nanoseconds. + Weight::from_ref_time(60_000_000) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(5)) } @@ -580,14 +580,14 @@ impl WeightInfo for () { } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_max() -> Weight { - // Minimum execution time: 22_000 nanoseconds. - Weight::from_ref_time(24_000_000) + // Minimum execution time: 21_000 nanoseconds. + Weight::from_ref_time(22_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: NominationPools BondedPools (r:1 w:1) fn set_commission_change_rate() -> Weight { - // Minimum execution time: 21_000 nanoseconds. + // Minimum execution time: 22_000 nanoseconds. Weight::from_ref_time(23_000_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) From 1a9f2c6aebb767901f2ae9927d8b29e299c0753d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 16:04:21 +0700 Subject: [PATCH 153/221] zero change rate tests + fixes --- frame/nomination-pools/src/lib.rs | 16 +-- frame/nomination-pools/src/tests.rs | 168 +++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 14 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index aab980955c27c..34021a56e5aa5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -649,20 +649,16 @@ impl Commission { // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(f); - // calculate intervals passed since `throttle_from`. If minimum delay is set to 0, - // infinite intervals have passed. - let intervals_passed = if blocks_passed == Zero::zero() { - Zero::zero() + // calculate intervals passed since `throttle_from`. If min delay is set to 0, + // set intervals passed to 1 as to only allow 1 * max_increase. + let intervals_passed = if t.min_delay == Zero::zero() { + 1_u32 } else { - if t.min_delay == Zero::zero() { - Bounded::max_value() - } else { - blocks_passed.div(t.min_delay).saturated_into::() - } + blocks_passed.div(t.min_delay).saturated_into::() }; // calculate maximum allowed increase, where `max_increase` is converted into a - // u32 by multiplying itself with 100_u32, then multiplied by durations passed, + // u32 by multiplying itself with 100_u32, then multiplied by intervals passed, // before being converted back into a Perbill. let max_allowed_increase = Perbill::from_percent((t.max_increase * 100_u32) * intervals_passed); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 8c6cc9bbf7b3e..5c7e7a6d283ea 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5826,7 +5826,7 @@ mod commission { assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, - CommissionChangeRate { max_increase: Perbill::from_percent(5), min_delay: 20_u64 } + CommissionChangeRate { max_increase: Perbill::from_percent(4), min_delay: 20_u64 } )); // Successful more restrictive change of both max_increase and min_delay @@ -5838,7 +5838,7 @@ mod commission { // multi duration `min_delay` test. // - // set the commission change_rate of 1% per 3000 blocks. + // set the commission change_rate of 1% per 30 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, @@ -5853,14 +5853,14 @@ mod commission { max: None, change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(1), - min_delay: 30 + min_delay: 30_u64 }), throttle_from: Some(1), } ); // run `min_delay` blocks so commission can be set again. - run_blocks(30); + run_blocks(30_u64); // pre-requisite: set the commission to 1%. assert_ok!(Pools::set_commission( @@ -5868,6 +5868,7 @@ mod commission { 1, Some((Perbill::from_percent(1), 900)), )); + // Run 90 blocks into the future so we are eligible to update commission // with 3 `min_delay` durations passed. run_blocks(91); @@ -5889,6 +5890,165 @@ mod commission { 1, Some((Perbill::from_percent(4), 900)) )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 10 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(5), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(4), + min_delay: 20 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(3), + min_delay: 30 + } + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 30 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(1), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(4), 900)) + } + ] + ); }); } + + #[test] + fn set_commission_change_rate_zero_max_increase_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% max increase test. + // + // set commission change rate to 0% per 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 10_u64 } + )); + + // even though there is a min delay of 10 blocks, a max increase of 0% essentially + // freezes the commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_min_delay_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% min delay test. + // + // set commission change rate to 1% with a 0 block min_delay. + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 0_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: None, + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 0 + }), + throttle_from: Some(1) + } + ); + + // since there is no min delay, we should be able to immediately set the commission. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + )); + + // sanity check: increasing again to more than +1% will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + ), + Error::::CommissionChangeThrottled + ); + }) + } + + #[test] + fn set_commission_change_rate_zero_value_works() { + ExtBuilder::default().build_and_execute(|| { + // Check zero values play nice. 0 `min_delay` and 0% max_increase test. + // + // set commission change rate to 0% per 0 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(0), min_delay: 0_u64 } + )); + + // even though there is no min delay, a max increase of 0% essentially freezes the + // commission. All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionChangeThrottled + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(0), + min_delay: 0_u64 + } + } + ] + ); + }) + } } From 460b9ec272294d86bfb76ce6f1273fb4c370eba9 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 16:06:32 +0700 Subject: [PATCH 154/221] + 0% max commission test --- frame/nomination-pools/src/tests.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5c7e7a6d283ea..f6834b98966e9 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5944,6 +5944,27 @@ mod commission { }); } + #[test] + fn set_commission_max_to_zero_works() { + ExtBuilder::default().build_and_execute(|| { + // 0% max commission test. + // + // set commission max 0%. + assert_ok!(Pools::set_commission_max(RuntimeOrigin::signed(900), 1, Zero::zero())); + + // a max commission of 0% essentially freezes the current commission, even when None. + // All commission update attempts will fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(1), 900)) + ), + Error::::CommissionExceedsMaximum + ); + }) + } + #[test] fn set_commission_change_rate_zero_max_increase_works() { ExtBuilder::default().build_and_execute(|| { From fbe08905302e11c6801cc1ed7cbef8f346d616dd Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 16:10:51 +0700 Subject: [PATCH 155/221] fix migration --- frame/nomination-pools/src/migration.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index 599fdffc2bf61..c8953b06140e3 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -527,10 +527,11 @@ pub mod v4 { fn post_upgrade(_: Vec) -> Result<(), &'static str> { // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( - BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && + BondedPools::::iter().all(|(_, inner)| + inner.commission.current.is_none() && inner.commission.max.is_none() && - inner.commission.change_rate.is_none()) && - inner.commission.throttle_from.is_none(), + inner.commission.change_rate.is_none() && + inner.commission.throttle_from.is_none()), "a commission value has been incorrectly set" ); ensure!(Pallet::::on_chain_storage_version() == 4, "wrong storage version"); From dfa3d1333adf271cae44bf142da8ac4658a1e419 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 16:11:04 +0700 Subject: [PATCH 156/221] fmt --- frame/nomination-pools/src/migration.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/migration.rs b/frame/nomination-pools/src/migration.rs index c8953b06140e3..ad87eb0fbe45c 100644 --- a/frame/nomination-pools/src/migration.rs +++ b/frame/nomination-pools/src/migration.rs @@ -527,8 +527,7 @@ pub mod v4 { fn post_upgrade(_: Vec) -> Result<(), &'static str> { // ensure all BondedPools items now contain an `inner.commission: Commission` field. ensure!( - BondedPools::::iter().all(|(_, inner)| - inner.commission.current.is_none() && + BondedPools::::iter().all(|(_, inner)| inner.commission.current.is_none() && inner.commission.max.is_none() && inner.commission.change_rate.is_none() && inner.commission.throttle_from.is_none()), From ac8a16aee08a23b18059c9a82172fcfffbdbf7c2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:17:40 +0700 Subject: [PATCH 157/221] do not transfer reward on 100% commission + test --- frame/nomination-pools/src/lib.rs | 23 +- frame/nomination-pools/src/tests.rs | 336 ++++++++++++++-------------- 2 files changed, 185 insertions(+), 174 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 34021a56e5aa5..fbf9164fedc5f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2670,7 +2670,7 @@ impl Pallet { pending_rewards = pending_rewards.saturating_sub(*pool_commission); // Send any non-zero `pool_commission` to the commission `payee`. - if pool_commission != &BalanceOf::::zero() { + if pool_commission > &Zero::zero() { T::Currency::transfer( &bonded_pool.reward_account(), &payee, @@ -2681,14 +2681,19 @@ impl Pallet { } // Transfer remaining payout to the member. - T::Currency::transfer( - &bonded_pool.reward_account(), - &member_account, - pending_rewards, - // defensive: the depositor has put existential deposit into the pool and it stays - // untouched, reward account shall not die. - ExistenceRequirement::AllowDeath, - )?; + // + // In scenarios where commission is 100%, `pending_rewards` will be zero. We therefore check if + // there is a non-zero payout to be transferred. + if pending_rewards > Zero::zero() { + T::Currency::transfer( + &bonded_pool.reward_account(), + &member_account, + pending_rewards, + // defensive: the depositor has put existential deposit into the pool and it stays + // untouched, reward account shall not die. + ExistenceRequirement::AllowDeath, + )?; + } Self::deposit_event(Event::::PaidOut { member: member_account.clone(), diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index f6834b98966e9..1637f37385658 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -950,97 +950,6 @@ mod claim_payout { }) } - #[test] - fn do_reward_payout_with_various_commissions() { - ExtBuilder::default().build_and_execute(|| { - let (mut member, bonded_pool, mut reward_pool) = - Pools::get_member_with_pools(&10).unwrap(); - - // top up commission payee account to existential deposit - let _ = Balances::deposit_creating(&2, 5); - - // Set a commission pool 1 to 50%, with a payee set to `2` - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - bonded_pool.id, - Some((Perbill::from_percent(33), 2)), - )); - - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(33), 2)) - }, - ] - ); - - // The pool earns 10 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); - - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] - ); - - // The pool earns 17 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] - ); - - // The pool earns 50 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] - ); - - // The pool earns 10439 points - assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); - assert_ok!(Pools::do_reward_payout( - &10, - &mut member, - &mut BondedPool::::get(1).unwrap(), - &mut reward_pool - )); - - // Then - assert_eq!( - pool_events_since_last_call(), - vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] - ); - }) - } - #[test] fn do_reward_payout_works_with_a_pool_of_1() { let del = |last_recorded_reward_counter| del_float(10, last_recorded_reward_counter); @@ -5355,7 +5264,7 @@ mod commission { 1, Some((Perbill::from_percent(10), 900)) )); - // set the commission again to 0% + // set the commission to 0% assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, @@ -5367,7 +5276,6 @@ mod commission { Commission { current: None, max: None, change_rate: None, throttle_from: Some(1) } ); - // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ @@ -5399,64 +5307,6 @@ mod commission { }); } - #[test] - fn global_max_commission_works() { - ExtBuilder::default().build_and_execute(|| { - // With global maximum commission removed, we can now set a 100% - // commission. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(100), 900)) - )); - - // Set a more restrictive commission of 95% - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(95), 900)) - )); - - // Succesfully set max commission of 75% - assert_ok!(Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(75) - )); - - // Succesfully update commission to 75% - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(75), 900)) - )); - - assert_eq!( - pool_events_since_last_call(), - vec![ - Event::Created { depositor: 10, pool_id: 1 }, - Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(100), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(95), 900)) - }, - Event::PoolMaxCommissionUpdated { - pool_id: 1, - max_commission: Perbill::from_percent(75) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(75), 900)) - } - ] - ); - }); - } - #[test] fn set_commission_handles_errors() { ExtBuilder::default().build_and_execute(|| { @@ -5486,8 +5336,9 @@ mod commission { Some((Perbill::from_percent(5), 900)), )); - // Change rate test. We will set a change rate to be a +1% commission increase every 2 - // blocks. + // Change rate test. + // + // We will set a change rate to be a +1% commission increase every 2 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, @@ -5502,11 +5353,11 @@ mod commission { max_increase: Perbill::from_percent(1), min_delay: 2_u64 }), - throttle_from: Some(1), + throttle_from: Some(1_u64), } ); - // We now try to increase commission to 10% (5% increase): this should be throttled. + // We now try to increase commission to 10% (5% increase). This should be throttled. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), @@ -5519,8 +5370,8 @@ mod commission { // Run to block 3 run_blocks(2); - // We now try to increase commission by 1%, and provide an initial payee. This should - // work, and set the `throttle_from` field. + // We now try to increase commission by 1% and provide an initial payee. This should + // succeed and set the `throttle_from` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, @@ -5539,9 +5390,9 @@ mod commission { } ); - // Attempt to increase the commission an additional 1% (now 2%) again immediately. this - // will fail as `throttle_from` is now the current block, and at least 2 blocks - // need to pass before we can set commission again. + // Attempt to increase the commission an additional 1% (now 2%). this will fail as + // `throttle_from` is now the current block. At least 2 blocks need to pass before we + // can set commission again. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), @@ -5575,19 +5426,31 @@ mod commission { Error::::CommissionChangeThrottled ); - // We will now set a max commission to the current 2%. This will also - // update commission.current to 2%. + // We will now set a max commission to the current 2%. This will also update the current + // commissio to 2%. assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, Perbill::from_percent(2) )); + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(2), 900)), + max: Some(Perbill::from_percent(2)), + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 2 + }), + throttle_from: Some(5) + } + ); // Run 2 blocks into the future so we are eligible to update commission again. run_blocks(2); // We will now attempt again to increase the commission by 1%, to 3%. This is within the - // change rate allowance, but the max_commission will now prevent us from going any + // change rate allowance, but `max_commission` will now prevent us from going any // higher. assert_noop!( Pools::set_commission( @@ -5598,7 +5461,6 @@ mod commission { Error::::CommissionExceedsMaximum ); - // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ @@ -5691,7 +5553,6 @@ mod commission { } ); - // Commission change events triggered successfully assert_eq!( pool_events_since_last_call(), vec![ @@ -6072,4 +5933,149 @@ mod commission { ); }) } + + #[test] + fn do_reward_payout_with_various_commissions() { + ExtBuilder::default().build_and_execute(|| { + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 33%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(33), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(33), 2)) + }, + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] + ); + + // The pool earns 17 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] + ); + + // The pool earns 50 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 34, commission: 16 },] + ); + + // The pool earns 10439 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10439)); + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 6994, commission: 3445 },] + ); + }) + } + + #[test] + fn do_reward_payout_with_100_percent_commission() { + ExtBuilder::default().build_and_execute(|| { + // turn off GlobalMaxCommission for this test. + GlobalMaxCommission::::set(None); + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // Ensure the commission equals the total amount of points. + let maybe_commission = + &BondedPools::::get(1).unwrap().commission.get_commission_and_payee(&10); + assert_eq!(*maybe_commission, Some((10_u128, 2_u128))); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Then + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 0, commission: 10 },] + ); + }) + } } From 9593b55ddd90b943d6df794464182ff93df5e78c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:21:25 +0700 Subject: [PATCH 158/221] + global max prevents 100% commission test --- frame/nomination-pools/src/tests.rs | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 1637f37385658..aacd07538c413 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6078,4 +6078,59 @@ mod commission { ); }) } + + + #[test] + fn global_max_prevents_100_percent_commission_payout() { + ExtBuilder::default().build_and_execute(|| { + // Note: GlobalMaxCommission is set at 90%. + + let (mut member, bonded_pool, mut reward_pool) = + Pools::get_member_with_pools(&10).unwrap(); + + // top up commission payee account to existential deposit + let _ = Balances::deposit_creating(&2, 5); + + // Set a commission pool 1 to 100%, with a payee set to `2` + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + bonded_pool.id, + Some((Perbill::from_percent(100), 2)), + )); + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 2)) + } + ] + ); + + // The pool earns 10 points + assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); + + // Ensure the commission equals 90% of the total points. + let maybe_commission = + &BondedPools::::get(1).unwrap().commission.get_commission_and_payee(&10); + assert_eq!(*maybe_commission, Some((9_u128, 2_u128))); + + // execute the payout + assert_ok!(Pools::do_reward_payout( + &10, + &mut member, + &mut BondedPool::::get(1).unwrap(), + &mut reward_pool + )); + + // Confirm the commission was only 9 points out of 10 points, and the payout was 1 out of 10 + // points, reflecting the 90% global max commission. + assert_eq!( + pool_events_since_last_call(), + vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 9 },] + ); + }) + } } From 0620b834db6cbe71d139c37e12e0e0f1684d8779 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:21:55 +0700 Subject: [PATCH 159/221] fmt --- frame/nomination-pools/src/lib.rs | 4 ++-- frame/nomination-pools/src/tests.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index fbf9164fedc5f..f46eee16b4fe7 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2682,8 +2682,8 @@ impl Pallet { // Transfer remaining payout to the member. // - // In scenarios where commission is 100%, `pending_rewards` will be zero. We therefore check if - // there is a non-zero payout to be transferred. + // In scenarios where commission is 100%, `pending_rewards` will be zero. We therefore check + // if there is a non-zero payout to be transferred. if pending_rewards > Zero::zero() { T::Currency::transfer( &bonded_pool.reward_account(), diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index aacd07538c413..959db80cda009 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6079,7 +6079,6 @@ mod commission { }) } - #[test] fn global_max_prevents_100_percent_commission_payout() { ExtBuilder::default().build_and_execute(|| { @@ -6125,8 +6124,8 @@ mod commission { &mut reward_pool )); - // Confirm the commission was only 9 points out of 10 points, and the payout was 1 out of 10 - // points, reflecting the 90% global max commission. + // Confirm the commission was only 9 points out of 10 points, and the payout was 1 out + // of 10 points, reflecting the 90% global max commission. assert_eq!( pool_events_since_last_call(), vec![Event::PaidOut { member: 10, pool_id: 1, payout: 1, commission: 9 },] From 99e9935234ac0cda1112d6f2c9c311a1a127ee18 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:29:37 +0700 Subject: [PATCH 160/221] use less_restrictive (allows same change rates) --- frame/nomination-pools/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f46eee16b4fe7..4152b5b107fdc 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -744,7 +744,7 @@ impl Commission { &mut self, change_rate: CommissionChangeRate, ) -> DispatchResult { - ensure!(&self.more_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); + ensure!(!&self.less_restrictive(&change_rate), Error::::CommissionChangeRateNotAllowed); if self.change_rate.is_none() { self.throttle_from = Some(>::block_number()); @@ -777,13 +777,13 @@ impl Commission { /// Checks whether a change rate is less restrictive than the current change rate, if any. /// - /// Any change rate is more restrictive than no change rate at all, so where no `change_rate` is - /// currently set, `true` is returned. - fn more_restrictive(&self, new: &CommissionChangeRate) -> bool { + /// No change rate will always be less restrictive than some change rate, so where no + /// `change_rate` is currently set, `false` is returned. + fn less_restrictive(&self, new: &CommissionChangeRate) -> bool { self.change_rate .as_ref() - .map(|c| new.max_increase <= c.max_increase && new.min_delay >= c.min_delay) - .unwrap_or(true) + .map(|c| new.max_increase > c.max_increase || new.min_delay < c.min_delay) + .unwrap_or(false) } } From 49220c7108a7027bb0f92401df9d09e8403f1561 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:44:32 +0700 Subject: [PATCH 161/221] commissions not above 100% tests --- frame/nomination-pools/src/tests.rs | 59 ++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 959db80cda009..90c80917e3335 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5329,6 +5329,25 @@ mod commission { Error::::DoesNotHavePermission ); + // Commission clamps to 100% where > 100% is provided. + // + // attempt to set a commission at 101%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(101), 900)), + )); + // ensure the commission actually set was at 100%. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(100), 900)), + max: None, + change_rate: None, + throttle_from: Some(1_u64), + } + ); + // Set the initial commission to 5%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -5466,6 +5485,10 @@ mod commission { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(100), 900)) + }, Event::PoolCommissionUpdated { pool_id: 1, current: Some((Perbill::from_percent(5), 900)), @@ -5512,6 +5535,28 @@ mod commission { Error::::DoesNotHavePermission ); + // Max commission clamps to 100% where > 100% is provided. + // + // attempt to set a commission at 101%. + + assert_ok!( + Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(101) + ) + ); + // ensure max commission actually set was at 100%. + assert_eq!( + BondedPool::::get(1).unwrap().commission, + Commission { + current: None, + max: Some(Perbill::from_percent(100)), + change_rate: None, + throttle_from: None, + } + ); + // Set a max commission commission pool 1 to 80% assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), @@ -5558,6 +5603,10 @@ mod commission { vec![ Event::Created { depositor: 10, pool_id: 1 }, Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolMaxCommissionUpdated { + pool_id: 1, + max_commission: Perbill::from_percent(100) + }, Event::PoolMaxCommissionUpdated { pool_id: 1, max_commission: Perbill::from_percent(80) @@ -5648,8 +5697,8 @@ mod commission { }) ); - // We now try to half the min_delay - this will be disallowed. - // A greater delay between commission changes is seen as more restrictive. + // We now try to half the min_delay - this will be disallowed. A greater delay between + // commission changes is seen as more restrictive. assert_noop!( Pools::set_commission_change_rate( RuntimeOrigin::signed(900), @@ -5662,8 +5711,8 @@ mod commission { Error::::CommissionChangeRateNotAllowed ); - // We now try to increase the allowed max_increase - this will fail. - // A smaller allowed commission change is seen as more restrictive. + // We now try to increase the allowed max_increase - this will fail. A smaller allowed + // commission change is seen as more restrictive. assert_noop!( Pools::set_commission_change_rate( RuntimeOrigin::signed(900), @@ -5899,7 +5948,7 @@ mod commission { ExtBuilder::default().build_and_execute(|| { // Check zero values play nice. 0 `min_delay` and 0% max_increase test. // - // set commission change rate to 0% per 0 blocks + // set commission change rate to 0% per 0 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, From 52bb0b9ee918a991d25105bc4f641263152ae844 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:50:29 +0700 Subject: [PATCH 162/221] fmt --- frame/nomination-pools/src/tests.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 90c80917e3335..85894e416fc74 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5539,13 +5539,11 @@ mod commission { // // attempt to set a commission at 101%. - assert_ok!( - Pools::set_commission_max( - RuntimeOrigin::signed(900), - 1, - Perbill::from_percent(101) - ) - ); + assert_ok!(Pools::set_commission_max( + RuntimeOrigin::signed(900), + 1, + Perbill::from_percent(101) + )); // ensure max commission actually set was at 100%. assert_eq!( BondedPool::::get(1).unwrap().commission, From 4ba4c0976f2ed99cfee17e7c02496f972c87d41d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Tue, 27 Dec 2022 23:56:18 +0700 Subject: [PATCH 163/221] use KeepAlive --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 4152b5b107fdc..a90b639620e9f 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -2691,7 +2691,7 @@ impl Pallet { pending_rewards, // defensive: the depositor has put existential deposit into the pool and it stays // untouched, reward account shall not die. - ExistenceRequirement::AllowDeath, + ExistenceRequirement::KeepAlive, )?; } From 06811c726bbaa139a1125d5585541a0846d0c3eb Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 05:37:29 +0000 Subject: [PATCH 164/221] Update frame/nomination-pools/src/lib.rs Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> --- frame/nomination-pools/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index a90b639620e9f..cf2f8ac30bad8 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -640,14 +640,14 @@ impl Commission { attempted_increase > t.max_increase, |f| { // `min_delay` blocks must have been passed since `throttle_from`. - if >::block_number().saturating_sub(f) < t.min_delay { + // the total durations passed since the last commission update. + let blocks_passed = >::block_number().saturating_sub(f); + // `min_delay` blocks must have been passed since `throttle_from`. + if blocks_passed < t.min_delay { return true } - // ensure the `max_increase` durations passed since the previous commission - // update allow the attempted commission increase. - // - // the total durations passed since the last commission update. - let blocks_passed = >::block_number().saturating_sub(f); + + // calculate intervals passed since `throttle_from`. If min delay is set to 0, // set intervals passed to 1 as to only allow 1 * max_increase. From 02bdc43e8243f4e11c21bc7ea87d4c372eb61c53 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 12:38:24 +0700 Subject: [PATCH 165/221] fmt --- frame/nomination-pools/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index cf2f8ac30bad8..05a2630719c22 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -639,15 +639,16 @@ impl Commission { // `max_increase` only. attempted_increase > t.max_increase, |f| { - // `min_delay` blocks must have been passed since `throttle_from`. - // the total durations passed since the last commission update. + // ensure the `max_increase` durations passed since the previous commission + // update allow the attempted commission increase. + // + // the total durations passed since the last commission update. let blocks_passed = >::block_number().saturating_sub(f); + // `min_delay` blocks must have been passed since `throttle_from`. if blocks_passed < t.min_delay { return true } - - // calculate intervals passed since `throttle_from`. If min delay is set to 0, // set intervals passed to 1 as to only allow 1 * max_increase. From d78909d5edf65926825184ca86139894204fb41b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 05:55:38 +0000 Subject: [PATCH 166/221] update docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gonçalo Pestana --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 05a2630719c22..f60cfb8b13bbe 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -590,7 +590,7 @@ pub struct PoolRoles { /// setting an initial `current` commission. /// /// `current` is a tuple of the commission percentage and payee of commission. `throttle_from` -/// keeps track of which block `current` was last updated. A `max` commission value can only be +/// keeps track of which block `current` was last updated. A `max` commission value can only be /// decreased after the initial value is set, to prevent commission from repeatedly increasing. /// /// An optional commission `change_rate` allows the pool to set strict limits to how much commission From 4c6855849373f98b80a9b63fe37a54666dfdf5b3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:10:42 +0700 Subject: [PATCH 167/221] some renaming --- frame/nomination-pools/src/lib.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index f60cfb8b13bbe..91d7b3c069afb 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -594,9 +594,7 @@ pub struct PoolRoles { /// decreased after the initial value is set, to prevent commission from repeatedly increasing. /// /// An optional commission `change_rate` allows the pool to set strict limits to how much commission -/// can change in each update, and how often updates can take place. If a `change_rate` is set -/// before a commission `current` is set, the initial commission `current` value will not be subject -/// to throttling. Subsequent commission updates will be. +/// can change in each update, and how often updates can take place. #[derive( Encode, Decode, DefaultNoBound, MaxEncodedLen, TypeInfo, DebugNoBound, PartialEq, Copy, Clone, )] @@ -679,10 +677,7 @@ impl Commission { /// removed without any change rate restrictions. If `change_rate` is present, update /// `throttle_from` to the current block. If the supplied commission is zero, `None` will be /// inserted and `payee` will be ignored. - fn maybe_update_current( - &mut self, - current: &Option<(Perbill, T::AccountId)>, - ) -> DispatchResult { + fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { if current.is_none() { self.current = None; } else { @@ -708,7 +703,7 @@ impl Commission { /// /// If `current.0` is larger than the updated max commission value, `current.0` will also be /// updated to the new maximum. This will also register a `throttle_from` update. - fn maybe_update_max(&mut self, new_max: Perbill) -> DispatchResult { + fn try_update_max(&mut self, new_max: Perbill) -> DispatchResult { if let Some(old) = self.max.as_mut() { if new_max > *old { return Err(Error::::MaxCommissionRestricted.into()) @@ -741,7 +736,7 @@ impl Commission { /// /// Update `throttle_from` to the current block upon setting change rate for the first time, so /// throttling can be checked from this block. - fn maybe_update_change_rate( + fn try_update_change_rate( &mut self, change_rate: CommissionChangeRate, ) -> DispatchResult { @@ -758,7 +753,7 @@ impl Commission { /// /// A zero commission along with a `None` payee is returned in the event a commission has not /// been configured to the pool. Commission is bounded to `GlobalMaxCommission`. - fn get_commission_and_payee( + fn maybe_commission_and_payee( &self, pending_rewards: &BalanceOf, ) -> Option<(BalanceOf, T::AccountId)> { @@ -2399,7 +2394,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_current(¤t)?; + bonded_pool.commission.try_update_current(¤t)?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, current }); Ok(()) @@ -2419,7 +2414,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_max(max_commission)?; + bonded_pool.commission.try_update_max(max_commission)?; bonded_pool.put(); Self::deposit_event(Event::::PoolMaxCommissionUpdated { pool_id, max_commission }); @@ -2440,7 +2435,7 @@ pub mod pallet { let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.maybe_update_change_rate(change_rate)?; + bonded_pool.commission.try_update_change_rate(change_rate)?; bonded_pool.put(); Self::deposit_event(Event::::PoolCommissionChangeRateUpdated { @@ -2664,7 +2659,7 @@ impl Pallet { // Gets the commission percentage and payee to be paid if commission has been set. // Otherwise, `None` is returned. - let maybe_commission = &bonded_pool.commission.get_commission_and_payee(&pending_rewards); + let maybe_commission = &bonded_pool.commission.maybe_commission_and_payee(&pending_rewards); if let Some((pool_commission, payee)) = maybe_commission { // Deduct any outstanding commission from the reward being claimed. From f9b2cfba87d5621c8e9c7ca69c05d554e4996e31 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:10:57 +0700 Subject: [PATCH 168/221] + change rate decreasing commissions test --- frame/nomination-pools/src/tests.rs | 98 +++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 85894e416fc74..419b4e7bba094 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5852,6 +5852,92 @@ mod commission { }); } + #[test] + fn change_rate_does_not_apply_to_decreasing_commission() { + ExtBuilder::default().build_and_execute(|| { + // set initial commission of the pool to 10%. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + )); + + // Set a commission change rate for pool 1, 1% every 10 blocks + assert_ok!(Pools::set_commission_change_rate( + RuntimeOrigin::signed(900), + 1, + CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 10_u64 } + )); + assert_eq!( + BondedPools::::get(1).unwrap().commission.change_rate, + Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }) + ); + + // run `min_delay` blocks to allow a commission update. + run_blocks(10_u64); + + // Attempt to decrease the commission by 5%. Should succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(5), 900)) + )); + + // run `min_delay` blocks to allow a commission update. + run_blocks(10_u64); + + // Attempt to *increase* the commission by 5%. Should fail. + assert_noop!( + Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(10), 900)) + ), + Error::::CommissionChangeThrottled + ); + + // sanity check: the current pool Commission state. + assert_eq!( + BondedPools::::get(1).unwrap().commission, + Commission { + current: Some((Perbill::from_percent(5), 900)), + max: None, + change_rate: Some(CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10_u64 + }), + throttle_from: Some(11), + } + ); + + assert_eq!( + pool_events_since_last_call(), + vec![ + Event::Created { depositor: 10, pool_id: 1 }, + Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(10), 900)) + }, + Event::PoolCommissionChangeRateUpdated { + pool_id: 1, + change_rate: CommissionChangeRate { + max_increase: Perbill::from_percent(1), + min_delay: 10 + } + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(5), 900)) + } + ] + ); + }); + } + #[test] fn set_commission_max_to_zero_works() { ExtBuilder::default().build_and_execute(|| { @@ -6106,8 +6192,10 @@ mod commission { assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); // Ensure the commission equals the total amount of points. - let maybe_commission = - &BondedPools::::get(1).unwrap().commission.get_commission_and_payee(&10); + let maybe_commission = &BondedPools::::get(1) + .unwrap() + .commission + .maybe_commission_and_payee(&10); assert_eq!(*maybe_commission, Some((10_u128, 2_u128))); // execute the payout @@ -6159,8 +6247,10 @@ mod commission { assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); // Ensure the commission equals 90% of the total points. - let maybe_commission = - &BondedPools::::get(1).unwrap().commission.get_commission_and_payee(&10); + let maybe_commission = &BondedPools::::get(1) + .unwrap() + .commission + .maybe_commission_and_payee(&10); assert_eq!(*maybe_commission, Some((9_u128, 2_u128))); // execute the payout From 10968e1ab42c7a216a2201a037ef14d3a8b82ed4 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:15:04 +0700 Subject: [PATCH 169/221] change test name --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 419b4e7bba094..7f5c977b5b81a 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5853,7 +5853,7 @@ mod commission { } #[test] - fn change_rate_does_not_apply_to_decreasing_commission() { + fn max_increase_does_not_apply_to_decreasing_commission() { ExtBuilder::default().build_and_execute(|| { // set initial commission of the pool to 10%. assert_ok!(Pools::set_commission( From 2363716302f47dccad24ba5c0959dfc971144141 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:30:51 +0700 Subject: [PATCH 170/221] test fixes + commission decrease test --- frame/nomination-pools/src/lib.rs | 5 +++ frame/nomination-pools/src/tests.rs | 62 +++++++++++++++++------------ 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 91d7b3c069afb..d67ab8b1b6fa5 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -626,6 +626,11 @@ impl Commission { let commission_as_percent = self.current.as_ref().map(|(x, _)| *x).unwrap_or(Perbill::zero()); + // do not throttle if `to` is the same or a decrease in commission. + if *to <= commission_as_percent { + return false + } + // the attempted increase in commission relative to the current commission. let attempted_increase = (*to).saturating_sub(commission_as_percent); diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 7f5c977b5b81a..dbf2ab2db012d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5357,7 +5357,7 @@ mod commission { // Change rate test. // - // We will set a change rate to be a +1% commission increase every 2 blocks. + // Set a change rate to be a +1% commission increase every 2 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, @@ -5376,7 +5376,7 @@ mod commission { } ); - // We now try to increase commission to 10% (5% increase). This should be throttled. + // Now try to increase commission to 10% (5% increase). This should be throttled. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), @@ -5389,7 +5389,7 @@ mod commission { // Run to block 3 run_blocks(2); - // We now try to increase commission by 1% and provide an initial payee. This should + // Now try to increase commission by 1% and provide an initial payee. This should // succeed and set the `throttle_from` field. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), @@ -5409,14 +5409,14 @@ mod commission { } ); - // Attempt to increase the commission an additional 1% (now 2%). this will fail as + // Attempt to increase the commission an additional 1% (now 7%). this will fail as // `throttle_from` is now the current block. At least 2 blocks need to pass before we // can set commission again. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some((Perbill::from_percent(2), 900)) + Some((Perbill::from_percent(7), 900)) ), Error::::CommissionChangeThrottled ); @@ -5424,58 +5424,58 @@ mod commission { // Run 2 blocks into the future, to block 3. run_blocks(2); - // We can now successfully increase the commission again, to 2%. + // Can now successfully increase the commission again, to 7%. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some((Perbill::from_percent(2), 900)), + Some((Perbill::from_percent(7), 900)), )); // Run 2 blocks into the future, to block 5. run_blocks(2); - // We've now surpassed the `min_delay` threshold, but the `max_increase` threshold is - // still at play. An attempted commission change now to 4% (+2% increase) should fail. + // Now surpassed the `min_delay` threshold, but the `max_increase` threshold is + // still at play. An attempted commission change now to 8% (+2% increase) should fail. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some((Perbill::from_percent(4), 900)), + Some((Perbill::from_percent(9), 900)), ), Error::::CommissionChangeThrottled ); - // We will now set a max commission to the current 2%. This will also update the current - // commissio to 2%. + // Now set a max commission to the current 5%. This will also update the current + // commission to 5%. assert_ok!(Pools::set_commission_max( RuntimeOrigin::signed(900), 1, - Perbill::from_percent(2) + Perbill::from_percent(5) )); assert_eq!( BondedPool::::get(1).unwrap().commission, Commission { - current: Some((Perbill::from_percent(2), 900)), - max: Some(Perbill::from_percent(2)), + current: Some((Perbill::from_percent(5), 900)), + max: Some(Perbill::from_percent(5)), change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 2 }), - throttle_from: Some(5) + throttle_from: Some(7) } ); // Run 2 blocks into the future so we are eligible to update commission again. run_blocks(2); - // We will now attempt again to increase the commission by 1%, to 3%. This is within the + // Now attempt again to increase the commission by 1%, to 6%. This is within the // change rate allowance, but `max_commission` will now prevent us from going any // higher. assert_noop!( Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some((Perbill::from_percent(3), 900)), + Some((Perbill::from_percent(6), 900)), ), Error::::CommissionExceedsMaximum ); @@ -5491,7 +5491,7 @@ mod commission { }, Event::PoolCommissionUpdated { pool_id: 1, - current: Some((Perbill::from_percent(5), 900)), + current: Some((Perbill::from_percent(5), 900)) }, Event::PoolCommissionChangeRateUpdated { pool_id: 1, @@ -5506,11 +5506,11 @@ mod commission { }, Event::PoolCommissionUpdated { pool_id: 1, - current: Some((Perbill::from_percent(2), 900)) + current: Some((Perbill::from_percent(7), 900)) }, Event::PoolMaxCommissionUpdated { pool_id: 1, - max_commission: Perbill::from_percent(2) + max_commission: Perbill::from_percent(5) } ] ); @@ -5879,13 +5879,21 @@ mod commission { // run `min_delay` blocks to allow a commission update. run_blocks(10_u64); - // Attempt to decrease the commission by 5%. Should succeed. + // Test `max_increase`: attempt to decrease the commission by 5%. Should succeed. assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), 1, Some((Perbill::from_percent(5), 900)) )); + // Test `min_delay`: *immediately* attempt to decrease the commission by 2%. Should + // succeed. + assert_ok!(Pools::set_commission( + RuntimeOrigin::signed(900), + 1, + Some((Perbill::from_percent(3), 900)) + )); + // run `min_delay` blocks to allow a commission update. run_blocks(10_u64); @@ -5894,16 +5902,16 @@ mod commission { Pools::set_commission( RuntimeOrigin::signed(900), 1, - Some((Perbill::from_percent(10), 900)) + Some((Perbill::from_percent(8), 900)) ), Error::::CommissionChangeThrottled ); - // sanity check: the current pool Commission state. + // Sanity check: the resulting pool Commission state. assert_eq!( BondedPools::::get(1).unwrap().commission, Commission { - current: Some((Perbill::from_percent(5), 900)), + current: Some((Perbill::from_percent(3), 900)), max: None, change_rate: Some(CommissionChangeRate { max_increase: Perbill::from_percent(1), @@ -5932,6 +5940,10 @@ mod commission { Event::PoolCommissionUpdated { pool_id: 1, current: Some((Perbill::from_percent(5), 900)) + }, + Event::PoolCommissionUpdated { + pool_id: 1, + current: Some((Perbill::from_percent(3), 900)) } ] ); From 1452bdb8dc4f5d90dc0572148078acab58123bcf Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:32:28 +0700 Subject: [PATCH 171/221] test name --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index dbf2ab2db012d..b86bfe074c1cc 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5853,7 +5853,7 @@ mod commission { } #[test] - fn max_increase_does_not_apply_to_decreasing_commission() { + fn change_rate_does_not_apply_to_decreasing_commission() { ExtBuilder::default().build_and_execute(|| { // set initial commission of the pool to 10%. assert_ok!(Pools::set_commission( From 860da81bee9c5613d41013b8652c59948db16f6c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:37:08 +0700 Subject: [PATCH 172/221] feedback nitpicks amendments --- frame/nomination-pools/src/lib.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index d67ab8b1b6fa5..1e94624fb0a46 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -720,9 +720,9 @@ impl Commission { let updated_current = self .current .as_mut() - .map(|(x, _)| { - let u = *x > new_max; - *x = (*x).min(new_max); + .map(|(c, _)| { + let u = *c > new_max; + *c = (*c).min(new_max); u }) .unwrap_or(false); @@ -756,8 +756,8 @@ impl Commission { /// Gets the current commission (if any) and payee to be paid. /// - /// A zero commission along with a `None` payee is returned in the event a commission has not - /// been configured to the pool. Commission is bounded to `GlobalMaxCommission`. + /// /// `None` is returned if a commission has not been set. Commission is bounded to + /// `GlobalMaxCommission`. fn maybe_commission_and_payee( &self, pending_rewards: &BalanceOf, @@ -2393,15 +2393,18 @@ pub mod pallet { pub fn set_commission( origin: OriginFor, pool_id: PoolId, - current: Option<(Perbill, T::AccountId)>, + new_commission: Option<(Perbill, T::AccountId)>, ) -> DispatchResult { let who = ensure_signed(origin)?; let mut bonded_pool = BondedPool::::get(pool_id).ok_or(Error::::PoolNotFound)?; ensure!(bonded_pool.can_set_commission(&who), Error::::DoesNotHavePermission); - bonded_pool.commission.try_update_current(¤t)?; + bonded_pool.commission.try_update_current(&new_commission)?; bonded_pool.put(); - Self::deposit_event(Event::::PoolCommissionUpdated { pool_id, current }); + Self::deposit_event(Event::::PoolCommissionUpdated { + pool_id, + current: new_commission, + }); Ok(()) } From 22b63568efb26f6046a1ef8f730afaf36275804a Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:39:49 +0700 Subject: [PATCH 173/221] + doc --- frame/nomination-pools/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 1e94624fb0a46..c4a99a5dca953 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -793,6 +793,8 @@ impl Commission { /// The pool root is able to set a commission change rate for their pool. A commission change rate /// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of /// blocks that must elapse before commission updates are allowed again. +/// +/// Commission change rates are not applied to decreases in commission. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionChangeRate { /// The maximum amount the commission can be updated by per `min_delay` period. From c1fa307e944d9131b4433b9ac1e5f1ee1f7acc26 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 13:40:07 +0700 Subject: [PATCH 174/221] fmt --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index c4a99a5dca953..6f8dfeb8d4690 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -793,7 +793,7 @@ impl Commission { /// The pool root is able to set a commission change rate for their pool. A commission change rate /// consists of 2 values; (1) the maximum allowed commission change, and (2) the minimum amount of /// blocks that must elapse before commission updates are allowed again. -/// +/// /// Commission change rates are not applied to decreases in commission. #[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Debug, PartialEq, Copy, Clone)] pub struct CommissionChangeRate { From c78b99c6b71816041aa054a9cb9baf34cf190253 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 14:59:44 +0700 Subject: [PATCH 175/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index b86bfe074c1cc..fb2c62702cb1d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6234,7 +6234,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit + // top up the commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 100%, with a payee set to `2` From 4072074c6829b75316a0237ed19c6afeb1c35e5e Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 15:03:15 +0700 Subject: [PATCH 176/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index fb2c62702cb1d..b86bfe074c1cc 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6234,7 +6234,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up the commission payee account to existential deposit + // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 100%, with a payee set to `2` From 55fd2effa6f1a3b4903816729c525147527fc984 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 15:23:10 +0700 Subject: [PATCH 177/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index b86bfe074c1cc..28a827ee747e2 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6200,7 +6200,7 @@ mod commission { ] ); - // The pool earns 10 points + // Pool earns 10 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); // Ensure the commission equals the total amount of points. From c0b798c13ce942dc2ddc55d003303465805ea65b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 15:34:04 +0700 Subject: [PATCH 178/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 28a827ee747e2..9240e791998fd 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6210,7 +6210,7 @@ mod commission { .maybe_commission_and_payee(&10); assert_eq!(*maybe_commission, Some((10_u128, 2_u128))); - // execute the payout + // Execute the payout assert_ok!(Pools::do_reward_payout( &10, &mut member, From 8f2b9fd0b73d0638503058aa6d8ed3b94aac511b Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 15:38:51 +0700 Subject: [PATCH 179/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 9240e791998fd..1a1a45b068b0a 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6229,7 +6229,7 @@ mod commission { #[test] fn global_max_prevents_100_percent_commission_payout() { ExtBuilder::default().build_and_execute(|| { - // Note: GlobalMaxCommission is set at 90%. + // Note: GlobalMaxCommission is set to 90%. let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); From 69d55ea9fb17a50aeda648e97372e9d246e3eaa3 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 15:39:38 +0700 Subject: [PATCH 180/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 1a1a45b068b0a..dd83c56f30462 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6181,7 +6181,7 @@ mod commission { // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); - // Set a commission pool 1 to 100%, with a payee set to `2` + // Set commission of pool 1 to 100%, with a payee set to `2` assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, From 8b51bb977aaa12a6541ea45c54c1b42cc96c13fe Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:12:04 +0700 Subject: [PATCH 181/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index dd83c56f30462..f5bf2871b65c5 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6181,7 +6181,7 @@ mod commission { // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); - // Set commission of pool 1 to 100%, with a payee set to `2` + // Set commission of pool 1 to 100%, with a payee of `2` assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, From 13857d51f444a6963e63ce71a3d8074312693b89 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:52:37 +0700 Subject: [PATCH 182/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index f5bf2871b65c5..5cc4c40682e7d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6178,7 +6178,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit + // top up commission payee account to existential deposit. let _ = Balances::deposit_creating(&2, 5); // Set commission of pool 1 to 100%, with a payee of `2` From 7059ec409c63de0609cb790fda2724061e45cbfa Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:53:36 +0700 Subject: [PATCH 183/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5cc4c40682e7d..6f0f0de97159b 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6137,7 +6137,7 @@ mod commission { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] ); - // The pool earns 50 points + // Pool earns 50 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); assert_ok!(Pools::do_reward_payout( &10, From f17e7145707daee58a68710d22c5f7fc8cf26457 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:53:51 +0700 Subject: [PATCH 184/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 6f0f0de97159b..a45f89662bc24 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6122,7 +6122,7 @@ mod commission { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] ); - // The pool earns 17 points + // Pool earns 17 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); assert_ok!(Pools::do_reward_payout( &10, From f0db5057d3f8f4e722e27c856b806def23bd9813 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:57:03 +0700 Subject: [PATCH 185/221] test --- frame/nomination-pools/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a45f89662bc24..a5675351cb2d4 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6085,7 +6085,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit + // Top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 33%, with a payee set to `2` @@ -6106,7 +6106,7 @@ mod commission { ] ); - // The pool earns 10 points + // Pool earns 10 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); assert_ok!(Pools::do_reward_payout( From 6959bbb745f766c3a7637504d806e746733fb8cd Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 16:58:57 +0700 Subject: [PATCH 186/221] one more test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a5675351cb2d4..fbf81bf59e20c 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6044,7 +6044,7 @@ mod commission { ExtBuilder::default().build_and_execute(|| { // Check zero values play nice. 0 `min_delay` and 0% max_increase test. // - // set commission change rate to 0% per 0 blocks. + // Set commission change rate to 0% per 0 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, From 3abc71b5a03c50ae7a1edd73f2ccab9e0101cadc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:23:21 +0700 Subject: [PATCH 187/221] test --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index fbf81bf59e20c..3146a7b976150 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6042,7 +6042,7 @@ mod commission { #[test] fn set_commission_change_rate_zero_value_works() { ExtBuilder::default().build_and_execute(|| { - // Check zero values play nice. 0 `min_delay` and 0% max_increase test. + // Check zero values play nice: 0 `min_delay` and 0% max_increase test. // // Set commission change rate to 0% per 0 blocks. assert_ok!(Pools::set_commission_change_rate( From 101363486d30a6964697408b0459b74a1073cd1c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:14 +0700 Subject: [PATCH 188/221] Revert "test" This reverts commit 3abc71b5a03c50ae7a1edd73f2ccab9e0101cadc. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 3146a7b976150..fbf81bf59e20c 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6042,7 +6042,7 @@ mod commission { #[test] fn set_commission_change_rate_zero_value_works() { ExtBuilder::default().build_and_execute(|| { - // Check zero values play nice: 0 `min_delay` and 0% max_increase test. + // Check zero values play nice. 0 `min_delay` and 0% max_increase test. // // Set commission change rate to 0% per 0 blocks. assert_ok!(Pools::set_commission_change_rate( From de0edd39d3fb401e5763c27fee1c3f5341badfb8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:24 +0700 Subject: [PATCH 189/221] Revert "one more test" This reverts commit 6959bbb745f766c3a7637504d806e746733fb8cd. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index fbf81bf59e20c..a5675351cb2d4 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6044,7 +6044,7 @@ mod commission { ExtBuilder::default().build_and_execute(|| { // Check zero values play nice. 0 `min_delay` and 0% max_increase test. // - // Set commission change rate to 0% per 0 blocks. + // set commission change rate to 0% per 0 blocks. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, From 180744d2b6aa0fce32c507099faa78efaa3592de Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:27 +0700 Subject: [PATCH 190/221] Revert "test" This reverts commit f0db5057d3f8f4e722e27c856b806def23bd9813. --- frame/nomination-pools/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a5675351cb2d4..a45f89662bc24 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6085,7 +6085,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // Top up commission payee account to existential deposit + // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 33%, with a payee set to `2` @@ -6106,7 +6106,7 @@ mod commission { ] ); - // Pool earns 10 points + // The pool earns 10 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); assert_ok!(Pools::do_reward_payout( From 36d77dfc05f113b2bcf4e596a8e3b7acc9d1e19c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:29 +0700 Subject: [PATCH 191/221] Revert "test" This reverts commit f17e7145707daee58a68710d22c5f7fc8cf26457. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index a45f89662bc24..6f0f0de97159b 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6122,7 +6122,7 @@ mod commission { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 7, commission: 3 },] ); - // Pool earns 17 points + // The pool earns 17 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 17)); assert_ok!(Pools::do_reward_payout( &10, From 49e0aeb405c3f8a90edc3de30d08d62f3928e0f2 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:31 +0700 Subject: [PATCH 192/221] Revert "test" This reverts commit 7059ec409c63de0609cb790fda2724061e45cbfa. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 6f0f0de97159b..5cc4c40682e7d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6137,7 +6137,7 @@ mod commission { vec![Event::PaidOut { member: 10, pool_id: 1, payout: 11, commission: 6 },] ); - // Pool earns 50 points + // The pool earns 50 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 50)); assert_ok!(Pools::do_reward_payout( &10, From a10f6e2edf37d13d4755d3c3ce48788e3c54006d Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:33 +0700 Subject: [PATCH 193/221] Revert "test" This reverts commit 13857d51f444a6963e63ce71a3d8074312693b89. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 5cc4c40682e7d..f5bf2871b65c5 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6178,7 +6178,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit. + // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set commission of pool 1 to 100%, with a payee of `2` From c3db72ed6f72ca0ab56248ae570a8dbaf2004a93 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:34 +0700 Subject: [PATCH 194/221] Revert "test" This reverts commit 8b51bb977aaa12a6541ea45c54c1b42cc96c13fe. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index f5bf2871b65c5..dd83c56f30462 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6181,7 +6181,7 @@ mod commission { // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); - // Set commission of pool 1 to 100%, with a payee of `2` + // Set commission of pool 1 to 100%, with a payee set to `2` assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, From 4a437aab54f2e45a984c37d54de67d1e61d56a26 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:36 +0700 Subject: [PATCH 195/221] Revert "test" This reverts commit 69d55ea9fb17a50aeda648e97372e9d246e3eaa3. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index dd83c56f30462..1a1a45b068b0a 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6181,7 +6181,7 @@ mod commission { // top up commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); - // Set commission of pool 1 to 100%, with a payee set to `2` + // Set a commission pool 1 to 100%, with a payee set to `2` assert_ok!(Pools::set_commission( RuntimeOrigin::signed(900), bonded_pool.id, From 7dec077d139c657055aaa2a9a3d1750ce4e07502 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:38 +0700 Subject: [PATCH 196/221] Revert "test" This reverts commit 8f2b9fd0b73d0638503058aa6d8ed3b94aac511b. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 1a1a45b068b0a..9240e791998fd 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6229,7 +6229,7 @@ mod commission { #[test] fn global_max_prevents_100_percent_commission_payout() { ExtBuilder::default().build_and_execute(|| { - // Note: GlobalMaxCommission is set to 90%. + // Note: GlobalMaxCommission is set at 90%. let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); From 73d9bf8d7e69cae6e50123f12b7fb9121cbfa334 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:39 +0700 Subject: [PATCH 197/221] Revert "test" This reverts commit c0b798c13ce942dc2ddc55d003303465805ea65b. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 9240e791998fd..28a827ee747e2 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6210,7 +6210,7 @@ mod commission { .maybe_commission_and_payee(&10); assert_eq!(*maybe_commission, Some((10_u128, 2_u128))); - // Execute the payout + // execute the payout assert_ok!(Pools::do_reward_payout( &10, &mut member, From f5005d67ee7611dba31af74833781a1655d061db Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:41 +0700 Subject: [PATCH 198/221] Revert "test" This reverts commit 55fd2effa6f1a3b4903816729c525147527fc984. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index 28a827ee747e2..b86bfe074c1cc 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6200,7 +6200,7 @@ mod commission { ] ); - // Pool earns 10 points + // The pool earns 10 points assert_ok!(Balances::mutate_account(&default_reward_account(), |a| a.free += 10)); // Ensure the commission equals the total amount of points. From 0bf345c70a56c47f5a1b83fdd73ba754fdc7039c Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 28 Dec 2022 17:58:43 +0700 Subject: [PATCH 199/221] Revert "test" This reverts commit 4072074c6829b75316a0237ed19c6afeb1c35e5e. --- frame/nomination-pools/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index b86bfe074c1cc..fb2c62702cb1d 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -6234,7 +6234,7 @@ mod commission { let (mut member, bonded_pool, mut reward_pool) = Pools::get_member_with_pools(&10).unwrap(); - // top up commission payee account to existential deposit + // top up the commission payee account to existential deposit let _ = Balances::deposit_creating(&2, 5); // Set a commission pool 1 to 100%, with a payee set to `2` From 3c4c61cf91e4727ab5c6a4c7fb071c6576eb2f92 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Mon, 26 Dec 2022 16:48:40 +0100 Subject: [PATCH 200/221] Allow StakingAdmin to set `min_commission` (#13018) * staking admin can set min commission * ".git/.scripts/bench-bot.sh" pallet dev pallet_staking * fmt * fix for pr comments Co-authored-by: command-bot <> --- bin/node/runtime/src/lib.rs | 2 +- frame/babe/src/mock.rs | 2 +- frame/fast-unstake/src/mock.rs | 2 +- frame/grandpa/src/mock.rs | 2 +- .../nomination-pools/benchmarking/src/mock.rs | 2 +- .../nomination-pools/test-staking/src/mock.rs | 2 +- frame/offences/benchmarking/src/mock.rs | 2 +- frame/root-offences/src/mock.rs | 2 +- frame/session/benchmarking/src/mock.rs | 2 +- frame/staking/src/benchmarking.rs | 7 + frame/staking/src/mock.rs | 14 +- frame/staking/src/pallet/mod.rs | 23 +- frame/staking/src/tests.rs | 40 ++ frame/staking/src/weights.rs | 385 +++++++++--------- 14 files changed, 286 insertions(+), 201 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3e2701b2278c8..0c946fa180f20 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -567,7 +567,7 @@ impl pallet_staking::Config for Runtime { type BondingDuration = BondingDuration; type SlashDeferDuration = SlashDeferDuration; /// A super-majority of the council can cancel the slash. - type SlashCancelOrigin = EitherOfDiverse< + type AdminOrigin = EitherOfDiverse< EnsureRoot, pallet_collective::EnsureProportionAtLeast, >; diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 204de8aae172e..dbeb588c1830e 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -195,7 +195,7 @@ impl pallet_staking::Config for Test { type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; type SlashDeferDuration = SlashDeferDuration; - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type SessionInterface = Self; type UnixTime = pallet_timestamp::Pallet; type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/fast-unstake/src/mock.rs b/frame/fast-unstake/src/mock.rs index b67dcf581ed97..3f974e5e1a9d6 100644 --- a/frame/fast-unstake/src/mock.rs +++ b/frame/fast-unstake/src/mock.rs @@ -140,7 +140,7 @@ impl pallet_staking::Config for Runtime { type Reward = (); type SessionsPerEra = (); type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = BondingDuration; type SessionInterface = (); type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 1a97b1345fe5d..131f6cafcd179 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -199,7 +199,7 @@ impl pallet_staking::Config for Test { type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type SessionInterface = Self; type UnixTime = pallet_timestamp::Pallet; type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/nomination-pools/benchmarking/src/mock.rs b/frame/nomination-pools/benchmarking/src/mock.rs index 2bafd3c0fb99c..db097004ef73b 100644 --- a/frame/nomination-pools/benchmarking/src/mock.rs +++ b/frame/nomination-pools/benchmarking/src/mock.rs @@ -102,7 +102,7 @@ impl pallet_staking::Config for Runtime { type Reward = (); type SessionsPerEra = (); type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = ConstU32<3>; type SessionInterface = (); type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/nomination-pools/test-staking/src/mock.rs b/frame/nomination-pools/test-staking/src/mock.rs index e1b9cc981463a..fb3aef130a77d 100644 --- a/frame/nomination-pools/test-staking/src/mock.rs +++ b/frame/nomination-pools/test-staking/src/mock.rs @@ -116,7 +116,7 @@ impl pallet_staking::Config for Runtime { type Reward = (); type SessionsPerEra = (); type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = BondingDuration; type SessionInterface = (); type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index de3a4eca6308d..223f551a6e5c1 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -173,7 +173,7 @@ impl pallet_staking::Config for Test { type Reward = (); type SessionsPerEra = (); type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = (); type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/root-offences/src/mock.rs b/frame/root-offences/src/mock.rs index 65bfcad4b26fc..e1d9ee14ee2fc 100644 --- a/frame/root-offences/src/mock.rs +++ b/frame/root-offences/src/mock.rs @@ -184,7 +184,7 @@ impl pallet_staking::Config for Test { type Reward = (); type SessionsPerEra = SessionsPerEra; type SlashDeferDuration = SlashDeferDuration; - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = BondingDuration; type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 2db7eb385111c..0699640bc092a 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -167,7 +167,7 @@ impl pallet_staking::Config for Test { type Reward = (); type SessionsPerEra = (); type SlashDeferDuration = (); - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = frame_system::EnsureRoot; type BondingDuration = (); type SessionInterface = Self; type EraPayout = pallet_staking::ConvertCurve; diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 81fa0f9d81dbf..b3d32b26ec1f7 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -922,6 +922,13 @@ benchmarks! { ); } + set_min_commission { + let min_commission = Perbill::max_value(); + }: _(RawOrigin::Root, min_commission) + verify { + assert_eq!(MinCommission::::get(), Perbill::from_percent(100)); + } + impl_benchmark_test_suite!( Staking, crate::mock::ExtBuilder::default().has_stakers(true), diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index d3affda05277a..843e452125a6e 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -20,13 +20,14 @@ use crate::{self as pallet_staking, *}; use frame_election_provider_support::{onchain, SequentialPhragmen, VoteWeight}; use frame_support::{ - assert_ok, parameter_types, + assert_ok, ord_parameter_types, parameter_types, traits::{ - ConstU32, ConstU64, Currency, FindAuthor, GenesisBuild, Get, Hooks, Imbalance, - OnUnbalanced, OneSessionHandler, + ConstU32, ConstU64, Currency, EitherOfDiverse, FindAuthor, GenesisBuild, Get, Hooks, + Imbalance, OnUnbalanced, OneSessionHandler, }, weights::constants::RocksDbWeight, }; +use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_core::H256; use sp_io; use sp_runtime::{ @@ -292,7 +293,7 @@ impl crate::pallet::pallet::Config for Test { type Reward = MockReward; type SessionsPerEra = SessionsPerEra; type SlashDeferDuration = SlashDeferDuration; - type SlashCancelOrigin = frame_system::EnsureRoot; + type AdminOrigin = EnsureOneOrRoot; type BondingDuration = BondingDuration; type SessionInterface = Self; type EraPayout = ConvertCurve; @@ -797,6 +798,11 @@ pub(crate) fn staking_events() -> Vec> { parameter_types! { static StakingEventsIndex: usize = 0; } +ord_parameter_types! { + pub const One: u64 = 1; +} + +type EnsureOneOrRoot = EitherOfDiverse, EnsureSignedBy>; pub(crate) fn staking_events_since_last_call() -> Vec> { let all: Vec<_> = System::events() diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index f04d13d05f468..8e8a8d9c7f600 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -183,8 +183,10 @@ pub mod pallet { #[pallet::constant] type SlashDeferDuration: Get; - /// The origin which can cancel a deferred slash. Root can always do this. - type SlashCancelOrigin: EnsureOrigin; + /// The origin which can manage less critical staking parameters that does not require root. + /// + /// Supported actions: (1) cancel deferred slash, (2) set minimum commission. + type AdminOrigin: EnsureOrigin; /// Interface for interacting with a session pallet. type SessionInterface: SessionInterface; @@ -1452,7 +1454,7 @@ pub mod pallet { /// Cancel enactment of a deferred slash. /// - /// Can be called by the `T::SlashCancelOrigin`. + /// Can be called by the `T::AdminOrigin`. /// /// Parameters: era and indices of the slashes for that era to kill. #[pallet::call_index(17)] @@ -1462,7 +1464,7 @@ pub mod pallet { era: EraIndex, slash_indices: Vec, ) -> DispatchResult { - T::SlashCancelOrigin::ensure_origin(origin)?; + T::AdminOrigin::ensure_origin(origin)?; ensure!(!slash_indices.is_empty(), Error::::EmptyTargets); ensure!(is_sorted_and_unique(&slash_indices), Error::::NotSortedAndUnique); @@ -1683,7 +1685,6 @@ pub mod pallet { config_op_exp!(MinCommission, min_commission); Ok(()) } - /// Declare a `controller` to stop participating as either a validator or nominator. /// /// Effects will be felt at the beginning of the next era. @@ -1792,6 +1793,18 @@ pub mod pallet { })?; Ok(()) } + + /// Sets the minimum amount of commission that each validators must maintain. + /// + /// This call has lower privilege requirements than `set_staking_config` and can be called + /// by the `T::AdminOrigin`. Root can always call this. + #[pallet::call_index(25)] + #[pallet::weight(T::WeightInfo::set_min_commission())] + pub fn set_min_commission(origin: OriginFor, new: Perbill) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + MinCommission::::put(new); + Ok(()) + } } } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 5c2766deee307..46c3c97441938 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5726,6 +5726,46 @@ fn scale_validator_count_errors() { }) } +#[test] +fn set_min_commission_works_with_admin_origin() { + ExtBuilder::default().build_and_execute(|| { + // no minimum commission set initially + assert_eq!(MinCommission::::get(), Zero::zero()); + + // root can set min commission + assert_ok!(Staking::set_min_commission(RuntimeOrigin::root(), Perbill::from_percent(10))); + + assert_eq!(MinCommission::::get(), Perbill::from_percent(10)); + + // Non privileged origin can not set min_commission + assert_noop!( + Staking::set_min_commission(RuntimeOrigin::signed(2), Perbill::from_percent(15)), + BadOrigin + ); + + // Admin Origin can set min commission + assert_ok!(Staking::set_min_commission( + RuntimeOrigin::signed(1), + Perbill::from_percent(15), + )); + + // setting commission below min_commission fails + assert_noop!( + Staking::validate( + RuntimeOrigin::signed(10), + ValidatorPrefs { commission: Perbill::from_percent(14), blocked: false } + ), + Error::::CommissionTooLow + ); + + // setting commission >= min_commission works + assert_ok!(Staking::validate( + RuntimeOrigin::signed(10), + ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false } + )); + }) +} + mod staking_interface { use frame_support::storage::with_storage_layer; use sp_staking::StakingInterface; diff --git a/frame/staking/src/weights.rs b/frame/staking/src/weights.rs index aebb8eeb9b06e..9c283f5a065e3 100644 --- a/frame/staking/src/weights.rs +++ b/frame/staking/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-12-14, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-12-25, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -77,6 +77,7 @@ pub trait WeightInfo { fn set_staking_configs_all_remove() -> Weight; fn chill_other() -> Weight; fn force_apply_min_commission() -> Weight; + fn set_min_commission() -> Weight; } /// Weights for pallet_staking using the Substrate node and recommended hardware. @@ -88,8 +89,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn bond() -> Weight { - // Minimum execution time: 54_402 nanoseconds. - Weight::from_ref_time(55_096_000) + // Minimum execution time: 54_884 nanoseconds. + Weight::from_ref_time(55_487_000) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -99,8 +100,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra() -> Weight { - // Minimum execution time: 94_407 nanoseconds. - Weight::from_ref_time(95_209_000) + // Minimum execution time: 95_115 nanoseconds. + Weight::from_ref_time(96_213_000) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -114,8 +115,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Bonded (r:1 w:0) // Storage: VoterList ListBags (r:2 w:2) fn unbond() -> Weight { - // Minimum execution time: 101_046 nanoseconds. - Weight::from_ref_time(101_504_000) + // Minimum execution time: 102_031 nanoseconds. + Weight::from_ref_time(102_842_000) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(8)) } @@ -125,10 +126,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 45_452 nanoseconds. - Weight::from_ref_time(47_031_537) - // Standard Error: 491 - .saturating_add(Weight::from_ref_time(67_148).saturating_mul(s.into())) + // Minimum execution time: 46_569 nanoseconds. + Weight::from_ref_time(48_034_493) + // Standard Error: 654 + .saturating_add(Weight::from_ref_time(63_628).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -148,10 +149,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking SpanSlash (r:0 w:2) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 88_067 nanoseconds. - Weight::from_ref_time(93_309_587) - // Standard Error: 4_762 - .saturating_add(Weight::from_ref_time(1_114_938).saturating_mul(s.into())) + // Minimum execution time: 90_154 nanoseconds. + Weight::from_ref_time(95_725_631) + // Standard Error: 2_491 + .saturating_add(Weight::from_ref_time(1_110_795).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(13)) .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -168,8 +169,8 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList CounterForListNodes (r:1 w:1) // Storage: Staking CounterForValidators (r:1 w:1) fn validate() -> Weight { - // Minimum execution time: 67_308 nanoseconds. - Weight::from_ref_time(68_266_000) + // Minimum execution time: 67_978 nanoseconds. + Weight::from_ref_time(69_153_000) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -177,10 +178,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Nominators (r:1 w:1) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { - // Minimum execution time: 40_913 nanoseconds. - Weight::from_ref_time(48_140_584) - // Standard Error: 13_396 - .saturating_add(Weight::from_ref_time(6_862_893).saturating_mul(k.into())) + // Minimum execution time: 45_328 nanoseconds. + Weight::from_ref_time(47_719_103) + // Standard Error: 14_458 + .saturating_add(Weight::from_ref_time(6_999_252).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -198,10 +199,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 73_490 nanoseconds. - Weight::from_ref_time(72_520_864) - // Standard Error: 7_090 - .saturating_add(Weight::from_ref_time(2_800_566).saturating_mul(n.into())) + // Minimum execution time: 74_650 nanoseconds. + Weight::from_ref_time(74_350_075) + // Standard Error: 10_527 + .saturating_add(Weight::from_ref_time(2_878_737).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(6)) @@ -214,58 +215,58 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 66_293 nanoseconds. - Weight::from_ref_time(66_946_000) + // Minimum execution time: 67_790 nanoseconds. + Weight::from_ref_time(68_738_000) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: Staking Ledger (r:1 w:0) // Storage: Staking Payee (r:0 w:1) fn set_payee() -> Weight { - // Minimum execution time: 18_134 nanoseconds. - Weight::from_ref_time(18_497_000) + // Minimum execution time: 19_237 nanoseconds. + Weight::from_ref_time(19_534_000) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking Bonded (r:1 w:1) // Storage: Staking Ledger (r:2 w:2) fn set_controller() -> Weight { - // Minimum execution time: 26_728 nanoseconds. - Weight::from_ref_time(27_154_000) + // Minimum execution time: 27_288 nanoseconds. + Weight::from_ref_time(27_667_000) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Staking ValidatorCount (r:0 w:1) fn set_validator_count() -> Weight { - // Minimum execution time: 4_877 nanoseconds. - Weight::from_ref_time(5_028_000) + // Minimum execution time: 5_155 nanoseconds. + Weight::from_ref_time(5_464_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_no_eras() -> Weight { - // Minimum execution time: 5_000 nanoseconds. - Weight::from_ref_time(5_290_000) + // Minimum execution time: 5_405 nanoseconds. + Weight::from_ref_time(5_670_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_new_era() -> Weight { - // Minimum execution time: 5_093 nanoseconds. - Weight::from_ref_time(5_378_000) + // Minimum execution time: 5_459 nanoseconds. + Weight::from_ref_time(5_616_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_new_era_always() -> Weight { - // Minimum execution time: 5_144 nanoseconds. - Weight::from_ref_time(5_454_000) + // Minimum execution time: 5_476 nanoseconds. + Weight::from_ref_time(5_692_000) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking Invulnerables (r:0 w:1) /// The range of component `v` is `[0, 1000]`. fn set_invulnerables(v: u32, ) -> Weight { - // Minimum execution time: 5_190 nanoseconds. - Weight::from_ref_time(5_960_962) - // Standard Error: 41 - .saturating_add(Weight::from_ref_time(10_329).saturating_mul(v.into())) + // Minimum execution time: 5_544 nanoseconds. + Weight::from_ref_time(6_513_190) + // Standard Error: 76 + .saturating_add(Weight::from_ref_time(9_975).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking Bonded (r:1 w:1) @@ -283,10 +284,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking SpanSlash (r:0 w:2) /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { - // Minimum execution time: 80_516 nanoseconds. - Weight::from_ref_time(86_317_884) - // Standard Error: 2_212 - .saturating_add(Weight::from_ref_time(1_103_962).saturating_mul(s.into())) + // Minimum execution time: 82_414 nanoseconds. + Weight::from_ref_time(88_511_246) + // Standard Error: 2_622 + .saturating_add(Weight::from_ref_time(1_131_814).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -294,10 +295,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking UnappliedSlashes (r:1 w:1) /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { - // Minimum execution time: 91_795 nanoseconds. - Weight::from_ref_time(904_524_900) - // Standard Error: 59_193 - .saturating_add(Weight::from_ref_time(4_944_680).saturating_mul(s.into())) + // Minimum execution time: 94_197 nanoseconds. + Weight::from_ref_time(903_418_326) + // Standard Error: 59_354 + .saturating_add(Weight::from_ref_time(4_948_354).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -312,10 +313,10 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Minimum execution time: 127_774 nanoseconds. - Weight::from_ref_time(178_857_156) - // Standard Error: 15_229 - .saturating_add(Weight::from_ref_time(22_112_174).saturating_mul(n.into())) + // Minimum execution time: 133_065 nanoseconds. + Weight::from_ref_time(197_555_906) + // Standard Error: 19_561 + .saturating_add(Weight::from_ref_time(22_683_426).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -333,10 +334,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Balances Locks (r:1 w:1) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Minimum execution time: 161_910 nanoseconds. - Weight::from_ref_time(217_635_072) - // Standard Error: 30_726 - .saturating_add(Weight::from_ref_time(31_244_329).saturating_mul(n.into())) + // Minimum execution time: 164_719 nanoseconds. + Weight::from_ref_time(226_304_276) + // Standard Error: 31_675 + .saturating_add(Weight::from_ref_time(32_622_427).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(3)) @@ -350,10 +351,10 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:2 w:2) /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { - // Minimum execution time: 92_986 nanoseconds. - Weight::from_ref_time(94_880_481) - // Standard Error: 2_007 - .saturating_add(Weight::from_ref_time(31_421).saturating_mul(l.into())) + // Minimum execution time: 95_631 nanoseconds. + Weight::from_ref_time(96_861_556) + // Standard Error: 2_114 + .saturating_add(Weight::from_ref_time(37_543).saturating_mul(l.into())) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(8)) } @@ -372,10 +373,10 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking SpanSlash (r:0 w:1) /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { - // Minimum execution time: 92_750 nanoseconds. - Weight::from_ref_time(95_115_568) - // Standard Error: 2_037 - .saturating_add(Weight::from_ref_time(1_086_488).saturating_mul(s.into())) + // Minimum execution time: 95_251 nanoseconds. + Weight::from_ref_time(97_818_954) + // Standard Error: 2_356 + .saturating_add(Weight::from_ref_time(1_104_695).saturating_mul(s.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(12)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -396,19 +397,20 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking ErasStakers (r:0 w:1) // Storage: Staking ErasTotalStake (r:0 w:1) // Storage: Staking ErasStartSessionIndex (r:0 w:1) + // Storage: Staking MinimumActiveStake (r:0 w:1) /// The range of component `v` is `[1, 10]`. /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 506_543 nanoseconds. - Weight::from_ref_time(507_261_000) - // Standard Error: 1_766_631 - .saturating_add(Weight::from_ref_time(59_139_153).saturating_mul(v.into())) - // Standard Error: 176_035 - .saturating_add(Weight::from_ref_time(13_512_781).saturating_mul(n.into())) + // Minimum execution time: 512_923 nanoseconds. + Weight::from_ref_time(514_740_000) + // Standard Error: 1_790_238 + .saturating_add(Weight::from_ref_time(59_320_539).saturating_mul(v.into())) + // Standard Error: 178_387 + .saturating_add(Weight::from_ref_time(13_902_705).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(206)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(v.into()))) } // Storage: VoterList CounterForListNodes (r:1 w:0) @@ -418,27 +420,29 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking Validators (r:500 w:0) // Storage: Staking Bonded (r:1500 w:0) // Storage: Staking Ledger (r:1500 w:0) + // Storage: Staking MinimumActiveStake (r:0 w:1) /// The range of component `v` is `[500, 1000]`. /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 24_155_382 nanoseconds. - Weight::from_ref_time(24_252_568_000) - // Standard Error: 319_250 - .saturating_add(Weight::from_ref_time(3_596_056).saturating_mul(v.into())) - // Standard Error: 319_250 - .saturating_add(Weight::from_ref_time(2_852_023).saturating_mul(n.into())) + // Minimum execution time: 24_913_316 nanoseconds. + Weight::from_ref_time(25_053_596_000) + // Standard Error: 324_610 + .saturating_add(Weight::from_ref_time(3_454_859).saturating_mul(v.into())) + // Standard Error: 324_610 + .saturating_add(Weight::from_ref_time(3_020_267).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(201)) .saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(1)) } // Storage: Staking CounterForValidators (r:1 w:0) // Storage: Staking Validators (r:501 w:0) /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { - // Minimum execution time: 4_741_111 nanoseconds. - Weight::from_ref_time(113_360_179) - // Standard Error: 25_375 - .saturating_add(Weight::from_ref_time(9_494_142).saturating_mul(v.into())) + // Minimum execution time: 4_916_401 nanoseconds. + Weight::from_ref_time(81_160_966) + // Standard Error: 23_829 + .saturating_add(Weight::from_ref_time(9_883_413).saturating_mul(v.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into()))) } @@ -449,8 +453,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking MaxNominatorsCount (r:0 w:1) // Storage: Staking MinNominatorBond (r:0 w:1) fn set_staking_configs_all_set() -> Weight { - // Minimum execution time: 11_074 nanoseconds. - Weight::from_ref_time(11_312_000) + // Minimum execution time: 10_937 nanoseconds. + Weight::from_ref_time(11_324_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: Staking MinCommission (r:0 w:1) @@ -460,8 +464,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Staking MaxNominatorsCount (r:0 w:1) // Storage: Staking MinNominatorBond (r:0 w:1) fn set_staking_configs_all_remove() -> Weight { - // Minimum execution time: 9_795 nanoseconds. - Weight::from_ref_time(10_116_000) + // Minimum execution time: 9_424 nanoseconds. + Weight::from_ref_time(10_021_000) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: Staking Ledger (r:1 w:0) @@ -475,19 +479,25 @@ impl WeightInfo for SubstrateWeight { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill_other() -> Weight { - // Minimum execution time: 82_914 nanoseconds. - Weight::from_ref_time(83_848_000) + // Minimum execution time: 84_495 nanoseconds. + Weight::from_ref_time(85_559_000) .saturating_add(T::DbWeight::get().reads(11)) .saturating_add(T::DbWeight::get().writes(6)) } // Storage: Staking MinCommission (r:1 w:0) // Storage: Staking Validators (r:1 w:1) fn force_apply_min_commission() -> Weight { - // Minimum execution time: 20_317 nanoseconds. - Weight::from_ref_time(20_639_000) + // Minimum execution time: 20_385 nanoseconds. + Weight::from_ref_time(20_824_000) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } + // Storage: Staking MinCommission (r:0 w:1) + fn set_min_commission() -> Weight { + // Minimum execution time: 6_995 nanoseconds. + Weight::from_ref_time(7_213_000) + .saturating_add(T::DbWeight::get().writes(1)) + } } // For backwards compatibility and tests @@ -498,8 +508,8 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) // Storage: Staking Payee (r:0 w:1) fn bond() -> Weight { - // Minimum execution time: 54_402 nanoseconds. - Weight::from_ref_time(55_096_000) + // Minimum execution time: 54_884 nanoseconds. + Weight::from_ref_time(55_487_000) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(4)) } @@ -509,8 +519,8 @@ impl WeightInfo for () { // Storage: VoterList ListNodes (r:3 w:3) // Storage: VoterList ListBags (r:2 w:2) fn bond_extra() -> Weight { - // Minimum execution time: 94_407 nanoseconds. - Weight::from_ref_time(95_209_000) + // Minimum execution time: 95_115 nanoseconds. + Weight::from_ref_time(96_213_000) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(7)) } @@ -524,8 +534,8 @@ impl WeightInfo for () { // Storage: Staking Bonded (r:1 w:0) // Storage: VoterList ListBags (r:2 w:2) fn unbond() -> Weight { - // Minimum execution time: 101_046 nanoseconds. - Weight::from_ref_time(101_504_000) + // Minimum execution time: 102_031 nanoseconds. + Weight::from_ref_time(102_842_000) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().writes(8)) } @@ -535,10 +545,10 @@ impl WeightInfo for () { // Storage: System Account (r:1 w:1) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_update(s: u32, ) -> Weight { - // Minimum execution time: 45_452 nanoseconds. - Weight::from_ref_time(47_031_537) - // Standard Error: 491 - .saturating_add(Weight::from_ref_time(67_148).saturating_mul(s.into())) + // Minimum execution time: 46_569 nanoseconds. + Weight::from_ref_time(48_034_493) + // Standard Error: 654 + .saturating_add(Weight::from_ref_time(63_628).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(4)) .saturating_add(RocksDbWeight::get().writes(3)) } @@ -558,10 +568,10 @@ impl WeightInfo for () { // Storage: Staking SpanSlash (r:0 w:2) /// The range of component `s` is `[0, 100]`. fn withdraw_unbonded_kill(s: u32, ) -> Weight { - // Minimum execution time: 88_067 nanoseconds. - Weight::from_ref_time(93_309_587) - // Standard Error: 4_762 - .saturating_add(Weight::from_ref_time(1_114_938).saturating_mul(s.into())) + // Minimum execution time: 90_154 nanoseconds. + Weight::from_ref_time(95_725_631) + // Standard Error: 2_491 + .saturating_add(Weight::from_ref_time(1_110_795).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(13)) .saturating_add(RocksDbWeight::get().writes(12)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -578,8 +588,8 @@ impl WeightInfo for () { // Storage: VoterList CounterForListNodes (r:1 w:1) // Storage: Staking CounterForValidators (r:1 w:1) fn validate() -> Weight { - // Minimum execution time: 67_308 nanoseconds. - Weight::from_ref_time(68_266_000) + // Minimum execution time: 67_978 nanoseconds. + Weight::from_ref_time(69_153_000) .saturating_add(RocksDbWeight::get().reads(11)) .saturating_add(RocksDbWeight::get().writes(5)) } @@ -587,10 +597,10 @@ impl WeightInfo for () { // Storage: Staking Nominators (r:1 w:1) /// The range of component `k` is `[1, 128]`. fn kick(k: u32, ) -> Weight { - // Minimum execution time: 40_913 nanoseconds. - Weight::from_ref_time(48_140_584) - // Standard Error: 13_396 - .saturating_add(Weight::from_ref_time(6_862_893).saturating_mul(k.into())) + // Minimum execution time: 45_328 nanoseconds. + Weight::from_ref_time(47_719_103) + // Standard Error: 14_458 + .saturating_add(Weight::from_ref_time(6_999_252).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) @@ -608,10 +618,10 @@ impl WeightInfo for () { // Storage: Staking CounterForNominators (r:1 w:1) /// The range of component `n` is `[1, 16]`. fn nominate(n: u32, ) -> Weight { - // Minimum execution time: 73_490 nanoseconds. - Weight::from_ref_time(72_520_864) - // Standard Error: 7_090 - .saturating_add(Weight::from_ref_time(2_800_566).saturating_mul(n.into())) + // Minimum execution time: 74_650 nanoseconds. + Weight::from_ref_time(74_350_075) + // Standard Error: 10_527 + .saturating_add(Weight::from_ref_time(2_878_737).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(6)) @@ -624,58 +634,58 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill() -> Weight { - // Minimum execution time: 66_293 nanoseconds. - Weight::from_ref_time(66_946_000) + // Minimum execution time: 67_790 nanoseconds. + Weight::from_ref_time(68_738_000) .saturating_add(RocksDbWeight::get().reads(8)) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: Staking Ledger (r:1 w:0) // Storage: Staking Payee (r:0 w:1) fn set_payee() -> Weight { - // Minimum execution time: 18_134 nanoseconds. - Weight::from_ref_time(18_497_000) + // Minimum execution time: 19_237 nanoseconds. + Weight::from_ref_time(19_534_000) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking Bonded (r:1 w:1) // Storage: Staking Ledger (r:2 w:2) fn set_controller() -> Weight { - // Minimum execution time: 26_728 nanoseconds. - Weight::from_ref_time(27_154_000) + // Minimum execution time: 27_288 nanoseconds. + Weight::from_ref_time(27_667_000) .saturating_add(RocksDbWeight::get().reads(3)) .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Staking ValidatorCount (r:0 w:1) fn set_validator_count() -> Weight { - // Minimum execution time: 4_877 nanoseconds. - Weight::from_ref_time(5_028_000) + // Minimum execution time: 5_155 nanoseconds. + Weight::from_ref_time(5_464_000) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_no_eras() -> Weight { - // Minimum execution time: 5_000 nanoseconds. - Weight::from_ref_time(5_290_000) + // Minimum execution time: 5_405 nanoseconds. + Weight::from_ref_time(5_670_000) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_new_era() -> Weight { - // Minimum execution time: 5_093 nanoseconds. - Weight::from_ref_time(5_378_000) + // Minimum execution time: 5_459 nanoseconds. + Weight::from_ref_time(5_616_000) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking ForceEra (r:0 w:1) fn force_new_era_always() -> Weight { - // Minimum execution time: 5_144 nanoseconds. - Weight::from_ref_time(5_454_000) + // Minimum execution time: 5_476 nanoseconds. + Weight::from_ref_time(5_692_000) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking Invulnerables (r:0 w:1) /// The range of component `v` is `[0, 1000]`. fn set_invulnerables(v: u32, ) -> Weight { - // Minimum execution time: 5_190 nanoseconds. - Weight::from_ref_time(5_960_962) - // Standard Error: 41 - .saturating_add(Weight::from_ref_time(10_329).saturating_mul(v.into())) + // Minimum execution time: 5_544 nanoseconds. + Weight::from_ref_time(6_513_190) + // Standard Error: 76 + .saturating_add(Weight::from_ref_time(9_975).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking Bonded (r:1 w:1) @@ -693,10 +703,10 @@ impl WeightInfo for () { // Storage: Staking SpanSlash (r:0 w:2) /// The range of component `s` is `[0, 100]`. fn force_unstake(s: u32, ) -> Weight { - // Minimum execution time: 80_516 nanoseconds. - Weight::from_ref_time(86_317_884) - // Standard Error: 2_212 - .saturating_add(Weight::from_ref_time(1_103_962).saturating_mul(s.into())) + // Minimum execution time: 82_414 nanoseconds. + Weight::from_ref_time(88_511_246) + // Standard Error: 2_622 + .saturating_add(Weight::from_ref_time(1_131_814).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(11)) .saturating_add(RocksDbWeight::get().writes(12)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -704,10 +714,10 @@ impl WeightInfo for () { // Storage: Staking UnappliedSlashes (r:1 w:1) /// The range of component `s` is `[1, 1000]`. fn cancel_deferred_slash(s: u32, ) -> Weight { - // Minimum execution time: 91_795 nanoseconds. - Weight::from_ref_time(904_524_900) - // Standard Error: 59_193 - .saturating_add(Weight::from_ref_time(4_944_680).saturating_mul(s.into())) + // Minimum execution time: 94_197 nanoseconds. + Weight::from_ref_time(903_418_326) + // Standard Error: 59_354 + .saturating_add(Weight::from_ref_time(4_948_354).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(1)) .saturating_add(RocksDbWeight::get().writes(1)) } @@ -722,10 +732,10 @@ impl WeightInfo for () { // Storage: System Account (r:1 w:1) /// The range of component `n` is `[0, 256]`. fn payout_stakers_dead_controller(n: u32, ) -> Weight { - // Minimum execution time: 127_774 nanoseconds. - Weight::from_ref_time(178_857_156) - // Standard Error: 15_229 - .saturating_add(Weight::from_ref_time(22_112_174).saturating_mul(n.into())) + // Minimum execution time: 133_065 nanoseconds. + Weight::from_ref_time(197_555_906) + // Standard Error: 19_561 + .saturating_add(Weight::from_ref_time(22_683_426).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().reads((3_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(2)) @@ -743,10 +753,10 @@ impl WeightInfo for () { // Storage: Balances Locks (r:1 w:1) /// The range of component `n` is `[0, 256]`. fn payout_stakers_alive_staked(n: u32, ) -> Weight { - // Minimum execution time: 161_910 nanoseconds. - Weight::from_ref_time(217_635_072) - // Standard Error: 30_726 - .saturating_add(Weight::from_ref_time(31_244_329).saturating_mul(n.into())) + // Minimum execution time: 164_719 nanoseconds. + Weight::from_ref_time(226_304_276) + // Standard Error: 31_675 + .saturating_add(Weight::from_ref_time(32_622_427).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(10)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(n.into()))) .saturating_add(RocksDbWeight::get().writes(3)) @@ -760,10 +770,10 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:2 w:2) /// The range of component `l` is `[1, 32]`. fn rebond(l: u32, ) -> Weight { - // Minimum execution time: 92_986 nanoseconds. - Weight::from_ref_time(94_880_481) - // Standard Error: 2_007 - .saturating_add(Weight::from_ref_time(31_421).saturating_mul(l.into())) + // Minimum execution time: 95_631 nanoseconds. + Weight::from_ref_time(96_861_556) + // Standard Error: 2_114 + .saturating_add(Weight::from_ref_time(37_543).saturating_mul(l.into())) .saturating_add(RocksDbWeight::get().reads(9)) .saturating_add(RocksDbWeight::get().writes(8)) } @@ -782,10 +792,10 @@ impl WeightInfo for () { // Storage: Staking SpanSlash (r:0 w:1) /// The range of component `s` is `[1, 100]`. fn reap_stash(s: u32, ) -> Weight { - // Minimum execution time: 92_750 nanoseconds. - Weight::from_ref_time(95_115_568) - // Standard Error: 2_037 - .saturating_add(Weight::from_ref_time(1_086_488).saturating_mul(s.into())) + // Minimum execution time: 95_251 nanoseconds. + Weight::from_ref_time(97_818_954) + // Standard Error: 2_356 + .saturating_add(Weight::from_ref_time(1_104_695).saturating_mul(s.into())) .saturating_add(RocksDbWeight::get().reads(12)) .saturating_add(RocksDbWeight::get().writes(12)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into()))) @@ -806,19 +816,20 @@ impl WeightInfo for () { // Storage: Staking ErasStakers (r:0 w:1) // Storage: Staking ErasTotalStake (r:0 w:1) // Storage: Staking ErasStartSessionIndex (r:0 w:1) + // Storage: Staking MinimumActiveStake (r:0 w:1) /// The range of component `v` is `[1, 10]`. /// The range of component `n` is `[0, 100]`. fn new_era(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 506_543 nanoseconds. - Weight::from_ref_time(507_261_000) - // Standard Error: 1_766_631 - .saturating_add(Weight::from_ref_time(59_139_153).saturating_mul(v.into())) - // Standard Error: 176_035 - .saturating_add(Weight::from_ref_time(13_512_781).saturating_mul(n.into())) + // Minimum execution time: 512_923 nanoseconds. + Weight::from_ref_time(514_740_000) + // Standard Error: 1_790_238 + .saturating_add(Weight::from_ref_time(59_320_539).saturating_mul(v.into())) + // Standard Error: 178_387 + .saturating_add(Weight::from_ref_time(13_902_705).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(206)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3)) + .saturating_add(RocksDbWeight::get().writes(4)) .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(v.into()))) } // Storage: VoterList CounterForListNodes (r:1 w:0) @@ -828,27 +839,29 @@ impl WeightInfo for () { // Storage: Staking Validators (r:500 w:0) // Storage: Staking Bonded (r:1500 w:0) // Storage: Staking Ledger (r:1500 w:0) + // Storage: Staking MinimumActiveStake (r:0 w:1) /// The range of component `v` is `[500, 1000]`. /// The range of component `n` is `[500, 1000]`. fn get_npos_voters(v: u32, n: u32, ) -> Weight { - // Minimum execution time: 24_155_382 nanoseconds. - Weight::from_ref_time(24_252_568_000) - // Standard Error: 319_250 - .saturating_add(Weight::from_ref_time(3_596_056).saturating_mul(v.into())) - // Standard Error: 319_250 - .saturating_add(Weight::from_ref_time(2_852_023).saturating_mul(n.into())) + // Minimum execution time: 24_913_316 nanoseconds. + Weight::from_ref_time(25_053_596_000) + // Standard Error: 324_610 + .saturating_add(Weight::from_ref_time(3_454_859).saturating_mul(v.into())) + // Standard Error: 324_610 + .saturating_add(Weight::from_ref_time(3_020_267).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(201)) .saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into()))) .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into()))) + .saturating_add(RocksDbWeight::get().writes(1)) } // Storage: Staking CounterForValidators (r:1 w:0) // Storage: Staking Validators (r:501 w:0) /// The range of component `v` is `[500, 1000]`. fn get_npos_targets(v: u32, ) -> Weight { - // Minimum execution time: 4_741_111 nanoseconds. - Weight::from_ref_time(113_360_179) - // Standard Error: 25_375 - .saturating_add(Weight::from_ref_time(9_494_142).saturating_mul(v.into())) + // Minimum execution time: 4_916_401 nanoseconds. + Weight::from_ref_time(81_160_966) + // Standard Error: 23_829 + .saturating_add(Weight::from_ref_time(9_883_413).saturating_mul(v.into())) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into()))) } @@ -859,8 +872,8 @@ impl WeightInfo for () { // Storage: Staking MaxNominatorsCount (r:0 w:1) // Storage: Staking MinNominatorBond (r:0 w:1) fn set_staking_configs_all_set() -> Weight { - // Minimum execution time: 11_074 nanoseconds. - Weight::from_ref_time(11_312_000) + // Minimum execution time: 10_937 nanoseconds. + Weight::from_ref_time(11_324_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: Staking MinCommission (r:0 w:1) @@ -870,8 +883,8 @@ impl WeightInfo for () { // Storage: Staking MaxNominatorsCount (r:0 w:1) // Storage: Staking MinNominatorBond (r:0 w:1) fn set_staking_configs_all_remove() -> Weight { - // Minimum execution time: 9_795 nanoseconds. - Weight::from_ref_time(10_116_000) + // Minimum execution time: 9_424 nanoseconds. + Weight::from_ref_time(10_021_000) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: Staking Ledger (r:1 w:0) @@ -885,17 +898,23 @@ impl WeightInfo for () { // Storage: VoterList ListBags (r:1 w:1) // Storage: VoterList CounterForListNodes (r:1 w:1) fn chill_other() -> Weight { - // Minimum execution time: 82_914 nanoseconds. - Weight::from_ref_time(83_848_000) + // Minimum execution time: 84_495 nanoseconds. + Weight::from_ref_time(85_559_000) .saturating_add(RocksDbWeight::get().reads(11)) .saturating_add(RocksDbWeight::get().writes(6)) } // Storage: Staking MinCommission (r:1 w:0) // Storage: Staking Validators (r:1 w:1) fn force_apply_min_commission() -> Weight { - // Minimum execution time: 20_317 nanoseconds. - Weight::from_ref_time(20_639_000) + // Minimum execution time: 20_385 nanoseconds. + Weight::from_ref_time(20_824_000) .saturating_add(RocksDbWeight::get().reads(2)) .saturating_add(RocksDbWeight::get().writes(1)) } + // Storage: Staking MinCommission (r:0 w:1) + fn set_min_commission() -> Weight { + // Minimum execution time: 6_995 nanoseconds. + Weight::from_ref_time(7_213_000) + .saturating_add(RocksDbWeight::get().writes(1)) + } } From ff816f2e9c21379e071965f2f85a17cf13ee63ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Mon, 26 Dec 2022 17:37:09 +0100 Subject: [PATCH 201/221] Make CLI state pruning optional again (#13017) * Make CLI state pruning optional again The state pruning setting is stored in the database when it is created. In later runs it is fine to drop the `--state-pruning` CLI argument as the setting is stored in the database. The state db will only return an error if the stored state pruning doesn't match the state pruning given via CLI. Recently we improved the state pruning CLI handling and accidentally made the state pruning value always present (as we set some default value for the clap). If we could find out if a user has passed a value or the default value was taken, we could keep the default value in the CLI interface, but clap isn't supporting this right now. So, we need to go back and make `state_pruning` an optional with the default written into the docs. It also adds a test to ensure that we don't break this behavior again. * More docs --- bin/node/cli/tests/common.rs | 6 ++- .../cli/tests/remember_state_pruning_works.rs | 38 +++++++++++++++++ client/cli/src/params/pruning_params.rs | 41 +++++++++++++++---- 3 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 bin/node/cli/tests/remember_state_pruning_works.rs diff --git a/bin/node/cli/tests/common.rs b/bin/node/cli/tests/common.rs index 358c09779d59a..c8f9e2b3d3c82 100644 --- a/bin/node/cli/tests/common.rs +++ b/bin/node/cli/tests/common.rs @@ -161,6 +161,7 @@ pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) { let line = line.expect("failed to obtain next line from stdout for WS address discovery"); data.push_str(&line); + data.push_str("\n"); // does the line contain our port (we expect this specific output from substrate). let sock_addr = match line.split_once("Running JSON-RPC WS server: addr=") { @@ -170,7 +171,10 @@ pub fn find_ws_url_from_output(read: impl Read + Send) -> (String, String) { Some(format!("ws://{}", sock_addr)) }) - .expect("We should get a WebSocket address"); + .unwrap_or_else(|| { + eprintln!("Observed node output:\n{}", data); + panic!("We should get a WebSocket address") + }); (ws_url, data) } diff --git a/bin/node/cli/tests/remember_state_pruning_works.rs b/bin/node/cli/tests/remember_state_pruning_works.rs new file mode 100644 index 0000000000000..5b8e34cc7a00d --- /dev/null +++ b/bin/node/cli/tests/remember_state_pruning_works.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +use tempfile::tempdir; + +pub mod common; + +#[tokio::test] +#[cfg(unix)] +async fn remember_state_pruning_works() { + let base_path = tempdir().expect("could not create a temp dir"); + + // First run with `--state-pruning=archive`. + common::run_node_for_a_while( + base_path.path(), + &["--dev", "--state-pruning=archive", "--no-hardware-benchmarks"], + ) + .await; + + // Then run again without specifying the state pruning. + // This should load state pruning settings from the db. + common::run_node_for_a_while(base_path.path(), &["--dev", "--no-hardware-benchmarks"]).await; +} diff --git a/client/cli/src/params/pruning_params.rs b/client/cli/src/params/pruning_params.rs index 7e50f53d7169a..fd01ba67bab7d 100644 --- a/client/cli/src/params/pruning_params.rs +++ b/client/cli/src/params/pruning_params.rs @@ -28,21 +28,44 @@ pub struct PruningParams { /// This mode specifies when the block's state (ie, storage) /// should be pruned (ie, removed) from the database. /// + /// This setting can only be set on the first creation of the database. Every subsequent run + /// will load the pruning mode from the database and will error if the stored mode doesn't + /// match this CLI value. It is fine to drop this CLI flag for subsequent runs. + /// /// Possible values: - /// 'archive' Keep the state of all blocks. - /// 'archive-canonical' Keep only the state of finalized blocks. - /// number Keep the state of the last number of finalized blocks. - #[arg(alias = "pruning", long, value_name = "PRUNING_MODE", default_value = "256")] - pub state_pruning: DatabasePruningMode, + /// + /// - archive: + /// + /// Keep the state of all blocks. + /// + /// - 'archive-canonical' + /// + /// Keep only the state of finalized blocks. + /// + /// - number + /// + /// Keep the state of the last number of finalized blocks. + /// + /// [default: 256] + #[arg(alias = "pruning", long, value_name = "PRUNING_MODE")] + pub state_pruning: Option, /// Specify the blocks pruning mode. /// /// This mode specifies when the block's body (including justifications) /// should be pruned (ie, removed) from the database. /// /// Possible values: - /// 'archive' Keep all blocks. - /// 'archive-canonical' Keep only finalized blocks. - /// number Keep the last `number` of finalized blocks. + /// - 'archive' + /// + /// Keep all blocks. + /// + /// - 'archive-canonical' + /// + /// Keep only finalized blocks. + /// + /// - number + /// + /// Keep the last `number` of finalized blocks. #[arg( alias = "keep-blocks", long, @@ -55,7 +78,7 @@ pub struct PruningParams { impl PruningParams { /// Get the pruning value from the parameters pub fn state_pruning(&self) -> error::Result> { - Ok(Some(self.state_pruning.into())) + Ok(self.state_pruning.map(|v| v.into())) } /// Get the block pruning value from the parameters From 15b18aebff05f1a2b7e8737ccc96ca3860b648c4 Mon Sep 17 00:00:00 2001 From: Dmitry Markin Date: Tue, 27 Dec 2022 13:05:12 +0300 Subject: [PATCH 202/221] Runtime diagnostics for leaked messages in unbounded channels (part 2) (#13020) * Fix code review issues * Clarify doc * Get rid of backtrace mutex * kick CI --- Cargo.lock | 64 +++++++++++++++++------- client/network/Cargo.toml | 1 + client/network/src/service/out_events.rs | 24 ++++----- client/utils/Cargo.toml | 1 + client/utils/src/mpsc.rs | 27 +++++----- 5 files changed, 69 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19abb44e5ff9c..c93a0b7fdd28a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,16 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli", + "gimli 0.26.1", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.0", ] [[package]] @@ -316,16 +325,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.64" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ - "addr2line", + "addr2line 0.19.0", "cc", "cfg-if", "libc", - "miniz_oxide", - "object 0.27.1", + "miniz_oxide 0.6.2", + "object 0.30.0", "rustc-demangle", ] @@ -1017,7 +1026,7 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.26.1", "log", "regalloc2", "smallvec", @@ -1820,7 +1829,7 @@ dependencies = [ "crc32fast", "libc", "libz-sys", - "miniz_oxide", + "miniz_oxide 0.4.4", ] [[package]] @@ -2445,6 +2454,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + [[package]] name = "git2" version = "0.14.2" @@ -3877,6 +3892,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.4" @@ -4611,22 +4635,22 @@ dependencies = [ [[package]] name = "object" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap", "memchr", ] [[package]] name = "object" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" dependencies = [ - "crc32fast", - "hashbrown 0.12.3", - "indexmap", "memchr", ] @@ -7756,6 +7780,7 @@ dependencies = [ "assert_matches", "async-trait", "asynchronous-codec", + "backtrace", "bytes", "either", "fnv", @@ -8403,6 +8428,7 @@ dependencies = [ name = "sc-utils" version = "4.0.0-dev" dependencies = [ + "backtrace", "futures", "futures-timer", "lazy_static", @@ -11142,7 +11168,7 @@ dependencies = [ "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli", + "gimli 0.26.1", "log", "object 0.29.0", "target-lexicon", @@ -11159,7 +11185,7 @@ checksum = "5c587c62e91c5499df62012b87b88890d0eb470b2ffecc5964e9da967b70c77c" dependencies = [ "anyhow", "cranelift-entity", - "gimli", + "gimli 0.26.1", "indexmap", "log", "object 0.29.0", @@ -11176,12 +11202,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "047839b5dabeae5424a078c19b8cc897e5943a7fadc69e3d888b9c9a897666b3" dependencies = [ - "addr2line", + "addr2line 0.17.0", "anyhow", "bincode", "cfg-if", "cpp_demangle", - "gimli", + "gimli 0.26.1", "log", "object 0.29.0", "rustc-demangle", diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index be9a74d2c1602..35d4253f6b37f 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -17,6 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] array-bytes = "4.1" async-trait = "0.1" asynchronous-codec = "0.6" +backtrace = "0.3.67" bytes = "1" codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } either = "1.5.3" diff --git a/client/network/src/service/out_events.rs b/client/network/src/service/out_events.rs index 8febdd4726b37..5e0c8ac6a1a40 100644 --- a/client/network/src/service/out_events.rs +++ b/client/network/src/service/out_events.rs @@ -31,13 +31,13 @@ //! - Send events by calling [`OutChannels::send`]. Events are cloned for each sender in the //! collection. +use backtrace::Backtrace; use futures::{channel::mpsc, prelude::*, ready, stream::FusedStream}; use log::error; use parking_lot::Mutex; use prometheus_endpoint::{register, CounterVec, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_network_common::protocol::event::Event; use std::{ - backtrace::{Backtrace, BacktraceStatus}, cell::RefCell, fmt, pin::Pin, @@ -62,7 +62,7 @@ pub fn channel(name: &'static str, queue_size_warning: i64) -> (Sender, Receiver queue_size: queue_size.clone(), queue_size_warning, warning_fired: false, - creation_backtrace: Backtrace::capture(), + creation_backtrace: Backtrace::new_unresolved(), metrics: metrics.clone(), }; let rx = Receiver { inner: rx, name, queue_size, metrics }; @@ -91,7 +91,8 @@ pub struct Sender { warning_fired: bool, /// Backtrace of a place where the channel was created. creation_backtrace: Backtrace, - /// Clone of [`Receiver::metrics`]. + /// Clone of [`Receiver::metrics`]. Will be initialized when [`Sender`] is added to + /// [`OutChannels`] with `OutChannels::push()`. metrics: Arc>>>>, } @@ -193,17 +194,12 @@ impl OutChannels { let queue_size = sender.queue_size.fetch_add(1, Ordering::Relaxed); if queue_size == sender.queue_size_warning && !sender.warning_fired { sender.warning_fired = true; - match sender.creation_backtrace.status() { - BacktraceStatus::Captured => error!( - "The number of unprocessed events in channel `{}` reached {}.\n\ - The channel was created at:\n{}", - sender.name, sender.queue_size_warning, sender.creation_backtrace, - ), - _ => error!( - "The number of unprocessed events in channel `{}` reached {}.", - sender.name, sender.queue_size_warning, - ), - } + sender.creation_backtrace.resolve(); + error!( + "The number of unprocessed events in channel `{}` reached {}.\n\ + The channel was created at:\n{:?}", + sender.name, sender.queue_size_warning, sender.creation_backtrace, + ); } sender.inner.unbounded_send(event.clone()).is_ok() }); diff --git a/client/utils/Cargo.toml b/client/utils/Cargo.toml index 082ac3b55e80d..e80588453597e 100644 --- a/client/utils/Cargo.toml +++ b/client/utils/Cargo.toml @@ -10,6 +10,7 @@ description = "I/O for Substrate runtimes" readme = "README.md" [dependencies] +backtrace = "0.3.67" futures = "0.3.21" futures-timer = "3.0.2" lazy_static = "1.4.0" diff --git a/client/utils/src/mpsc.rs b/client/utils/src/mpsc.rs index 7db5e49f5bca9..d74703c4abd08 100644 --- a/client/utils/src/mpsc.rs +++ b/client/utils/src/mpsc.rs @@ -37,6 +37,7 @@ mod inner { mod inner { // tracing implementation use crate::metrics::UNBOUNDED_CHANNELS_COUNTER; + use backtrace::Backtrace; use futures::{ channel::mpsc::{ self, SendError, TryRecvError, TrySendError, UnboundedReceiver, UnboundedSender, @@ -47,7 +48,6 @@ mod inner { }; use log::error; use std::{ - backtrace::{Backtrace, BacktraceStatus}, pin::Pin, sync::{ atomic::{AtomicBool, AtomicI64, Ordering}, @@ -108,7 +108,7 @@ mod inner { queue_size: queue_size.clone(), queue_size_warning, warning_fired: Arc::new(AtomicBool::new(false)), - creation_backtrace: Arc::new(Backtrace::capture()), + creation_backtrace: Arc::new(Backtrace::new_unresolved()), }; let receiver = TracingUnboundedReceiver { inner: r, name, queue_size }; (sender, receiver) @@ -149,23 +149,20 @@ mod inner { let queue_size = self.queue_size.fetch_add(1, Ordering::Relaxed); if queue_size == self.queue_size_warning && - !self.warning_fired.load(Ordering::Relaxed) + self.warning_fired + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() { // `warning_fired` and `queue_size` are not synchronized, so it's possible // that the warning is fired few times before the `warning_fired` is seen // by all threads. This seems better than introducing a mutex guarding them. - self.warning_fired.store(true, Ordering::Relaxed); - match self.creation_backtrace.status() { - BacktraceStatus::Captured => error!( - "The number of unprocessed messages in channel `{}` reached {}.\n\ - The channel was created at:\n{}", - self.name, self.queue_size_warning, self.creation_backtrace, - ), - _ => error!( - "The number of unprocessed messages in channel `{}` reached {}.", - self.name, self.queue_size_warning, - ), - } + let mut backtrace = (*self.creation_backtrace).clone(); + backtrace.resolve(); + error!( + "The number of unprocessed messages in channel `{}` reached {}.\n\ + The channel was created at:\n{:?}", + self.name, self.queue_size_warning, backtrace, + ); } s From f653eac24a8aa19d03f67f9ac81bd2a1bd608321 Mon Sep 17 00:00:00 2001 From: Sasha Gryaznov Date: Tue, 27 Dec 2022 15:24:49 +0200 Subject: [PATCH 203/221] [contracts] Make debug buffer work like a FIFO pipe (#12953) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make debug buffer work like a FIFO pipe * remove unused Error type * Remove panics * Update frame/contracts/src/exec.rs Co-authored-by: Sasha Gryaznov Co-authored-by: Alexander Theißen --- frame/contracts/src/exec.rs | 84 ++++++++++++++++------------- frame/contracts/src/lib.rs | 3 -- frame/contracts/src/wasm/mod.rs | 4 +- frame/contracts/src/wasm/runtime.rs | 4 +- 4 files changed, 52 insertions(+), 43 deletions(-) diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index f4dc3a0ec0250..df1dd24856a8a 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -279,7 +279,7 @@ pub trait Ext: sealing::Sealed { /// when the code is executing on-chain. /// /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. - fn append_debug_buffer(&mut self, msg: &str) -> Result; + fn append_debug_buffer(&mut self, msg: &str) -> bool; /// Call some dispatchable and return the result. fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; @@ -1334,16 +1334,34 @@ where &mut self.top_frame_mut().nested_gas } - fn append_debug_buffer(&mut self, msg: &str) -> Result { + fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - if !msg.is_empty() { - buffer - .try_extend(&mut msg.bytes()) - .map_err(|_| Error::::DebugBufferExhausted)?; - } - Ok(true) + let mut msg = msg.bytes(); + let num_drain = { + let capacity = DebugBufferVec::::bound().checked_sub(buffer.len()).expect( + " + `buffer` is of type `DebugBufferVec`, + `DebugBufferVec` is a `BoundedVec`, + `BoundedVec::len()` <= `BoundedVec::bound()`; + qed + ", + ); + msg.len().saturating_sub(capacity).min(buffer.len()) + }; + buffer.drain(0..num_drain); + buffer + .try_extend(&mut msg) + .map_err(|_| { + log::debug!( + target: "runtime::contracts", + "Debug message to big (size={}) for debug buffer (bound={})", + msg.len(), DebugBufferVec::::bound(), + ); + }) + .ok(); + true } else { - Ok(false) + false } } @@ -2511,12 +2529,8 @@ mod tests { #[test] fn printing_works() { let code_hash = MockLoader::insert(Call, |ctx, _| { - ctx.ext - .append_debug_buffer("This is a test") - .expect("Maximum allowed debug buffer size exhausted!"); - ctx.ext - .append_debug_buffer("More text") - .expect("Maximum allowed debug buffer size exhausted!"); + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); exec_success() }); @@ -2549,12 +2563,8 @@ mod tests { #[test] fn printing_works_on_fail() { let code_hash = MockLoader::insert(Call, |ctx, _| { - ctx.ext - .append_debug_buffer("This is a test") - .expect("Maximum allowed debug buffer size exhausted!"); - ctx.ext - .append_debug_buffer("More text") - .expect("Maximum allowed debug buffer size exhausted!"); + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); exec_trapped() }); @@ -2587,7 +2597,7 @@ mod tests { #[test] fn debug_buffer_is_limited() { let code_hash = MockLoader::insert(Call, move |ctx, _| { - ctx.ext.append_debug_buffer("overflowing bytes")?; + ctx.ext.append_debug_buffer("overflowing bytes"); exec_success() }); @@ -2602,20 +2612,22 @@ mod tests { set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); let mut storage_meter = storage::meter::Meter::new(&ALICE, Some(0), 0).unwrap(); - assert_err!( - MockStack::run_call( - ALICE, - BOB, - &mut gas_meter, - &mut storage_meter, - &schedule, - 0, - vec![], - Some(&mut debug_buffer), - Determinism::Deterministic, - ) - .map_err(|e| e.error), - Error::::DebugBufferExhausted + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &mut storage_meter, + &schedule, + 0, + vec![], + Some(&mut debug_buffer), + Determinism::Deterministic, + ) + .unwrap(); + assert_eq!( + &String::from_utf8(debug_buffer[DebugBufferVec::::bound() - 17..].to_vec()) + .unwrap(), + "overflowing bytes" ); }); } diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 39626e43f4653..778ceec961500 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -857,9 +857,6 @@ pub mod pallet { CodeRejected, /// An indetermistic code was used in a context where this is not permitted. Indeterministic, - /// The debug buffer size used during contract execution exceeded the limit determined by - /// the `MaxDebugBufferLen` pallet config parameter. - DebugBufferExhausted, } /// A mapping from an original code hash to the original code, untouched by instrumentation. diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 903ca0abb5e06..553bae59e78b9 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -588,9 +588,9 @@ mod tests { fn gas_meter(&mut self) -> &mut GasMeter { &mut self.gas_meter } - fn append_debug_buffer(&mut self, msg: &str) -> Result { + fn append_debug_buffer(&mut self, msg: &str) -> bool { self.debug_buffer.extend(msg.as_bytes()); - Ok(true) + true } fn call_runtime( &self, diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index aaacb9e5f80e6..7d67adc3ded33 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2380,11 +2380,11 @@ pub mod env { str_len: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::DebugMessage)?; - if ctx.ext.append_debug_buffer("")? { + if ctx.ext.append_debug_buffer("") { let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?; let msg = core::str::from_utf8(&data).map_err(|_| >::DebugMessageInvalidUTF8)?; - ctx.ext.append_debug_buffer(msg)?; + ctx.ext.append_debug_buffer(msg); return Ok(ReturnCode::Success) } Ok(ReturnCode::LoggingDisabled) From 7b7482c0404605fcf090629d75f53e616a076198 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 27 Dec 2022 14:48:17 +0100 Subject: [PATCH 204/221] Improve inactive fund tracking (#13009) * Improve inactive fund tracking * Resetting migration * Fix * Update frame/balances/src/migration.rs --- bin/node/executor/tests/basic.rs | 11 ++++++++++- frame/balances/src/migration.rs | 26 ++++++++++++++++++++++++++ frame/treasury/src/lib.rs | 19 ++++++++++++------- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index c88703b929e6b..02b2a8787b5d5 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -30,7 +30,7 @@ use sp_runtime::{ use kitchensink_runtime::{ constants::{currency::*, time::SLOT_DURATION}, Balances, CheckedExtrinsic, Header, Runtime, RuntimeCall, RuntimeEvent, System, - TransactionPayment, UncheckedExtrinsic, + TransactionPayment, Treasury, UncheckedExtrinsic, }; use node_primitives::{Balance, Hash}; use node_testing::keyring::*; @@ -398,6 +398,7 @@ fn full_native_block_import_works() { }); fees = t.execute_with(|| transfer_fee(&xt())); + let pot = t.execute_with(|| Treasury::pot()); executor_call(&mut t, "Core_execute_block", &block2.0, true).0.unwrap(); @@ -408,6 +409,14 @@ fn full_native_block_import_works() { ); assert_eq!(Balances::total_balance(&bob()), 179 * DOLLARS - fees); let events = vec![ + EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Treasury(pallet_treasury::Event::UpdatedInactive { + reactivated: 0, + deactivated: pot, + }), + topics: vec![], + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: RuntimeEvent::System(frame_system::Event::ExtrinsicSuccess { diff --git a/frame/balances/src/migration.rs b/frame/balances/src/migration.rs index 08e1d8c7a2c74..b660ec9fd3235 100644 --- a/frame/balances/src/migration.rs +++ b/frame/balances/src/migration.rs @@ -69,3 +69,29 @@ impl, A: Get>, I: 'static> OnRuntimeUpgrade migrate_v0_to_v1::(&A::get()) } } + +pub struct ResetInactive(PhantomData<(T, I)>); +impl, I: 'static> OnRuntimeUpgrade for ResetInactive { + fn on_runtime_upgrade() -> Weight { + let onchain_version = Pallet::::on_chain_storage_version(); + + if onchain_version == 1 { + // Remove the old `StorageVersion` type. + frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix( + Pallet::::name().as_bytes(), + "StorageVersion".as_bytes(), + )); + + InactiveIssuance::::kill(); + + // Set storage version to `0`. + StorageVersion::new(0).put::>(); + + log::info!(target: "runtime::balances", "Storage to version 0"); + T::DbWeight::get().reads_writes(1, 2) + } else { + log::info!(target: "runtime::balances", "Migration did not execute. This probably should be removed"); + T::DbWeight::get().reads(1) + } + } +} diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 0ffc53d8b7978..1bde1238191a4 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -225,7 +225,8 @@ pub mod pallet { /// The amount which has been reported as inactive to Currency. #[pallet::storage] - pub type Inactive, I: 'static = ()> = StorageValue<_, BalanceOf, ValueQuery>; + pub type Deactivated, I: 'static = ()> = + StorageValue<_, BalanceOf, ValueQuery>; /// Proposal indices that have been approved but not yet awarded. #[pallet::storage] @@ -292,6 +293,8 @@ pub mod pallet { amount: BalanceOf, beneficiary: T::AccountId, }, + /// The inactive funds of the pallet have been updated. + UpdatedInactive { reactivated: BalanceOf, deactivated: BalanceOf }, } /// Error for the treasury pallet. @@ -321,13 +324,15 @@ pub mod pallet { /// # fn on_initialize(n: T::BlockNumber) -> Weight { let pot = Self::pot(); - let deactivated = Inactive::::get(); + let deactivated = Deactivated::::get(); if pot != deactivated { - match (pot > deactivated, pot.max(deactivated) - pot.min(deactivated)) { - (true, delta) => T::Currency::deactivate(delta), - (false, delta) => T::Currency::reactivate(delta), - } - Inactive::::put(&pot); + T::Currency::reactivate(deactivated); + T::Currency::deactivate(pot); + Deactivated::::put(&pot); + Self::deposit_event(Event::::UpdatedInactive { + reactivated: deactivated, + deactivated: pot, + }); } // Check to see if we should spend some funds! From 811e9d7713d36aa77dc60f5ebc324367f9bf03aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 28 Dec 2022 09:16:52 +0100 Subject: [PATCH 205/221] Print "stalled" task on shutdown (#13022) * Print "stalled" task on shutdown When the node is shutting down, we give the Tokio runtime 60 seconds to shutdown. If after these 60 seconds there are still running tasks, we now print these tasks. This should help debugging nodes that have stalled tasks. This pr introduces a `TaskRegistry` that keeps track of all running tasks. Each task registers and unregisters itself in this `TaskRegistry`. * Fix rustdoc * Update client/service/src/lib.rs --- Cargo.lock | 1 + client/cli/Cargo.toml | 1 + client/cli/src/runner.rs | 117 ++++++++++++++++++++----- client/service/src/lib.rs | 2 +- client/service/src/task_manager/mod.rs | 98 ++++++++++++++++++++- 5 files changed, 193 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c93a0b7fdd28a..b2aca09fe2564 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7280,6 +7280,7 @@ dependencies = [ "sp-keystore", "sp-panic-handler", "sp-runtime", + "sp-tracing", "sp-version", "tempfile", "thiserror", diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index a5de78da487c8..ee4426b91dbb8 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -50,6 +50,7 @@ sp-version = { version = "5.0.0", path = "../../primitives/version" } [dev-dependencies] tempfile = "3.1.0" futures-timer = "3.0.1" +sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" } [features] default = ["rocksdb"] diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index d4191feddfa90..1a532b3bbc6fb 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -152,10 +152,38 @@ impl Runner { // // This is important to be done before we instruct the tokio runtime to shutdown. Otherwise // the tokio runtime will wait the full 60 seconds for all tasks to stop. - drop(task_manager); + let task_registry = task_manager.into_task_registry(); // Give all futures 60 seconds to shutdown, before tokio "leaks" them. - self.tokio_runtime.shutdown_timeout(Duration::from_secs(60)); + let shutdown_timeout = Duration::from_secs(60); + self.tokio_runtime.shutdown_timeout(shutdown_timeout); + + let running_tasks = task_registry.running_tasks(); + + if !running_tasks.is_empty() { + log::error!("Detected running(potentially stalled) tasks on shutdown:"); + running_tasks.iter().for_each(|(task, count)| { + let instances_desc = + if *count > 1 { format!("with {} instances ", count) } else { "".to_string() }; + + if task.is_default_group() { + log::error!( + "Task \"{}\" was still running {}after waiting {} seconds to finish.", + task.name, + instances_desc, + shutdown_timeout.as_secs(), + ); + } else { + log::error!( + "Task \"{}\" (Group: {}) was still running {}after waiting {} seconds to finish.", + task.name, + task.group, + instances_desc, + shutdown_timeout.as_secs(), + ); + } + }); + } res.map_err(Into::into) } @@ -388,34 +416,75 @@ mod tests { assert!((count as u128) < (Duration::from_secs(30).as_millis() / 50)); } + fn run_test_in_another_process( + test_name: &str, + test_body: impl FnOnce(), + ) -> Option { + if std::env::var("RUN_FORKED_TEST").is_ok() { + test_body(); + None + } else { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .arg(test_name) + .env("RUN_FORKED_TEST", "1") + .output() + .unwrap(); + + assert!(output.status.success()); + Some(output) + } + } + /// This test ensures that `run_node_until_exit` aborts waiting for "stuck" tasks after 60 /// seconds, aka doesn't wait until they are finished (which may never happen). #[test] fn ensure_run_until_exit_is_not_blocking_indefinitely() { - let runner = create_runner(); + let output = run_test_in_another_process( + "ensure_run_until_exit_is_not_blocking_indefinitely", + || { + sp_tracing::try_init_simple(); + + let runner = create_runner(); + + runner + .run_node_until_exit(move |cfg| async move { + let task_manager = + TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); + let (sender, receiver) = futures::channel::oneshot::channel(); + + // We need to use `spawn_blocking` here so that we get a dedicated thread + // for our future. This future is more blocking code that will never end. + task_manager.spawn_handle().spawn_blocking("test", None, async move { + let _ = sender.send(()); + loop { + std::thread::sleep(Duration::from_secs(30)); + } + }); + + task_manager.spawn_essential_handle().spawn_blocking( + "test2", + None, + async { + // Let's stop this essential task directly when our other task + // started. It will signal that the task manager should end. + let _ = receiver.await; + }, + ); + + Ok::<_, sc_service::Error>(task_manager) + }) + .unwrap_err(); + }, + ); - runner - .run_node_until_exit(move |cfg| async move { - let task_manager = TaskManager::new(cfg.tokio_handle.clone(), None).unwrap(); - let (sender, receiver) = futures::channel::oneshot::channel(); + let Some(output) = output else { return } ; - // We need to use `spawn_blocking` here so that we get a dedicated thread for our - // future. This future is more blocking code that will never end. - task_manager.spawn_handle().spawn_blocking("test", None, async move { - let _ = sender.send(()); - loop { - std::thread::sleep(Duration::from_secs(30)); - } - }); - - task_manager.spawn_essential_handle().spawn_blocking("test2", None, async { - // Let's stop this essential task directly when our other task started. - // It will signal that the task manager should end. - let _ = receiver.await; - }); + let stderr = dbg!(String::from_utf8(output.stderr).unwrap()); - Ok::<_, sc_service::Error>(task_manager) - }) - .unwrap_err(); + assert!( + stderr.contains("Task \"test\" was still running after waiting 60 seconds to finish.") + ); + assert!(!stderr + .contains("Task \"test2\" was still running after waiting 60 seconds to finish.")); } } diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index 8b3a29ba4032a..1529b822ade32 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -83,7 +83,7 @@ pub use sc_transaction_pool::Options as TransactionPoolOptions; pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, TransactionPool}; #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; -pub use task_manager::{SpawnTaskHandle, TaskManager, DEFAULT_GROUP_NAME}; +pub use task_manager::{SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME}; const DEFAULT_PROTOCOL_ID: &str = "sup"; diff --git a/client/service/src/task_manager/mod.rs b/client/service/src/task_manager/mod.rs index 23265f9672555..d792122576444 100644 --- a/client/service/src/task_manager/mod.rs +++ b/client/service/src/task_manager/mod.rs @@ -24,12 +24,19 @@ use futures::{ future::{pending, select, try_join_all, BoxFuture, Either}, Future, FutureExt, StreamExt, }; +use parking_lot::Mutex; use prometheus_endpoint::{ exponential_buckets, register, CounterVec, HistogramOpts, HistogramVec, Opts, PrometheusError, Registry, U64, }; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; -use std::{panic, pin::Pin, result::Result}; +use std::{ + collections::{hash_map::Entry, HashMap}, + panic, + pin::Pin, + result::Result, + sync::Arc, +}; use tokio::runtime::Handle; use tracing_futures::Instrument; @@ -72,6 +79,7 @@ pub struct SpawnTaskHandle { on_exit: exit_future::Exit, tokio_handle: Handle, metrics: Option, + task_registry: TaskRegistry, } impl SpawnTaskHandle { @@ -113,6 +121,7 @@ impl SpawnTaskHandle { ) { let on_exit = self.on_exit.clone(); let metrics = self.metrics.clone(); + let registry = self.task_registry.clone(); let group = match group.into() { GroupName::Specific(var) => var, @@ -129,6 +138,10 @@ impl SpawnTaskHandle { } let future = async move { + // Register the task and keep the "token" alive until the task is ended. Then this + // "token" will unregister this task. + let _registry_token = registry.register_task(name, group); + if let Some(metrics) = metrics { // Add some wrappers around `task`. let task = { @@ -298,6 +311,8 @@ pub struct TaskManager { /// terminates and gracefully shutdown. Also ends the parent `future()` if a child's essential /// task fails. children: Vec, + /// The registry of all running tasks. + task_registry: TaskRegistry, } impl TaskManager { @@ -324,6 +339,7 @@ impl TaskManager { essential_failed_rx, keep_alive: Box::new(()), children: Vec::new(), + task_registry: Default::default(), }) } @@ -333,6 +349,7 @@ impl TaskManager { on_exit: self.on_exit.clone(), tokio_handle: self.tokio_handle.clone(), metrics: self.metrics.clone(), + task_registry: self.task_registry.clone(), } } @@ -385,6 +402,14 @@ impl TaskManager { pub fn add_child(&mut self, child: TaskManager) { self.children.push(child); } + + /// Consume `self` and return the [`TaskRegistry`]. + /// + /// This [`TaskRegistry`] can be used to check for still running tasks after this task manager + /// was dropped. + pub fn into_task_registry(self) -> TaskRegistry { + self.task_registry + } } #[derive(Clone)] @@ -434,3 +459,74 @@ impl Metrics { }) } } + +/// Ensures that a [`Task`] is unregistered when this object is dropped. +struct UnregisterOnDrop { + task: Task, + registry: TaskRegistry, +} + +impl Drop for UnregisterOnDrop { + fn drop(&mut self) { + let mut tasks = self.registry.tasks.lock(); + + if let Entry::Occupied(mut entry) = (*tasks).entry(self.task.clone()) { + *entry.get_mut() -= 1; + + if *entry.get() == 0 { + entry.remove(); + } + } + } +} + +/// Represents a running async task in the [`TaskManager`]. +/// +/// As a task is identified by a name and a group, it is totally valid that there exists multiple +/// tasks with the same name and group. +#[derive(Clone, Hash, Eq, PartialEq)] +pub struct Task { + /// The name of the task. + pub name: &'static str, + /// The group this task is associated to. + pub group: &'static str, +} + +impl Task { + /// Returns if the `group` is the [`DEFAULT_GROUP_NAME`]. + pub fn is_default_group(&self) -> bool { + self.group == DEFAULT_GROUP_NAME + } +} + +/// Keeps track of all running [`Task`]s in [`TaskManager`]. +#[derive(Clone, Default)] +pub struct TaskRegistry { + tasks: Arc>>, +} + +impl TaskRegistry { + /// Register a task with the given `name` and `group`. + /// + /// Returns [`UnregisterOnDrop`] that ensures that the task is unregistered when this value is + /// dropped. + fn register_task(&self, name: &'static str, group: &'static str) -> UnregisterOnDrop { + let task = Task { name, group }; + + { + let mut tasks = self.tasks.lock(); + + *(*tasks).entry(task.clone()).or_default() += 1; + } + + UnregisterOnDrop { task, registry: self.clone() } + } + + /// Returns the running tasks. + /// + /// As a task is only identified by its `name` and `group`, there can be duplicate tasks. The + /// number per task represents the concurrently running tasks with the same identifier. + pub fn running_tasks(&self) -> HashMap { + (*self.tasks.lock()).clone() + } +} From d08a41269f0d68c309f873f0ee915463bab006e8 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Thu, 5 Jan 2023 06:10:12 +0000 Subject: [PATCH 206/221] Update frame/nomination-pools/src/lib.rs Co-authored-by: Oliver Tale-Yazdi --- frame/nomination-pools/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 6f8dfeb8d4690..92129d97a455b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -756,7 +756,7 @@ impl Commission { /// Gets the current commission (if any) and payee to be paid. /// - /// /// `None` is returned if a commission has not been set. Commission is bounded to + /// `None` is returned if a commission has not been set. Commission is bounded to /// `GlobalMaxCommission`. fn maybe_commission_and_payee( &self, From b8408bb929c9275ca087cdc1ff341543f2404ea3 Mon Sep 17 00:00:00 2001 From: Ankan <10196091+Ank4n@users.noreply.github.com> Date: Mon, 9 Jan 2023 08:33:29 +0100 Subject: [PATCH 207/221] add note on usage of twox hash (#13089) --- frame/fast-unstake/src/lib.rs | 2 ++ frame/nomination-pools/src/lib.rs | 2 ++ frame/staking/src/pallet/mod.rs | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index f2faeebc13478..c0bffc4427a11 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -147,6 +147,8 @@ pub mod pallet { /// The map of all accounts wishing to be unstaked. /// /// Keeps track of `AccountId` wishing to unstake and it's corresponding deposit. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] pub type Queue = CountedStorageMap<_, Twox64Concat, T::AccountId, BalanceOf>; diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 9102413034d72..20e71aaacc2b8 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -1512,6 +1512,8 @@ pub mod pallet { pub type GlobalMaxCommission = StorageValue<_, Perbill, OptionQuery>; /// Active members. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] pub type PoolMembers = CountedStorageMap<_, Twox64Concat, T::AccountId, PoolMember>; diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 92502949ef1a0..ce4f2494621b4 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -292,6 +292,8 @@ pub mod pallet { pub type Invulnerables = StorageValue<_, Vec, ValueQuery>; /// Map from all locked "stash" accounts to the controller account. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn bonded)] pub type Bonded = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; @@ -320,12 +322,16 @@ pub mod pallet { pub type Ledger = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger>; /// Where the reward payment should be made. Keyed by stash. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn payee)] pub type Payee = StorageMap<_, Twox64Concat, T::AccountId, RewardDestination, ValueQuery>; /// The map from (wannabe) validator stash key to the preferences of that validator. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn validators)] pub type Validators = @@ -353,6 +359,8 @@ pub mod pallet { /// /// Lastly, if any of the nominators become non-decodable, they can be chilled immediately via /// [`Call::chill_other`] dispatchable by anyone. + /// + /// TWOX-NOTE: SAFE since `AccountId` is a secure hash. #[pallet::storage] #[pallet::getter(fn nominators)] pub type Nominators = From 34febb31e57b8d50d0e04ff658512136b1849461 Mon Sep 17 00:00:00 2001 From: Sebastian Kunert Date: Mon, 9 Jan 2023 14:46:09 +0100 Subject: [PATCH 208/221] Remove unused code in state-db (#13103) --- client/state-db/src/noncanonical.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/client/state-db/src/noncanonical.rs b/client/state-db/src/noncanonical.rs index b7eec8ab60ee0..e950d245dcec9 100644 --- a/client/state-db/src/noncanonical.rs +++ b/client/state-db/src/noncanonical.rs @@ -323,7 +323,6 @@ impl NonCanonicalOverlay { &self, level_index: usize, discarded_journals: &mut Vec>, - discarded_blocks: &mut Vec, hash: &BlockHash, ) { if let Some(level) = self.levels.get(level_index) { @@ -335,13 +334,7 @@ impl NonCanonicalOverlay { .clone(); if parent == *hash { discarded_journals.push(overlay.journal_key.clone()); - discarded_blocks.push(overlay.hash.clone()); - self.discard_journals( - level_index + 1, - discarded_journals, - discarded_blocks, - &overlay.hash, - ); + self.discard_journals(level_index + 1, discarded_journals, &overlay.hash); } }); } @@ -393,7 +386,6 @@ impl NonCanonicalOverlay { self.pinned_canonincalized.push(hash.clone()); let mut discarded_journals = Vec::new(); - let mut discarded_blocks = Vec::new(); for (i, overlay) in level.blocks.into_iter().enumerate() { let mut pinned_children = 0; // That's the one we need to canonicalize @@ -411,12 +403,7 @@ impl NonCanonicalOverlay { commit.data.deleted.extend(overlay.deleted.clone()); } else { // Discard this overlay - self.discard_journals( - 0, - &mut discarded_journals, - &mut discarded_blocks, - &overlay.hash, - ); + self.discard_journals(0, &mut discarded_journals, &overlay.hash); pinned_children = discard_descendants( &mut self.levels.as_mut_slices(), &mut self.values, @@ -437,7 +424,6 @@ impl NonCanonicalOverlay { discard_values(&mut self.values, overlay.inserted); } discarded_journals.push(overlay.journal_key.clone()); - discarded_blocks.push(overlay.hash.clone()); } commit.meta.deleted.append(&mut discarded_journals); @@ -548,9 +534,6 @@ impl NonCanonicalOverlay { trace!(target: "state-db-pin", "Discarding unpinned non-canon block: {:?}", hash); discard_values(&mut self.values, inserted); self.parents.remove(&hash); - true - } else { - false } }, Entry::Vacant(_) => break, From ae9005c5230bc7e1bcfb03ce80040ef517fd4615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Mon, 9 Jan 2023 16:06:47 +0000 Subject: [PATCH 209/221] EPM and staking events improvement (#13035) * EPM and staking events improvement * Uses RawOrigin in ElectionCompute event * Refactors new phase events to PhaseTransition event * PhaseTransitioned and remove RawOrigin from event * Adds helpers for epm phase transition and staking force new * addresses review comments * nit: removes unecessary clone * fixes benchmarks Co-authored-by: parity-processbot <> --- .../src/benchmarking.rs | 7 +- .../election-provider-multi-phase/src/lib.rs | 281 +++++++++++++----- .../src/signed.rs | 128 ++++++-- .../src/unsigned.rs | 30 +- frame/staking/src/pallet/impls.rs | 11 +- frame/staking/src/pallet/mod.rs | 8 +- frame/staking/src/tests.rs | 28 +- 7 files changed, 369 insertions(+), 124 deletions(-) diff --git a/frame/election-provider-multi-phase/src/benchmarking.rs b/frame/election-provider-multi-phase/src/benchmarking.rs index 10041f6aec07c..16263d97da586 100644 --- a/frame/election-provider-multi-phase/src/benchmarking.rs +++ b/frame/election-provider-multi-phase/src/benchmarking.rs @@ -200,7 +200,7 @@ frame_benchmarking::benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_signed(); + >::phase_transition(Phase::Signed); } verify { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_signed()); @@ -210,7 +210,8 @@ frame_benchmarking::benchmarks! { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_off()); }: { - >::on_initialize_open_unsigned(true, 1u32.into()) + let now = frame_system::Pallet::::block_number(); + >::phase_transition(Phase::Unsigned((true, now))); } verify { assert!(>::snapshot().is_none()); assert!(>::current_phase().is_unsigned()); @@ -318,7 +319,7 @@ frame_benchmarking::benchmarks! { submit { // the queue is full and the solution is only better than the worse. >::create_snapshot().map_err(<&str>::from)?; - MultiPhase::::on_initialize_open_signed(); + >::phase_transition(Phase::Signed); >::put(1); let mut signed_submissions = SignedSubmissions::::get(); diff --git a/frame/election-provider-multi-phase/src/lib.rs b/frame/election-provider-multi-phase/src/lib.rs index 6c4a55800f7e8..6c1b1d163d0fb 100644 --- a/frame/election-provider-multi-phase/src/lib.rs +++ b/frame/election-provider-multi-phase/src/lib.rs @@ -758,7 +758,7 @@ pub mod pallet { // NOTE: if signed-phase length is zero, second part of the if-condition fails. match Self::create_snapshot() { Ok(_) => { - Self::on_initialize_open_signed(); + Self::phase_transition(Phase::Signed); T::WeightInfo::on_initialize_open_signed() }, Err(why) => { @@ -797,7 +797,7 @@ pub mod pallet { if need_snapshot { match Self::create_snapshot() { Ok(_) => { - Self::on_initialize_open_unsigned(enabled, now); + Self::phase_transition(Phase::Unsigned((enabled, now))); T::WeightInfo::on_initialize_open_unsigned() }, Err(why) => { @@ -806,7 +806,7 @@ pub mod pallet { }, } } else { - Self::on_initialize_open_unsigned(enabled, now); + Self::phase_transition(Phase::Unsigned((enabled, now))); T::WeightInfo::on_initialize_open_unsigned() } }, @@ -931,6 +931,7 @@ pub mod pallet { >::put(ready); Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: ejected_a_solution, }); @@ -983,6 +984,7 @@ pub mod pallet { Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Emergency, + origin: None, prev_ejected: QueuedSolution::::exists(), }); @@ -1060,6 +1062,7 @@ pub mod pallet { signed_submissions.put(); Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(who), prev_ejected: ejected_a_solution, }); Ok(()) @@ -1102,6 +1105,7 @@ pub mod pallet { Self::deposit_event(Event::SolutionStored { compute: ElectionCompute::Fallback, + origin: None, prev_ejected: QueuedSolution::::exists(), }); @@ -1115,11 +1119,16 @@ pub mod pallet { pub enum Event { /// A solution was stored with the given compute. /// - /// If the solution is signed, this means that it hasn't yet been processed. If the - /// solution is unsigned, this means that it has also been processed. - /// - /// The `bool` is `true` when a previous solution was ejected to make room for this one. - SolutionStored { compute: ElectionCompute, prev_ejected: bool }, + /// The `origin` indicates the origin of the solution. If `origin` is `Some(AccountId)`, + /// the stored solution was submited in the signed phase by a miner with the `AccountId`. + /// Otherwise, the solution was stored either during the unsigned phase or by + /// `T::ForceOrigin`. The `bool` is `true` when a previous solution was ejected to make + /// room for this one. + SolutionStored { + compute: ElectionCompute, + origin: Option, + prev_ejected: bool, + }, /// The election has been finalized, with the given computation and score. ElectionFinalized { compute: ElectionCompute, score: ElectionScore }, /// An election failed. @@ -1130,10 +1139,8 @@ pub mod pallet { Rewarded { account: ::AccountId, value: BalanceOf }, /// An account has been slashed for submitting an invalid signed submission. Slashed { account: ::AccountId, value: BalanceOf }, - /// The signed phase of the given round has started. - SignedPhaseStarted { round: u32 }, - /// The unsigned phase of the given round has started. - UnsignedPhaseStarted { round: u32 }, + /// There was a phase transition in a given round. + PhaseTransitioned { from: Phase, to: Phase, round: u32 }, } /// Error of the pallet that can be returned in response to dispatches. @@ -1349,19 +1356,15 @@ impl Pallet { } } - /// Logic for `::on_initialize` when signed phase is being opened. - pub fn on_initialize_open_signed() { - log!(info, "Starting signed phase round {}.", Self::round()); - >::put(Phase::Signed); - Self::deposit_event(Event::SignedPhaseStarted { round: Self::round() }); - } - - /// Logic for `>::on_initialize` when unsigned phase is being opened. - pub fn on_initialize_open_unsigned(enabled: bool, now: T::BlockNumber) { - let round = Self::round(); - log!(info, "Starting unsigned phase round {} enabled {}.", round, enabled); - >::put(Phase::Unsigned((enabled, now))); - Self::deposit_event(Event::UnsignedPhaseStarted { round }); + /// Phase transition helper. + pub(crate) fn phase_transition(to: Phase) { + log!(info, "Starting phase {:?}, round {}.", to, Self::round()); + Self::deposit_event(Event::PhaseTransitioned { + from: >::get(), + to, + round: Self::round(), + }); + >::put(to); } /// Parts of [`create_snapshot`] that happen inside of this pallet. @@ -1571,7 +1574,7 @@ impl Pallet { >::mutate(|r| *r += 1); // Phase is off now. - >::put(Phase::Off); + Self::phase_transition(Phase::Off); // Kill snapshots. Self::kill_snapshot(); @@ -1652,7 +1655,7 @@ impl ElectionProvider for Pallet { }, Err(why) => { log!(error, "Entering emergency mode: {:?}", why); - >::put(Phase::Emergency); + Self::phase_transition(Phase::Emergency); Err(why) }, } @@ -1898,7 +1901,10 @@ mod tests { roll_to_signed(); assert_eq!(MultiPhase::current_phase(), Phase::Signed); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert!(MultiPhase::snapshot().is_some()); assert_eq!(MultiPhase::round(), 1); @@ -1912,8 +1918,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 } + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, ], ); assert!(MultiPhase::snapshot().is_some()); @@ -1949,8 +1959,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1959,8 +1973,17 @@ mod tests { sum_stake_squared: 0 } }, - Event::SignedPhaseStarted { round: 2 }, - Event::UnsignedPhaseStarted { round: 2 } + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 2 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 55)), + round: 2 + }, ] ); }) @@ -1990,7 +2013,11 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { + from: Phase::Off, + to: Phase::Unsigned((true, 20)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1998,7 +2025,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 20)), + to: Phase::Off, + round: 2 + }, ] ); }); @@ -2028,7 +2060,7 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -2036,7 +2068,8 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ] ) }); @@ -2064,10 +2097,17 @@ mod tests { assert_eq!( multi_phase_events(), - vec![Event::ElectionFinalized { - compute: ElectionCompute::Fallback, - score: ElectionScore { minimal_stake: 0, sum_stake: 0, sum_stake_squared: 0 } - }] + vec![ + Event::ElectionFinalized { + compute: ElectionCompute::Fallback, + score: ElectionScore { + minimal_stake: 0, + sum_stake: 0, + sum_stake_squared: 0 + } + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Off, round: 2 }, + ] ); }); } @@ -2079,7 +2119,10 @@ mod tests { // Signed phase started at block 15 and will end at 25. roll_to_signed(); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert_eq!(MultiPhase::current_phase(), Phase::Signed); assert_eq!(MultiPhase::round(), 1); @@ -2090,11 +2133,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: Default::default() - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ], ); // All storage items must be cleared. @@ -2114,7 +2158,10 @@ mod tests { // signed phase started at block 15 and will end at 25. roll_to_signed(); - assert_eq!(multi_phase_events(), vec![Event::SignedPhaseStarted { round: 1 }]); + assert_eq!( + multi_phase_events(), + vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }] + ); assert_eq!(MultiPhase::current_phase(), Phase::Signed); assert_eq!(MultiPhase::round(), 1); @@ -2144,12 +2191,32 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Slashed { account: 99, value: 5 }, Event::Slashed { account: 99, value: 5 }, Event::Slashed { account: 99, value: 5 }, @@ -2162,7 +2229,8 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 }, ] ); }) @@ -2186,10 +2254,18 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Signed, score: ElectionScore { @@ -2197,7 +2273,12 @@ mod tests { sum_stake: 100, sum_stake_squared: 5200 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ], ); }) @@ -2230,10 +2311,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: false }, Event::ElectionFinalized { @@ -2243,7 +2329,12 @@ mod tests { sum_stake: 100, sum_stake_squared: 5200 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ], ); }) @@ -2270,8 +2361,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -2279,7 +2374,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Off, + round: 2 + }, ] ); }); @@ -2299,9 +2399,18 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, - Event::ElectionFailed + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, + Event::ElectionFailed, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Emergency, + round: 1 + }, ] ); }) @@ -2339,17 +2448,28 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFailed, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Emergency, + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Fallback, + origin: None, prev_ejected: false }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: Default::default() - } + }, + Event::PhaseTransitioned { from: Phase::Emergency, to: Phase::Off, round: 2 }, ] ); }) @@ -2375,10 +2495,17 @@ mod tests { assert_eq!( multi_phase_events(), - vec![Event::ElectionFinalized { - compute: ElectionCompute::Fallback, - score: ElectionScore { minimal_stake: 0, sum_stake: 0, sum_stake_squared: 0 } - }] + vec![ + Event::ElectionFinalized { + compute: ElectionCompute::Fallback, + score: ElectionScore { + minimal_stake: 0, + sum_stake: 0, + sum_stake_squared: 0 + } + }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Off, round: 2 }, + ] ); }); } @@ -2404,7 +2531,13 @@ mod tests { assert_eq!(err, ElectionError::Fallback("NoFallback.")); assert_eq!(MultiPhase::current_phase(), Phase::Emergency); - assert_eq!(multi_phase_events(), vec![Event::ElectionFailed]); + assert_eq!( + multi_phase_events(), + vec![ + Event::ElectionFailed, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Emergency, round: 1 } + ] + ); }); } diff --git a/frame/election-provider-multi-phase/src/signed.rs b/frame/election-provider-multi-phase/src/signed.rs index 12d39e83b6c09..895f3670a7f0d 100644 --- a/frame/election-provider-multi-phase/src/signed.rs +++ b/frame/election-provider-multi-phase/src/signed.rs @@ -620,8 +620,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false } + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + } ] ); }) @@ -645,8 +649,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); @@ -676,8 +684,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Slashed { account: 99, value: 5 } ] ); @@ -713,9 +725,17 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(999), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); @@ -788,12 +808,32 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(100), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(101), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(102), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(103), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 }, Event::ElectionFinalized { compute: ElectionCompute::Signed, @@ -856,13 +896,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(99), prev_ejected: false }, Event::SolutionStored { compute: ElectionCompute::Signed, + origin: Some(99), prev_ejected: true } ] @@ -1112,12 +1154,28 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(100), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(101), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(102), + prev_ejected: false + }, Event::Rewarded { account: 100, value: 7 }, - Event::UnsignedPhaseStarted { round: 1 } + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, ] ); }) @@ -1170,10 +1228,22 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(999), + prev_ejected: false + }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(9999), + prev_ejected: false + }, Event::Slashed { account: 999, value: 5 }, Event::Rewarded { account: 99, value: 7 } ] @@ -1304,8 +1374,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::SolutionStored { compute: ElectionCompute::Signed, prev_ejected: false }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::SolutionStored { + compute: ElectionCompute::Signed, + origin: Some(99), + prev_ejected: false + }, Event::Rewarded { account: 99, value: 7 } ] ); diff --git a/frame/election-provider-multi-phase/src/unsigned.rs b/frame/election-provider-multi-phase/src/unsigned.rs index 7340605dfe621..cdc71f7bf5bf0 100644 --- a/frame/election-provider-multi-phase/src/unsigned.rs +++ b/frame/election-provider-multi-phase/src/unsigned.rs @@ -1055,7 +1055,7 @@ mod tests { Runtime, RuntimeCall, RuntimeOrigin, System, TestNposSolution, TrimHelpers, UnsignedPhase, }, - CurrentPhase, Event, InvalidTransaction, Phase, QueuedSolution, TransactionSource, + Event, InvalidTransaction, Phase, QueuedSolution, TransactionSource, TransactionValidityError, }; use codec::Decode; @@ -1128,7 +1128,7 @@ mod tests { assert!(::pre_dispatch(&call).is_ok()); // unsigned -- but not enabled. - >::put(Phase::Unsigned((false, 25))); + MultiPhase::phase_transition(Phase::Unsigned((false, 25))); assert!(MultiPhase::current_phase().is_unsigned()); assert!(matches!( ::validate_unsigned( @@ -1321,10 +1321,15 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::SolutionStored { compute: ElectionCompute::Unsigned, + origin: None, prev_ejected: false } ] @@ -1661,7 +1666,7 @@ mod tests { let current_block = block_plus(offchain_repeat * 2 + 2); // force the unsigned phase to start on the current block. - CurrentPhase::::set(Phase::Unsigned((true, current_block))); + MultiPhase::phase_transition(Phase::Unsigned((true, current_block))); // clear the cache and create a solution since we are on the first block of the unsigned // phase. @@ -1673,8 +1678,12 @@ mod tests { assert_eq!( multi_phase_events(), vec![ - Event::SignedPhaseStarted { round: 1 }, - Event::UnsignedPhaseStarted { round: 1 }, + Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }, + Event::PhaseTransitioned { + from: Phase::Signed, + to: Phase::Unsigned((true, 25)), + round: 1 + }, Event::ElectionFinalized { compute: ElectionCompute::Fallback, score: ElectionScore { @@ -1682,7 +1691,12 @@ mod tests { sum_stake: 0, sum_stake_squared: 0 } - } + }, + Event::PhaseTransitioned { + from: Phase::Unsigned((true, 25)), + to: Phase::Unsigned((true, 37)), + round: 1 + }, ] ); }) diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index db9aeba6fb58e..7af9b0aaaa04a 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -339,7 +339,7 @@ impl Pallet { if maybe_new_era_validators.is_some() && matches!(ForceEra::::get(), Forcing::ForceNew) { - ForceEra::::put(Forcing::NotForcing); + Self::set_force_era(Forcing::NotForcing); } maybe_new_era_validators @@ -717,11 +717,18 @@ impl Pallet { } } + /// Helper to set a new `ForceEra` mode. + pub(crate) fn set_force_era(mode: Forcing) { + log!(info, "Setting force era mode {:?}.", mode); + ForceEra::::put(mode); + Self::deposit_event(Event::::ForceEra { mode }); + } + /// Ensures that at the end of the current session there will be a new era. pub(crate) fn ensure_new_era() { match ForceEra::::get() { Forcing::ForceAlways | Forcing::ForceNew => (), - _ => ForceEra::::put(Forcing::ForceNew), + _ => Self::set_force_era(Forcing::ForceNew), } } diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index ce4f2494621b4..c0c18b40cf02f 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -714,6 +714,8 @@ pub mod pallet { PayoutStarted { era_index: EraIndex, validator_stash: T::AccountId }, /// A validator has set their preferences. ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs }, + /// A new force era mode was set. + ForceEra { mode: Forcing }, } #[pallet::error] @@ -1377,7 +1379,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_no_eras())] pub fn force_no_eras(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceNone); + Self::set_force_era(Forcing::ForceNone); Ok(()) } @@ -1401,7 +1403,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_new_era())] pub fn force_new_era(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceNew); + Self::set_force_era(Forcing::ForceNew); Ok(()) } @@ -1452,7 +1454,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_new_era_always())] pub fn force_new_era_always(origin: OriginFor) -> DispatchResult { ensure_root(origin)?; - ForceEra::::put(Forcing::ForceAlways); + Self::set_force_era(Forcing::ForceAlways); Ok(()) } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 46c3c97441938..acd41895287c5 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -899,7 +899,7 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 1); // no era change. - ForceEra::::put(Forcing::ForceNone); + Staking::set_force_era(Forcing::ForceNone); start_session(4); assert_eq!(active_era(), 1); @@ -915,7 +915,7 @@ fn forcing_new_era_works() { // back to normal. // this immediately starts a new session. - ForceEra::::put(Forcing::NotForcing); + Staking::set_force_era(Forcing::NotForcing); start_session(8); assert_eq!(active_era(), 1); @@ -923,7 +923,7 @@ fn forcing_new_era_works() { start_session(9); assert_eq!(active_era(), 2); // forceful change - ForceEra::::put(Forcing::ForceAlways); + Staking::set_force_era(Forcing::ForceAlways); start_session(10); assert_eq!(active_era(), 2); @@ -935,7 +935,7 @@ fn forcing_new_era_works() { assert_eq!(active_era(), 4); // just one forceful change - ForceEra::::put(Forcing::ForceNew); + Staking::set_force_era(Forcing::ForceNew); start_session(13); assert_eq!(active_era(), 5); assert_eq!(ForceEra::::get(), Forcing::NotForcing); @@ -2303,7 +2303,7 @@ fn era_is_always_same_length() { ); let session = Session::current_index(); - ForceEra::::put(Forcing::ForceNew); + Staking::set_force_era(Forcing::ForceNew); advance_session(); advance_session(); assert_eq!(current_era(), 3); @@ -2914,7 +2914,10 @@ fn deferred_slashes_are_deferred() { staking_events_since_last_call().as_slice(), &[ Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, slash_era: 1, .. }, + Event::StakersElected, + Event::ForceEra { mode: Forcing::NotForcing }, .., Event::Slashed { staker: 11, amount: 100 }, Event::Slashed { staker: 101, amount: 12 } @@ -2949,6 +2952,7 @@ fn retroactive_deferred_slashes_two_eras_before() { staking_events_since_last_call().as_slice(), &[ Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, slash_era: 1, .. }, .., Event::Slashed { staker: 11, amount: 100 }, @@ -3251,6 +3255,7 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(10), @@ -3318,6 +3323,7 @@ fn non_slashable_offence_doesnt_disable_validator() { Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(0), @@ -3380,6 +3386,7 @@ fn slashing_independent_of_disabling_validator() { Event::StakersElected, Event::EraPaid { era_index: 0, validator_payout: 11075, remainder: 33225 }, Event::Chilled { stash: 11 }, + Event::ForceEra { mode: Forcing::ForceNew }, Event::SlashReported { validator: 11, fraction: Perbill::from_percent(0), @@ -4662,8 +4669,15 @@ mod election_data_provider { MinimumValidatorCount::::put(2); run_to_block(55); assert_eq!(Staking::next_election_prediction(System::block_number()), 55 + 25); - assert_eq!(staking_events().len(), 6); - assert_eq!(*staking_events().last().unwrap(), Event::StakersElected); + assert_eq!(staking_events().len(), 10); + assert_eq!( + *staking_events().last().unwrap(), + Event::ForceEra { mode: Forcing::NotForcing } + ); + assert_eq!( + *staking_events().get(staking_events().len() - 2).unwrap(), + Event::StakersElected + ); // The new era has been planned, forcing is changed from `ForceNew` to `NotForcing`. assert_eq!(ForceEra::::get(), Forcing::NotForcing); }) From f61fc7ebe104b88fa60db869ce6d674f853eb2bb Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 9 Jan 2023 21:36:27 +0400 Subject: [PATCH 210/221] CI: configure `cargo-nextest`'s reporter options explicitly (#13104) --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a0b1464bae23e..f6347e51e62c5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,6 +53,8 @@ variables: RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" RUSTY_CACHIER_COMPRESSION_METHOD: zstd + NEXTEST_FAILURE_OUTPUT: immediate-final + NEXTEST_SUCCESS_OUTPUT: final ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.22" default: From 2dd6e7340b80ab9e68a1ac6a0a2dacea73bda160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Enrique=20Mu=C3=B1oz=20Mart=C3=ADn?= Date: Mon, 9 Jan 2023 20:04:24 +0100 Subject: [PATCH 211/221] Add ensure_pow method (#13042) * add ensure_pow method * reexport checked_pow and ensure_pow --- primitives/arithmetic/src/traits.rs | 52 +++++++++++++++++++++++------ primitives/runtime/src/traits.rs | 10 +++--- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 9e9dff8e6f9ea..dfba046754373 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -19,9 +19,9 @@ use codec::HasCompact; pub use ensure::{ - Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, EnsureFixedPointNumber, - EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, EnsureOpAssign, EnsureSub, - EnsureSubAssign, + ensure_pow, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, + EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, + EnsureOpAssign, EnsureSub, EnsureSubAssign, }; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ @@ -342,7 +342,7 @@ impl SaturatedConversion for T {} /// The *EnsureOps* family functions follows the same behavior as *CheckedOps* but /// returning an [`ArithmeticError`](crate::ArithmeticError) instead of `None`. mod ensure { - use super::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Zero}; + use super::{checked_pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, Zero}; use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand}; /// Performs addition that returns [`ArithmeticError`] instead of wrapping around on overflow. @@ -511,6 +511,27 @@ mod ensure { } } + /// Raises a value to the power of exp, returning `ArithmeticError` if an overflow occurred. + /// + /// Check [`checked_pow`] for more info about border cases. + /// + /// ``` + /// use sp_arithmetic::{traits::ensure_pow, ArithmeticError}; + /// + /// fn overflow() -> Result<(), ArithmeticError> { + /// ensure_pow(2u64, 64)?; + /// Ok(()) + /// } + /// + /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); + /// ``` + pub fn ensure_pow( + base: T, + exp: usize, + ) -> Result { + checked_pow(base, exp).ok_or(ArithmeticError::Overflow) + } + impl EnsureAdd for T {} impl EnsureSub for T {} impl EnsureMul for T {} @@ -953,6 +974,15 @@ mod tests { test_ensure(values(), &EnsureDiv::ensure_div, &CheckedDiv::checked_div); } + #[test] + fn ensure_pow_works() { + test_ensure( + values().into_iter().map(|(base, exp)| (base, exp as usize)).collect(), + ensure_pow, + |&a, &b| checked_pow(a, b), + ); + } + #[test] fn ensure_add_assign_works() { test_ensure_assign(values(), &EnsureAddAssign::ensure_add_assign, &EnsureAdd::ensure_add); @@ -974,11 +1004,12 @@ mod tests { } /// Test that the ensured function returns the expected un-ensured value. - fn test_ensure(pairs: Vec<(V, V)>, ensured: E, unensured: P) + fn test_ensure(pairs: Vec<(V, W)>, ensured: E, unensured: P) where V: Ensure + core::fmt::Debug + Copy, - E: Fn(V, V) -> Result, - P: Fn(&V, &V) -> Option, + W: Ensure + core::fmt::Debug + Copy, + E: Fn(V, W) -> Result, + P: Fn(&V, &W) -> Option, { for (a, b) in pairs.into_iter() { match ensured(a, b) { @@ -993,11 +1024,12 @@ mod tests { } /// Test that the ensured function modifies `self` to the expected un-ensured value. - fn test_ensure_assign(pairs: Vec<(V, V)>, ensured: E, unensured: P) + fn test_ensure_assign(pairs: Vec<(V, W)>, ensured: E, unensured: P) where V: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy, - E: Fn(&mut V, V) -> Result<(), ArithmeticError>, - P: Fn(V, V) -> Result + std::panic::RefUnwindSafe, + W: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy, + E: Fn(&mut V, W) -> Result<(), ArithmeticError>, + P: Fn(V, W) -> Result + std::panic::RefUnwindSafe, { for (mut a, b) in pairs.into_iter() { let old_a = a; diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 8978cdb11c0c6..6eb19683b4439 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -32,11 +32,11 @@ use impl_trait_for_tuples::impl_for_tuples; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use sp_application_crypto::AppKey; pub use sp_arithmetic::traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedShl, - CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign, - EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp, - EnsureOpAssign, EnsureSub, EnsureSubAssign, IntegerSquareRoot, One, SaturatedConversion, - Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, + checked_pow, ensure_pow, AtLeast32Bit, AtLeast32BitUnsigned, Bounded, CheckedAdd, CheckedDiv, + CheckedMul, CheckedShl, CheckedShr, CheckedSub, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, + EnsureDivAssign, EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, + EnsureOp, EnsureOpAssign, EnsureSub, EnsureSubAssign, IntegerSquareRoot, One, + SaturatedConversion, Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, }; use sp_core::{self, storage::StateVersion, Hasher, RuntimeDebug, TypeId}; #[doc(hidden)] From db7a1c250e37b4586634b76abae2110b007ca754 Mon Sep 17 00:00:00 2001 From: alexgparity <115470171+alexgparity@users.noreply.github.com> Date: Tue, 10 Jan 2023 12:32:19 +0100 Subject: [PATCH 212/221] Add solution hint to error message (#13111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add solution hint to error message * make it compile * Update frame/support/procedural/src/pallet/expand/hooks.rs Co-authored-by: Bastian Köcher * Update frame/support/procedural/src/pallet/expand/storage.rs Co-authored-by: Bastian Köcher * Update frame/support/procedural/src/pallet/expand/storage.rs Co-authored-by: Bastian Köcher * Update frame/support/procedural/src/pallet/expand/storage.rs Co-authored-by: Bastian Köcher Co-authored-by: Bastian Köcher --- frame/support/procedural/src/pallet/expand/hooks.rs | 2 +- frame/support/procedural/src/pallet/expand/storage.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/support/procedural/src/pallet/expand/hooks.rs b/frame/support/procedural/src/pallet/expand/hooks.rs index 0aa7c1e7aaf06..c2b70c5632f94 100644 --- a/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/frame/support/procedural/src/pallet/expand/hooks.rs @@ -63,7 +63,7 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo - >::name::().expect("Every active pallet has a name in the runtime; qed"); + >::name::().expect("No name found for the pallet! This usually means that the pallet wasn't added to `construct_runtime!`."); #frame_support::log::debug!( target: #frame_support::LOG_TARGET, "🩺 try-state pallet {:?}", diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 181f35b545496..195a62431f279 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -535,7 +535,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::>() - .expect("Every active pallet has a name in the runtime; qed") + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") } const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const; } @@ -569,7 +569,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::>() - .expect("Every active pallet has a name in the runtime; qed") + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") } const STORAGE_PREFIX: &'static str = #prefix_struct_const; } @@ -648,7 +648,7 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ::PalletInfo as #frame_support::traits::PalletInfo >::name::<#pallet_ident<#type_use_gen>>() - .expect("Every active pallet has a name in the runtime; qed"), + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), entries: { #[allow(unused_mut)] let mut entries = #frame_support::sp_std::vec![]; From 1a29e8154065819971367d2b7b764c1db61ee56f Mon Sep 17 00:00:00 2001 From: JP <77391175+joao-paulo-parity@users.noreply.github.com> Date: Tue, 10 Jan 2023 09:01:44 -0300 Subject: [PATCH 213/221] Avoid unintentionally canceling the scheduled crate publishing job (#13088) * avoid unintentionally canceling the scheduled crate publishing job because publish-crates and publish-crates-manual share the resource group "crates-publishing", any instance of publish-crates-manual cancels a running instance of publish-crates, as demonstrated by https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2212179 a workaround for that unintended interaction is to avoid creating instances of publish-crates-manual and instead require pipelines to be triggered manually by checking $CI_JOB_MANUAL == "true" * check manual pipelines by $CI_PIPELINE_SOURCE instead of $CI_JOB_MANUAL * make crate-publishing pipelines uninterruptible * use conditional includes to work around interruptible limitations * organize comments * remove interruptible from common pipeline * wip: check include * wip: check include * fix include * fix include * fix include * fix yaml * fix yaml * remove shared common-pipeline * wip: retry common-pipeline * move .default-template to .gitlab-ci.yml * fix the pipeline add comments * fix default-pipeline.yml * revert publish-crates-manual to when: manual * move "needs:" back to publish-crates * avoid manual repetition * improve previous commit * try to avoid manual repetition * fix indentation * minor adjustments * move defaults to top of .gitlab-ci.yml * fix positioning on default in the diff * comments * indentation * Apply suggestions from code review Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> Co-authored-by: Alexander Samusev <41779041+alvicsam@users.noreply.github.com> --- .gitlab-ci.yml | 42 +++++++++++++++++-- .../ci/gitlab/crate-publishing-pipeline.yml | 1 + scripts/ci/gitlab/default-pipeline.yml | 1 + scripts/ci/gitlab/pipeline/publish.yml | 3 +- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 scripts/ci/gitlab/crate-publishing-pipeline.yml create mode 100644 scripts/ci/gitlab/default-pipeline.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f6347e51e62c5..ecc27d016a811 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,16 +57,30 @@ variables: NEXTEST_SUCCESS_OUTPUT: final ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.22" -default: +.shared-default: &shared-default retry: max: 2 when: - runner_system_failure - unknown_failure - api_failure - interruptible: true cache: {} +.default-pipeline-definitions: + default: + <<: *shared-default + interruptible: true + +.crate-publishing-pipeline-definitions: + default: + <<: *shared-default + # The crate-publishing pipeline defaults to `interruptible: false` so that we'll be able to + # reach and run the publishing jobs despite the "Auto-cancel redundant pipelines" CI setting. + # The setting is relevant because the crate-publishing pipeline runs on `master`, thus future + # pipelines on `master` (e.g. created for new commits or other schedules) might unintendedly + # cancel the publishing jobs or its dependencies before we get to actually publish the crates. + interruptible: false + .collect-artifacts: artifacts: name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" @@ -215,9 +229,15 @@ default: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" +.crate-publishing-pipeline: + rules: + - if: $CI_COMMIT_REF_NAME != "master" + when: never + .scheduled-crate-publishing-pipeline: rules: - - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" + - !reference [.crate-publishing-pipeline, rules] + - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" .crates-publishing-template: stage: test @@ -265,6 +285,22 @@ include: - scripts/ci/gitlab/pipeline/publish.yml # zombienet jobs - scripts/ci/gitlab/pipeline/zombienet.yml + # The crate-publishing pipeline requires a customized `interruptible` configuration. Unfortunately + # `interruptible` can't currently be dynamically set based on variables as per: + # - https://gitlab.com/gitlab-org/gitlab/-/issues/38349 + # - https://gitlab.com/gitlab-org/gitlab/-/issues/194023 + # Thus we work around that limitation by using conditional includes. + # For crate-publishing pipelines: run it with defaults + `interruptible: false`. The WHOLE + # pipeline is made uninterruptible to ensure that test jobs also get a chance to run to + # completion, because the publishing jobs depends on them AS INTENDED: crates should not be + # published before their source code is checked. + - local: scripts/ci/gitlab/crate-publishing-pipeline.yml + rules: + - if: $PIPELINE == "automatic-crate-publishing" + # For normal pipelines: run it with defaults + `interruptible: true` + - local: scripts/ci/gitlab/default-pipeline.yml + rules: + - if: $PIPELINE != "automatic-crate-publishing" #### stage: deploy diff --git a/scripts/ci/gitlab/crate-publishing-pipeline.yml b/scripts/ci/gitlab/crate-publishing-pipeline.yml new file mode 100644 index 0000000000000..9d5303952e6ef --- /dev/null +++ b/scripts/ci/gitlab/crate-publishing-pipeline.yml @@ -0,0 +1 @@ +default: !reference [.crate-publishing-pipeline-definitions, default] diff --git a/scripts/ci/gitlab/default-pipeline.yml b/scripts/ci/gitlab/default-pipeline.yml new file mode 100644 index 0000000000000..19f6c320c3c24 --- /dev/null +++ b/scripts/ci/gitlab/default-pipeline.yml @@ -0,0 +1 @@ +default: !reference [.default-pipeline-definitions, default] diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index cc7451a9fbb22..dff1b508625fb 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -214,7 +214,7 @@ update-node-template: timeout: 9h # A custom publishing environment is used for us to be able to set up protected secrets # specifically for it - environment: publish-crates + environment: publish-crates script: - rusty-cachier snapshot create - git clone @@ -234,5 +234,6 @@ publish-crates: publish-crates-manual: extends: .publish-crates-template + rules: !reference [.crate-publishing-pipeline, rules] when: manual allow_failure: true From fc43b5f3ead3464b0e55b23f2ba99682e6a060ae Mon Sep 17 00:00:00 2001 From: Anthony Alaribe Date: Tue, 10 Jan 2023 20:18:12 +0100 Subject: [PATCH 214/221] introduce log-target constant to more frame pallets (#13116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * introduce log-target constant to more frame pallets * cargo fmt * make LOG_TARGET in session public * Update frame/elections-phragmen/src/lib.rs Co-authored-by: Bastian Köcher * Update frame/elections-phragmen/src/migrations/v3.rs Co-authored-by: Bastian Köcher * Update frame/elections-phragmen/src/migrations/v3.rs Co-authored-by: Bastian Köcher * Update frame/elections-phragmen/src/migrations/v3.rs Co-authored-by: Bastian Köcher * move LOG_TARGET=runtime::session_historical to migrations module, where it's actually used Co-authored-by: Bastian Köcher --- frame/assets/src/lib.rs | 1 + frame/assets/src/migration.rs | 12 +++++++-- frame/balances/src/lib.rs | 6 +++-- frame/balances/src/migration.rs | 14 ++++++++--- frame/collective/src/lib.rs | 8 +++--- frame/collective/src/migrations/v4.rs | 9 ++++--- frame/elections-phragmen/src/lib.rs | 15 ++++------- frame/elections-phragmen/src/migrations/v3.rs | 25 ++++++------------- frame/elections-phragmen/src/migrations/v4.rs | 7 +++--- frame/membership/src/lib.rs | 2 ++ frame/membership/src/migrations/v4.rs | 9 ++++--- frame/session/src/migrations/v1.rs | 10 +++++--- frame/system/src/lib.rs | 16 ++++++------ frame/system/src/migrations/mod.rs | 7 +++--- frame/tips/src/lib.rs | 2 ++ frame/tips/src/migrations/v4.rs | 9 ++++--- frame/uniques/src/lib.rs | 2 ++ frame/uniques/src/migration.rs | 6 ++--- 18 files changed, 89 insertions(+), 71 deletions(-) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 1853ef75f52be..93e416c3bec4c 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -175,6 +175,7 @@ pub use pallet::*; pub use weights::WeightInfo; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +const LOG_TARGET: &str = "runtime::assets"; /// Trait with callbacks that are executed after successfull asset creation or destruction. pub trait AssetsCallback { diff --git a/frame/assets/src/migration.rs b/frame/assets/src/migration.rs index 89f8d39a9049c..9f8905ceff6c4 100644 --- a/frame/assets/src/migration.rs +++ b/frame/assets/src/migration.rs @@ -75,10 +75,18 @@ pub mod v1 { Some(old_value.migrate_to_v1()) }); current_version.put::>(); - log::info!(target: "runtime::assets", "Upgraded {} pools, storage to version {:?}", translated, current_version); + log::info!( + target: LOG_TARGET, + "Upgraded {} pools, storage to version {:?}", + translated, + current_version + ); T::DbWeight::get().reads_writes(translated + 1, translated + 1) } else { - log::info!(target: "runtime::assets", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index 57f76b1ff679d..99d77a3e73361 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -193,6 +193,8 @@ pub use weights::WeightInfo; pub use pallet::*; +const LOG_TARGET: &str = "runtime::balances"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] @@ -950,7 +952,7 @@ impl, I: 'static> Pallet { if locks.len() as u32 > T::MaxLocks::get() { log::warn!( - target: "runtime::balances", + target: LOG_TARGET, "Warning: A user has more currency locks than expected. \ A runtime configuration adjustment may be needed." ); @@ -985,7 +987,7 @@ impl, I: 'static> Pallet { // since the funds that are under the lock will themselves be stored in the // account and therefore will need a reference. log::warn!( - target: "runtime::balances", + target: LOG_TARGET, "Warning: Attempt to introduce lock consumer reference, yet no providers. \ This is unexpected but should be safe." ); diff --git a/frame/balances/src/migration.rs b/frame/balances/src/migration.rs index b660ec9fd3235..80d1bcd35ba9e 100644 --- a/frame/balances/src/migration.rs +++ b/frame/balances/src/migration.rs @@ -40,10 +40,13 @@ fn migrate_v0_to_v1, I: 'static>(accounts: &[T::AccountId]) -> Weig // Set storage version to `1`. StorageVersion::new(1).put::>(); - log::info!(target: "runtime::balances", "Storage to version 1"); + log::info!(target: LOG_TARGET, "Storage to version 1"); T::DbWeight::get().reads_writes(2 + accounts.len() as u64, 3) } else { - log::info!(target: "runtime::balances", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } @@ -87,10 +90,13 @@ impl, I: 'static> OnRuntimeUpgrade for ResetInactive { // Set storage version to `0`. StorageVersion::new(0).put::>(); - log::info!(target: "runtime::balances", "Storage to version 0"); + log::info!(target: LOG_TARGET, "Storage to version 0"); T::DbWeight::get().reads_writes(1, 2) } else { - log::info!(target: "runtime::balances", "Migration did not execute. This probably should be removed"); + log::info!( + target: LOG_TARGET, + "Migration did not execute. This probably should be removed" + ); T::DbWeight::get().reads(1) } } diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index c522b71891b3c..0fe05dbb01ac0 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -71,6 +71,8 @@ pub mod weights; pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::collective"; + /// Simple index type for proposal counting. pub type ProposalIndex = u32; @@ -390,7 +392,7 @@ pub mod pallet { ensure_root(origin)?; if new_members.len() > T::MaxMembers::get() as usize { log::error!( - target: "runtime::collective", + target: LOG_TARGET, "New members count ({}) exceeds maximum amount of members expected ({}).", new_members.len(), T::MaxMembers::get(), @@ -400,7 +402,7 @@ pub mod pallet { let old = Members::::get(); if old.len() > old_count as usize { log::warn!( - target: "runtime::collective", + target: LOG_TARGET, "Wrong count used to estimate set_members weight. expected ({}) vs actual ({})", old_count, old.len(), @@ -1040,7 +1042,7 @@ impl, I: 'static> ChangeMembers for Pallet { ) { if new.len() > T::MaxMembers::get() as usize { log::error!( - target: "runtime::collective", + target: LOG_TARGET, "New members count ({}) exceeds maximum amount of members expected ({}).", new.len(), T::MaxMembers::get(), diff --git a/frame/collective/src/migrations/v4.rs b/frame/collective/src/migrations/v4.rs index 483c3f9fa9e69..2756b3fd15a6b 100644 --- a/frame/collective/src/migrations/v4.rs +++ b/frame/collective/src/migrations/v4.rs @@ -17,6 +17,7 @@ use sp_io::hashing::twox_128; +use super::super::LOG_TARGET; use frame_support::{ traits::{ Get, GetStorageVersion, PalletInfoAccess, StorageVersion, @@ -42,7 +43,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::collective", + target: LOG_TARGET, "Running migration to v4 for collective with storage version {:?}", on_chain_storage_version, ); @@ -66,7 +67,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::collective", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -138,7 +139,7 @@ pub fn post_migrate>(old_ fn log_migration(stage: &str, old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::collective", + target: LOG_TARGET, "{}, prefix: '{}' ==> '{}'", stage, old_pallet_name, diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 1cfdc25fd9b47..1a020adb28632 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -122,6 +122,8 @@ pub use weights::WeightInfo; /// All migrations. pub mod migrations; +const LOG_TARGET: &str = "runtime::elections-phragmen"; + /// The maximum votes allowed per voter. pub const MAXIMUM_VOTE: usize = 16; @@ -789,10 +791,7 @@ impl Pallet { } else { // overlap. This can never happen. If so, it seems like our intended replacement // is already a member, so not much more to do. - log::error!( - target: "runtime::elections-phragmen", - "A member seems to also be a runner-up.", - ); + log::error!(target: LOG_TARGET, "A member seems to also be a runner-up."); } next_best }); @@ -939,7 +938,7 @@ impl Pallet { Ok(_) => (), Err(_) => { log::error!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Failed to run election. Number of voters exceeded", ); Self::deposit_event(Event::ElectionError); @@ -1103,11 +1102,7 @@ impl Pallet { >::mutate(|v| *v += 1); }) .map_err(|e| { - log::error!( - target: "runtime::elections-phragmen", - "Failed to run election [{:?}].", - e, - ); + log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,); Self::deposit_event(Event::ElectionError); }); diff --git a/frame/elections-phragmen/src/migrations/v3.rs b/frame/elections-phragmen/src/migrations/v3.rs index 9ec9c6e7eea6c..e48cd6c1a8056 100644 --- a/frame/elections-phragmen/src/migrations/v3.rs +++ b/frame/elections-phragmen/src/migrations/v3.rs @@ -17,6 +17,7 @@ //! Migrations to version [`3.0.0`], as denoted by the changelog. +use super::super::LOG_TARGET; use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ @@ -88,7 +89,7 @@ pub fn apply( ) -> Weight { let storage_version = StorageVersion::get::>(); log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Running migration for elections-phragmen with storage version {:?}", storage_version, ); @@ -104,7 +105,7 @@ pub fn apply( Weight::MAX } else { log::warn!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Attempted to apply migration to V3 but failed because storage version is {:?}", storage_version, ); @@ -118,22 +119,14 @@ pub fn migrate_voters_to_recorded_deposit(old_deposit: V:: Some(Voter { votes, stake, deposit: old_deposit }) }); - log::info!( - target: "runtime::elections-phragmen", - "migrated {} voter accounts.", - >::iter().count(), - ); + log::info!(target: LOG_TARGET, "migrated {} voter accounts.", >::iter().count()); } /// Migrate all candidates to recorded deposit. pub fn migrate_candidates_to_recorded_deposit(old_deposit: V::Balance) { let _ = >::translate::, _>(|maybe_old_candidates| { maybe_old_candidates.map(|old_candidates| { - log::info!( - target: "runtime::elections-phragmen", - "migrated {} candidate accounts.", - old_candidates.len(), - ); + log::info!(target: LOG_TARGET, "migrated {} candidate accounts.", old_candidates.len()); old_candidates.into_iter().map(|c| (c, old_deposit)).collect::>() }) }); @@ -143,11 +136,7 @@ pub fn migrate_candidates_to_recorded_deposit(old_deposit: pub fn migrate_members_to_recorded_deposit(old_deposit: V::Balance) { let _ = >::translate::, _>(|maybe_old_members| { maybe_old_members.map(|old_members| { - log::info!( - target: "runtime::elections-phragmen", - "migrated {} member accounts.", - old_members.len(), - ); + log::info!(target: LOG_TARGET, "migrated {} member accounts.", old_members.len()); old_members .into_iter() .map(|(who, stake)| SeatHolder { who, stake, deposit: old_deposit }) @@ -162,7 +151,7 @@ pub fn migrate_runners_up_to_recorded_deposit(old_deposit: |maybe_old_runners_up| { maybe_old_runners_up.map(|old_runners_up| { log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "migrated {} runner-up accounts.", old_runners_up.len(), ); diff --git a/frame/elections-phragmen/src/migrations/v4.rs b/frame/elections-phragmen/src/migrations/v4.rs index 76ef630706c50..535a9fb8201d7 100644 --- a/frame/elections-phragmen/src/migrations/v4.rs +++ b/frame/elections-phragmen/src/migrations/v4.rs @@ -17,6 +17,7 @@ //! Migrations to version [`4.0.0`], as denoted by the changelog. +use super::super::LOG_TARGET; use frame_support::{ traits::{Get, StorageVersion}, weights::Weight, @@ -35,14 +36,14 @@ pub const OLD_PREFIX: &[u8] = b"PhragmenElection"; pub fn migrate>(new_pallet_name: N) -> Weight { if new_pallet_name.as_ref().as_bytes() == OLD_PREFIX { log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "New pallet name is equal to the old prefix. No migration needs to be done.", ); return Weight::zero() } let storage_version = StorageVersion::get::>(); log::info!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Running migration to v4 for elections-phragmen with storage version {:?}", storage_version, ); @@ -59,7 +60,7 @@ pub fn migrate>(new_pallet_name: N) -> Weight { ::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::elections-phragmen", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", storage_version, ); diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 8531af120ec88..934abb996df80 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -36,6 +36,8 @@ pub mod weights; pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::membership"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] diff --git a/frame/membership/src/migrations/v4.rs b/frame/membership/src/migrations/v4.rs index 5b8735aa2bac9..cbc58cd6ae917 100644 --- a/frame/membership/src/migrations/v4.rs +++ b/frame/membership/src/migrations/v4.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::super::LOG_TARGET; use sp_io::hashing::twox_128; use frame_support::{ @@ -43,7 +44,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::membership", + target: LOG_TARGET, "Running migration to v4 for membership with storage version {:?}", on_chain_storage_version, ); @@ -67,7 +68,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::membership", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -139,7 +140,7 @@ pub fn post_migrate>(old_pallet_name: N, new fn log_migration(stage: &str, old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::membership", + target: LOG_TARGET, "{}, prefix: '{}' ==> '{}'", stage, old_pallet_name, diff --git a/frame/session/src/migrations/v1.rs b/frame/session/src/migrations/v1.rs index c0dce422fe8b5..6689ca7299f1e 100644 --- a/frame/session/src/migrations/v1.rs +++ b/frame/session/src/migrations/v1.rs @@ -29,6 +29,8 @@ use frame_support::{ use crate::historical as pallet_session_historical; +const LOG_TARGET: &str = "runtime::session_historical"; + const OLD_PREFIX: &str = "Session"; /// Migrate the entire storage of this pallet to a new prefix. @@ -44,7 +46,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::session_historical", + target: LOG_TARGET, "Running migration to v1 for session_historical with storage version {:?}", on_chain_storage_version, ); @@ -78,7 +80,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::session_historical", + target: LOG_TARGET, "Attempted to apply migration to v1 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -184,7 +186,7 @@ pub fn post_migrate< fn log_migration(stage: &str, storage_prefix: &[u8], old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::session_historical", + target: LOG_TARGET, "{} prefix of storage '{}': '{}' ==> '{}'", stage, str::from_utf8(storage_prefix).unwrap_or(""), diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index c790d33adddd7..3909b1e9c5257 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -129,6 +129,8 @@ pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::system"; + /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with @@ -1075,7 +1077,7 @@ impl Pallet { if account.providers == 0 { // Logic error - cannot decrement beyond zero. log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing provider", ); account.providers = 1; @@ -1101,7 +1103,7 @@ impl Pallet { } } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Account already dead when reducing provider", ); Ok(DecRefStatus::Reaped) @@ -1133,7 +1135,7 @@ impl Pallet { if account.sufficients == 0 { // Logic error - cannot decrement beyond zero. log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing sufficients", ); } @@ -1150,7 +1152,7 @@ impl Pallet { } } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Account already dead when reducing provider", ); DecRefStatus::Reaped @@ -1215,7 +1217,7 @@ impl Pallet { a.consumers -= 1; } else { log::error!( - target: "runtime::system", + target: LOG_TARGET, "Logic error: Unexpected underflow in reducing consumer", ); } @@ -1337,7 +1339,7 @@ impl Pallet { /// resulting header for this block. pub fn finalize() -> T::Header { log::debug!( - target: "runtime::system", + target: LOG_TARGET, "[{:?}] {} extrinsics, length: {} (normal {}%, op: {}%, mandatory {}%) / normal weight:\ {} ({}%) op weight {} ({}%) / mandatory weight {} ({}%)", Self::block_number(), @@ -1547,7 +1549,7 @@ impl Pallet { Ok(_) => Event::ExtrinsicSuccess { dispatch_info: info }, Err(err) => { log::trace!( - target: "runtime::system", + target: LOG_TARGET, "Extrinsic failed at block({:?}): {:?}", Self::block_number(), err, diff --git a/frame/system/src/migrations/mod.rs b/frame/system/src/migrations/mod.rs index 15746d7376ac5..90b88de1ab11c 100644 --- a/frame/system/src/migrations/mod.rs +++ b/frame/system/src/migrations/mod.rs @@ -17,6 +17,7 @@ //! Migrate the reference counting state. +use super::LOG_TARGET; use crate::{Config, Pallet}; use codec::{Decode, Encode, FullCodec}; use frame_support::{ @@ -75,7 +76,7 @@ pub fn migrate_from_single_u8_to_triple_ref_count() -> Wei Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data }) }); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from single u8 to triple reference counting to {:?} elements.", translated ); @@ -94,7 +95,7 @@ pub fn migrate_from_single_to_triple_ref_count() -> Weight }, ); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from single to triple reference counting to {:?} elements.", translated ); @@ -112,7 +113,7 @@ pub fn migrate_from_dual_to_triple_ref_count() -> Weight { }, ); log::info!( - target: "runtime::system", + target: LOG_TARGET, "Applied migration from dual to triple reference counting to {:?} elements.", translated ); diff --git a/frame/tips/src/lib.rs b/frame/tips/src/lib.rs index dd9ebc9813233..43002c7b5f196 100644 --- a/frame/tips/src/lib.rs +++ b/frame/tips/src/lib.rs @@ -78,6 +78,8 @@ use frame_support::{ pub use pallet::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::tips"; + pub type BalanceOf = pallet_treasury::BalanceOf; pub type NegativeImbalanceOf = pallet_treasury::NegativeImbalanceOf; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; diff --git a/frame/tips/src/migrations/v4.rs b/frame/tips/src/migrations/v4.rs index 5e10fa7dd2c6d..0107e96a41168 100644 --- a/frame/tips/src/migrations/v4.rs +++ b/frame/tips/src/migrations/v4.rs @@ -18,6 +18,7 @@ use sp_io::hashing::twox_128; use sp_std::str; +use super::super::LOG_TARGET; use frame_support::{ storage::StoragePrefixedMap, traits::{ @@ -46,7 +47,7 @@ pub fn migrate::on_chain_storage_version(); log::info!( - target: "runtime::tips", + target: LOG_TARGET, "Running migration to v4 for tips with storage version {:?}", on_chain_storage_version, ); @@ -80,7 +81,7 @@ pub fn migrate::BlockWeights::get().max_block } else { log::warn!( - target: "runtime::tips", + target: LOG_TARGET, "Attempted to apply migration to v4 but failed because storage version is {:?}", on_chain_storage_version, ); @@ -185,7 +186,7 @@ pub fn post_migrate< fn log_migration(stage: &str, storage_prefix: &[u8], old_pallet_name: &str, new_pallet_name: &str) { log::info!( - target: "runtime::tips", + target: LOG_TARGET, "{} prefix of storage '{}': '{}' ==> '{}'", stage, str::from_utf8(storage_prefix).unwrap_or(""), diff --git a/frame/uniques/src/lib.rs b/frame/uniques/src/lib.rs index 6b21477be4340..da0f0e0560674 100644 --- a/frame/uniques/src/lib.rs +++ b/frame/uniques/src/lib.rs @@ -60,6 +60,8 @@ pub use pallet::*; pub use types::*; pub use weights::WeightInfo; +const LOG_TARGET: &str = "runtime::uniques"; + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; #[frame_support::pallet] diff --git a/frame/uniques/src/migration.rs b/frame/uniques/src/migration.rs index 8a2a0ef808d90..fd98c030d7f1b 100644 --- a/frame/uniques/src/migration.rs +++ b/frame/uniques/src/migration.rs @@ -24,7 +24,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo ) -> frame_support::weights::Weight { let on_chain_storage_version =

::on_chain_storage_version(); log::info!( - target: "runtime::uniques", + target: LOG_TARGET, "Running migration storage v1 for uniques with storage version {:?}", on_chain_storage_version, ); @@ -37,7 +37,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo } StorageVersion::new(1).put::

(); log::info!( - target: "runtime::uniques", + target: LOG_TARGET, "Running migration storage v1 for uniques with storage version {:?} was complete", on_chain_storage_version, ); @@ -45,7 +45,7 @@ pub fn migrate_to_v1, I: 'static, P: GetStorageVersion + PalletInfo T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1) } else { log::warn!( - target: "runtime::uniques", + target: LOG_TARGET, "Attempted to apply migration to v1 but failed because storage version is {:?}", on_chain_storage_version, ); From 874e3e22ae10d23112fd89801fc9ce652bfb0678 Mon Sep 17 00:00:00 2001 From: Vladimir Istyufeev Date: Wed, 11 Jan 2023 01:08:28 +0400 Subject: [PATCH 215/221] Update tests for 1.66 and switch CI to stable Rust 1.66 (#13118) * Update UI tests for 1.66 * Fix `test_enum` assertion for Rust 1.66 * Fix another `test_enum` assertion for Rust 1.66 * Fix another `test_enum` assertion for Rust 1.66 * Fix another `test_enum` assertion for Rust 1.66 --- .gitlab-ci.yml | 2 +- .../construct_runtime_ui/invalid_module_details.stderr | 4 ++-- frame/support/test/tests/derive_no_bound.rs | 8 ++++---- .../test/tests/pallet_ui/call_invalid_return.stderr | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecc27d016a811..90298297a89c6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ variables: CARGO_INCREMENTAL: 0 DOCKER_OS: "debian:stretch" ARCH: "x86_64" - CI_IMAGE: "paritytech/ci-linux:production" + CI_IMAGE: "paritytech/ci-linux@sha256:9140bc3c843a8b12a3bcf6f5886346536092795bbadfd7f1836362cb28dfcc71" BUILDAH_IMAGE: "quay.io/buildah/stable:v1.27" RUSTY_CACHIER_SINGLE_BRANCH: master RUSTY_CACHIER_DONT_OPERATE_ON_MAIN_BRANCH: "true" diff --git a/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr b/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr index db96b8749ca11..0a20cf4e39a88 100644 --- a/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr +++ b/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr @@ -1,5 +1,5 @@ error: Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,` - --> $DIR/invalid_module_details.rs:9:17 + --> tests/construct_runtime_ui/invalid_module_details.rs:9:17 | 9 | system: System::(), - | ^^ + | ^ diff --git a/frame/support/test/tests/derive_no_bound.rs b/frame/support/test/tests/derive_no_bound.rs index f891b3a2d2db8..9162b5013bcdc 100644 --- a/frame/support/test/tests/derive_no_bound.rs +++ b/frame/support/test/tests/derive_no_bound.rs @@ -72,7 +72,7 @@ fn test_struct_named() { assert_eq!(a_2, a_1); assert_eq!( format!("{:?}", a_1), - String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData }") + String::from("StructNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }") ); let b = StructNamed:: { @@ -103,7 +103,7 @@ fn test_struct_unnamed() { assert_eq!(a_2.1, 2); assert_eq!(a_2.2, 3); assert_eq!(a_2, a_1); - assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData)")); + assert_eq!(format!("{:?}", a_1), String::from("StructUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)")); let b = StructUnnamed::(1, 2, 4, Default::default()); @@ -211,11 +211,11 @@ fn test_enum() { assert_eq!( format!("{:?}", variant_0), - String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData)"), + String::from("Enum::VariantUnnamed(1, 2, 3, PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)>)"), ); assert_eq!( format!("{:?}", variant_1), - String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData }"), + String::from("Enum::VariantNamed { a: 1, b: 2, c: 3, phantom: PhantomData<(derive_no_bound::ImplNone, derive_no_bound::ImplNone)> }"), ); assert_eq!(format!("{:?}", variant_2), String::from("Enum::VariantUnit")); assert_eq!(format!("{:?}", variant_3), String::from("Enum::VariantUnit2")); diff --git a/frame/support/test/tests/pallet_ui/call_invalid_return.stderr b/frame/support/test/tests/pallet_ui/call_invalid_return.stderr index 6a851ed3fc283..8803bbba01326 100644 --- a/frame/support/test/tests/pallet_ui/call_invalid_return.stderr +++ b/frame/support/test/tests/pallet_ui/call_invalid_return.stderr @@ -1,5 +1,5 @@ error: expected `DispatchResultWithPostInfo` or `DispatchResult` - --> $DIR/call_invalid_return.rs:17:39 + --> tests/pallet_ui/call_invalid_return.rs:17:39 | 17 | pub fn foo(origin: OriginFor) -> ::DispatchResult { todo!() } - | ^^ + | ^ From f39c290dbf6c0cbc59d15647e463f172a6a183bb Mon Sep 17 00:00:00 2001 From: JP <77391175+joao-paulo-parity@users.noreply.github.com> Date: Wed, 11 Jan 2023 06:03:24 -0300 Subject: [PATCH 216/221] remove update-node-template (#13100) will be moved to polkadot's pipeline ref https://github.com/paritytech/release-engineering/issues/142 --- scripts/ci/gitlab/pipeline/publish.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index dff1b508625fb..d1a7514d1707b 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -182,20 +182,6 @@ publish-draft-release: - ./scripts/ci/gitlab/publish_draft_release.sh allow_failure: true -# Ref: https://github.com/paritytech/opstooling/issues/111 -update-node-template: - stage: publish - extends: .kubernetes-env - rules: - - if: $CI_COMMIT_REF_NAME =~ /^polkadot-v[0-9]+\.[0-9]+.*$/ # i.e. polkadot-v1.0.99, polkadot-v2.1rc1 - script: - - git clone --depth=1 --branch="$PIPELINE_SCRIPTS_TAG" https://github.com/paritytech/pipeline-scripts - - ./pipeline-scripts/update_substrate_template.sh - --repo-name "substrate-node-template" - --template-path "bin/node-template" - --github-api-token "$GITHUB_TOKEN" - --polkadot-branch "$CI_COMMIT_REF_NAME" - .publish-crates-template: stage: publish extends: .crates-publishing-template From 6f9d9b1c433524feb0d64d3d7e50eeb3e3ff0111 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 11 Jan 2023 16:59:12 +0700 Subject: [PATCH 217/221] simplify throttling to 1 increment per interval --- frame/nomination-pools/src/lib.rs | 64 ++++++++++--------------- frame/nomination-pools/src/tests.rs | 74 +---------------------------- 2 files changed, 26 insertions(+), 112 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 20e71aaacc2b8..1da604c5d44f7 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -334,8 +334,8 @@ use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::{ traits::{ - AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, SaturatedConversion, - Saturating, StaticLookup, Zero, + AccountIdConversion, Bounded, CheckedAdd, CheckedSub, Convert, Saturating, StaticLookup, + Zero, }, FixedPointNumber, Perbill, }; @@ -630,47 +630,31 @@ impl Commission { if *to <= commission_as_percent { return false } + // Test for `max_increase` throttling. + // + // The attempted increase in commission relative to the current commission if throttled if + // greater than `max_increase`. + if (*to).saturating_sub(commission_as_percent) > t.max_increase { + return true + } - // the attempted increase in commission relative to the current commission. - let attempted_increase = (*to).saturating_sub(commission_as_percent); - - if self.throttle_from.map_or( - // Note: defensive only. `throttle_from` should always exist where `change_rate` - // has already been set, so this scenario should never happen. - // - // if no `throttle_from` exists. Therefore, the commission update is throttled via - // `max_increase` only. - attempted_increase > t.max_increase, - |f| { - // ensure the `max_increase` durations passed since the previous commission - // update allow the attempted commission increase. - // - // the total durations passed since the last commission update. - let blocks_passed = >::block_number().saturating_sub(f); - - // `min_delay` blocks must have been passed since `throttle_from`. - if blocks_passed < t.min_delay { - return true - } - - // calculate intervals passed since `throttle_from`. If min delay is set to 0, - // set intervals passed to 1 as to only allow 1 * max_increase. - let intervals_passed = if t.min_delay == Zero::zero() { - 1_u32 + // Test for `min_delay` throttling. + // + // Note: matching `None` is defensive only. `throttle_from` should always exist where + // `change_rate` has already been set, so this scenario should never happen. + return match self.throttle_from { + Some(f) => { + // if `min_delay` is zero (no delay), not throttling. + if t.min_delay == Zero::zero() { + false } else { - blocks_passed.div(t.min_delay).saturated_into::() - }; - - // calculate maximum allowed increase, where `max_increase` is converted into a - // u32 by multiplying itself with 100_u32, then multiplied by intervals passed, - // before being converted back into a Perbill. - let max_allowed_increase = - Perbill::from_percent((t.max_increase * 100_u32) * intervals_passed); - - attempted_increase > max_allowed_increase + // throttling if blocks passed is less than `min_delay`. + let blocks_surpassed = + >::block_number().saturating_sub(f); + blocks_surpassed < t.min_delay + } }, - ) { - return true + _ => false, } } false diff --git a/frame/nomination-pools/src/tests.rs b/frame/nomination-pools/src/tests.rs index fb2c62702cb1d..4bd4973fc7704 100644 --- a/frame/nomination-pools/src/tests.rs +++ b/frame/nomination-pools/src/tests.rs @@ -5654,7 +5654,7 @@ mod commission { } #[test] - fn set_commission_change_rate_works_with_error_tests() { + fn set_commission_change_rate_works_with_errors() { ExtBuilder::default().build_and_execute(|| { // Provided pool does not exist assert_noop!( @@ -5744,61 +5744,6 @@ mod commission { CommissionChangeRate { max_increase: Perbill::from_percent(3), min_delay: 30_u64 } )); - // multi duration `min_delay` test. - // - // set the commission change_rate of 1% per 30 blocks. - assert_ok!(Pools::set_commission_change_rate( - RuntimeOrigin::signed(900), - 1, - CommissionChangeRate { max_increase: Perbill::from_percent(1), min_delay: 30_u64 } - )); - - // sanity check: the current pool Commission. - assert_eq!( - BondedPools::::get(1).unwrap().commission, - Commission { - current: None, - max: None, - change_rate: Some(CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 30_u64 - }), - throttle_from: Some(1), - } - ); - - // run `min_delay` blocks so commission can be set again. - run_blocks(30_u64); - - // pre-requisite: set the commission to 1%. - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(1), 900)), - )); - - // Run 90 blocks into the future so we are eligible to update commission - // with 3 `min_delay` durations passed. - run_blocks(91); - - // we should not be able to increase the commission to 5%: 1% beyond the change rate - // limit. - assert_noop!( - Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(5), 900)) - ), - Error::::CommissionChangeThrottled - ); - - // we should however be able to increase the commission to 4%: 1% + (3*1%). - assert_ok!(Pools::set_commission( - RuntimeOrigin::signed(900), - 1, - Some((Perbill::from_percent(4), 900)) - )); - assert_eq!( pool_events_since_last_call(), vec![ @@ -5831,21 +5776,6 @@ mod commission { max_increase: Perbill::from_percent(3), min_delay: 30 } - }, - Event::PoolCommissionChangeRateUpdated { - pool_id: 1, - change_rate: CommissionChangeRate { - max_increase: Perbill::from_percent(1), - min_delay: 30 - } - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(1), 900)) - }, - Event::PoolCommissionUpdated { - pool_id: 1, - current: Some((Perbill::from_percent(4), 900)) } ] ); @@ -6001,7 +5931,7 @@ mod commission { ExtBuilder::default().build_and_execute(|| { // 0% min delay test. // - // set commission change rate to 1% with a 0 block min_delay. + // set commission change rate to 1% with a 0 block `min_delay`. assert_ok!(Pools::set_commission_change_rate( RuntimeOrigin::signed(900), 1, From f4f39d5f4f7aee14bdf337ac18beea5c7aa3f4bc Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 11 Jan 2023 16:59:24 +0700 Subject: [PATCH 218/221] fmt --- frame/nomination-pools/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 1da604c5d44f7..812a1ac639901 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -632,8 +632,8 @@ impl Commission { } // Test for `max_increase` throttling. // - // The attempted increase in commission relative to the current commission if throttled if - // greater than `max_increase`. + // The attempted increase in commission relative to the current commission if throttled + // if greater than `max_increase`. if (*to).saturating_sub(commission_as_percent) > t.max_increase { return true } From fb9766f1eeb3bcd84fec966b19591b1681a4d9d7 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 11 Jan 2023 17:08:22 +0700 Subject: [PATCH 219/221] use map_pr_else w. defensive! --- frame/nomination-pools/src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 812a1ac639901..72e067ab3d7b8 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -642,20 +642,23 @@ impl Commission { // // Note: matching `None` is defensive only. `throttle_from` should always exist where // `change_rate` has already been set, so this scenario should never happen. - return match self.throttle_from { - Some(f) => { + return self.throttle_from.map_or_else( + || { + defensive!("throttle_from should exist if change_rate is set"); + false + }, + |f| { // if `min_delay` is zero (no delay), not throttling. if t.min_delay == Zero::zero() { - false + return false } else { // throttling if blocks passed is less than `min_delay`. let blocks_surpassed = >::block_number().saturating_sub(f); - blocks_surpassed < t.min_delay + return blocks_surpassed < t.min_delay } }, - _ => false, - } + ) } false } From 7ba0e2b813ac741d10016a4edb01d099118d07f0 Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 11 Jan 2023 17:13:38 +0700 Subject: [PATCH 220/221] reformat try_update_current --- frame/nomination-pools/src/lib.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index 72e067ab3d7b8..de5ebecec3a7e 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -670,20 +670,21 @@ impl Commission { /// `throttle_from` to the current block. If the supplied commission is zero, `None` will be /// inserted and `payee` will be ignored. fn try_update_current(&mut self, current: &Option<(Perbill, T::AccountId)>) -> DispatchResult { - if current.is_none() { - self.current = None; - } else { - let (commission, payee) = current.as_ref().ok_or(Error::::NoCommissionSet)?; - ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); - ensure!( - self.max.map_or(true, |m| commission <= &m), - Error::::CommissionExceedsMaximum - ); - - self.current = - if commission == &Zero::zero() { None } else { Some((*commission, payee.clone())) }; - } - + self.current = match current { + None => None, + Some((commission, payee)) => { + ensure!(!self.throttling(&commission), Error::::CommissionChangeThrottled); + ensure!( + self.max.map_or(true, |m| commission <= &m), + Error::::CommissionExceedsMaximum + ); + if commission.is_zero() { + None + } else { + Some((*commission, payee.clone())) + } + }, + }; let _ = self.register_update(); Ok(()) } From d4be203bcbc50650b869729e8e2b31022afa12fa Mon Sep 17 00:00:00 2001 From: Ross Bulat Date: Wed, 11 Jan 2023 17:25:59 +0700 Subject: [PATCH 221/221] amend comment --- frame/nomination-pools/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frame/nomination-pools/src/lib.rs b/frame/nomination-pools/src/lib.rs index de5ebecec3a7e..a09e55e5d543b 100644 --- a/frame/nomination-pools/src/lib.rs +++ b/frame/nomination-pools/src/lib.rs @@ -632,8 +632,7 @@ impl Commission { } // Test for `max_increase` throttling. // - // The attempted increase in commission relative to the current commission if throttled - // if greater than `max_increase`. + // Throttled if the attempted increase in commission is greater than `max_increase`. if (*to).saturating_sub(commission_as_percent) > t.max_increase { return true }