From 301001a9f65106d4daaba3286c9aa92f91e53f05 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Thu, 3 Apr 2025 21:22:29 +0800 Subject: [PATCH 01/10] Start moving our changes out of the core Staking code. --- pallets/runtime/common/src/runtime.rs | 2 +- pallets/runtime/tests/src/staking/mock.rs | 2 +- pallets/staking/Cargo.toml | 1 - pallets/staking/src/lib.rs | 26 +- pallets/staking/src/pallet/impls.rs | 153 +-------- pallets/staking/src/pallet/mod.rs | 175 +--------- pallets/staking/src/permissioned.rs | 386 ++++++++++++++++++++++ pallets/staking/src/types.rs | 45 ++- 8 files changed, 471 insertions(+), 319 deletions(-) create mode 100644 pallets/staking/src/permissioned.rs diff --git a/pallets/runtime/common/src/runtime.rs b/pallets/runtime/common/src/runtime.rs index d2551df48e..1dcec0edbc 100644 --- a/pallets/runtime/common/src/runtime.rs +++ b/pallets/runtime/common/src/runtime.rs @@ -259,7 +259,7 @@ macro_rules! misc_pallet_impls { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = polymesh_primitives::EnsureRoot; type SessionInterface = Self; - type EraPayout = pallet_staking::ConvertCurve; + type EraPayout = pallet_staking::ConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; diff --git a/pallets/runtime/tests/src/staking/mock.rs b/pallets/runtime/tests/src/staking/mock.rs index 879199a78c..8dd52527e6 100644 --- a/pallets/runtime/tests/src/staking/mock.rs +++ b/pallets/runtime/tests/src/staking/mock.rs @@ -599,7 +599,7 @@ impl pallet_staking::Config for Test { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = frame_system::EnsureRoot; type SessionInterface = Self; - type EraPayout = ConvertCurve; + type EraPayout = ConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index d081526f64..9c03892561 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -60,7 +60,6 @@ std = [ "pallet-babe/std", "sp-arithmetic/std", "rand_chacha", - "frame-benchmarking", ] runtime-benchmarks = [ "frame-benchmarking", diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 99ffdcdeb4..9c4dac9b20 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -289,9 +289,12 @@ pub mod testing_utils; pub mod inflation; pub mod slashing; -pub mod types; pub mod weights; +pub mod permissioned; +pub use permissioned::PolymeshConvertCurve; +pub mod types; + pub mod pallet; use codec::{Decode, Encode, HasCompact, MaxEncodedLen}; @@ -886,8 +889,6 @@ pub trait EraPayout { total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, - max_inflated_issuance: Balance, - non_inflated_yearly_reward: Balance, ) -> (Balance, Balance); } @@ -896,8 +897,6 @@ impl EraPayout for () { _total_staked: Balance, _total_issuance: Balance, _era_duration_millis: u64, - _max_inflated_issuance: Balance, - _non_inflated_yearly_reward: Balance, ) -> (Balance, Balance) { (Default::default(), Default::default()) } @@ -905,26 +904,27 @@ impl EraPayout for () { /// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for /// backwards compatibility. -pub struct ConvertCurve(sp_std::marker::PhantomData); +pub struct ConvertCurve(sp_std::marker::PhantomData<(T, C)>); -impl>> - EraPayout for ConvertCurve +impl< + Balance: AtLeast32BitUnsigned + Clone, + T: Config, + C: Get<&'static PiecewiseLinear<'static>>, + > EraPayout for ConvertCurve { fn era_payout( total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, - max_inflated_issuance: Balance, - non_inflated_yearly_reward: Balance, ) -> (Balance, Balance) { let (validator_payout, max_payout) = inflation::compute_total_payout( - T::get(), + C::get(), total_staked, total_issuance, // Duration of era; more than u64::MAX is rewarded as u64::MAX. era_duration_millis, - max_inflated_issuance, - non_inflated_yearly_reward, + T::MaxVariableInflationTotalIssuance::get(), + T::FixedYearlyReward::get(), ); let rest = max_payout.saturating_sub(validator_payout.clone()); (validator_payout, rest) diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index cdcea46854..572bd1f61f 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -53,15 +53,12 @@ use super::{pallet::*, STAKING_ID}; // Polymesh change // ----------------------------------------------------------------- -use frame_support::traits::schedule::Anon; -use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; use frame_support::traits::DefensiveSaturating; use polymesh_primitives::traits::IdentityFnTrait; -use polymesh_primitives::IdentityId; -use polymesh_primitives::GC_DID; use crate::pallet::SlashingSwitch; +use crate::types::PermissionedStaking; use crate::UnlockChunk; // ----------------------------------------------------------------- @@ -152,7 +149,7 @@ impl Pallet { Ok(used_weight) } - pub(super) fn do_payout_stakers( + pub fn do_payout_stakers( validator_stash: T::AccountId, era: EraIndex, ) -> DispatchResultWithPostInfo { @@ -309,7 +306,7 @@ impl Pallet { /// Update the ledger for a controller. /// /// This will also update the stash lock. - pub(crate) fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger) { + pub fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger) { T::Currency::set_lock( STAKING_ID, &ledger.stash, @@ -320,7 +317,7 @@ impl Pallet { } /// Chill a stash account. - pub(crate) fn chill_stash(stash: &T::AccountId) { + pub fn chill_stash(stash: &T::AccountId) { // Polymesh change // ----------------------------------------------------------------- Self::release_running_validator(stash); @@ -496,51 +493,13 @@ impl Pallet { let era_duration = (now_as_millis_u64 - active_era_start).saturated_into::(); let staked = Self::eras_total_stake(&active_era.index); let issuance = T::Currency::total_issuance(); - let (validator_payout, remainder) = T::EraPayout::era_payout( - staked, - issuance, - era_duration, - T::MaxVariableInflationTotalIssuance::get(), - T::FixedYearlyReward::get(), - ); + let (validator_payout, remainder) = + T::EraPayout::era_payout(staked, issuance, era_duration); // Polymesh change // ----------------------------------------------------------------- // Schedule rewards - let next_block_number = >::block_number() + 1u32.into(); - for (index, validator_id) in T::SessionInterface::validators().into_iter().enumerate() { - let schedule_block_number = - next_block_number + index.saturated_into::(); - match T::RewardScheduler::schedule( - DispatchTime::At(schedule_block_number), - None, - HIGHEST_PRIORITY, - RawOrigin::Root.into(), - Call::::payout_stakers_by_system { - validator_stash: validator_id.clone(), - era: active_era.index, - }.into() - ) { - Ok(_) => log!( - info, - "💸 Rewards are successfully scheduled for validator id: {:?} at block number: {:?}", - &validator_id, - schedule_block_number, - ), - Err(error) => { - log!( - error, - "⛔ Detected error in scheduling the reward payment: {:?}", - error - ); - Self::deposit_event(Event::::RewardPaymentSchedulingInterrupted { - account_id: validator_id, - era: active_era.index, - error - }); - } - } - } + Self::schedule_payouts(&active_era); // ----------------------------------------------------------------- Self::deposit_event(Event::::EraPaid { @@ -903,9 +862,7 @@ impl Pallet { } } else if Validators::::contains_key(&voter) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&voter) - && Self::is_validator_active_balance_valid(&voter) - { + if Self::is_validator_compliant(&voter) { // if this voter is a validator: let self_vote = ( voter.clone(), @@ -980,9 +937,7 @@ impl Pallet { if Validators::::contains_key(&target) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&target) - && Self::is_validator_active_balance_valid(&target) - { + if Self::is_validator_compliant(&target) { all_targets.push(target); } } @@ -1096,62 +1051,6 @@ impl Pallet { ); } - // Polymesh change - // ----------------------------------------------------------------- - - /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. - pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { - if let Some(controller) = Self::bonded(&who) { - if let Some(ledger) = Self::ledger(&controller) { - return ledger.active >= MinValidatorBond::::get(); - } - } - false - } - - /// Returns `true` if `stash` has a valid cdd claim and is permissioned. Otherwise, returns `false`. - pub(crate) fn is_validator_compliant(stash: &T::AccountId) -> bool { - pallet_identity::Pallet::::get_identity(stash).map_or(false, |id| { - pallet_identity::Pallet::::has_valid_cdd(id) - && Self::permissioned_identity(id).is_some() - }) - } - - /// Returns `true` if `who` has a valid cdd claim. Otherwise, returns `false`. - pub(crate) fn is_nominator_compliant(who: &T::AccountId) -> bool { - pallet_identity::Pallet::::get_identity(who) - .map_or(false, |id| pallet_identity::Pallet::::has_valid_cdd(id)) - } - - pub(crate) fn get_bonding_duration_period() -> u64 { - (T::SessionsPerEra::get() * T::BondingDuration::get()) as u64 // total session - * T::EpochDuration::get() // session length - * T::ExpectedBlockTime::get().saturated_into::() - } - - /// Decrease the running count of validators by 1 for the stash identity. - pub(crate) fn release_running_validator(stash: &T::AccountId) { - if !>::contains_key(stash) { - return; - } - - if let Some(did) = pallet_identity::Pallet::::get_identity(stash) { - PermissionedIdentity::::mutate(&did, |preferences| { - if let Some(p) = preferences { - if p.running_count > 0 { - p.running_count -= 1; - } - } - }); - pallet_identity::Pallet::::remove_account_key_ref_count(&stash); - } - } - - /// Returns the maximum number of validators per identiy - pub fn maximum_number_of_validators_per_identity() -> u32 { - (T::MaxValidatorPerIdentity::get() * Self::validator_count()).max(1) - } - pub fn unbond_balance( controller_account: T::AccountId, ledger: &mut StakingLedger, @@ -1219,40 +1118,6 @@ impl Pallet { Ok(()) } - - pub(crate) fn base_chill_from_governance( - origin: T::RuntimeOrigin, - identity: IdentityId, - stash_keys: Vec, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - for key in &stash_keys { - let key_did = pallet_identity::Pallet::::get_identity(&key); - // Checks if the stash key identity is the same as the identity given. - ensure!(key_did == Some(identity), Error::::NotStash); - // Checks if the key is a validator if not returns an error. - ensure!( - >::contains_key(&key), - Error::::ValidatorNotFound - ); - } - - for key in stash_keys { - Self::chill_stash(&key); - } - - // Change identity status to be Non-Permissioned - PermissionedIdentity::::remove(&identity); - - Self::deposit_event(Event::::PermissionedIdentityRemoved { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) - } - - // ----------------------------------------------------------------- } impl Pallet { diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index c039bd6550..96648ba68e 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -53,15 +53,14 @@ use crate::{ use frame_support::traits::schedule::Anon; use frame_support::traits::IsSubType; use frame_system::offchain::SendTransactionTypes; -use sp_runtime::traits::{AccountIdConversion, Dispatchable}; +use sp_runtime::traits::Dispatchable; use sp_runtime::Permill; use pallet_identity::Config as IdentityConfig; -use polymesh_primitives::constants::GC_PALLET_ID; use polymesh_primitives::GC_DID; use polymesh_primitives::{storage_migration_ver, traits::IdentityFnTrait, IdentityId}; -use crate::types::{PermissionedIdentityPrefs, SlashingSwitch}; +use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch}; // ----------------------------------------------------------------- const STAKING_ID: LockIdentifier = *b"staking "; @@ -1327,19 +1326,6 @@ pub mod pallet { Error::::CommissionTooLow ); - // ensure their commission is correct. - ensure!( - prefs.commission <= Self::validator_commission_cap(), - Error::::CommissionTooHigh - ); - - // Polymesh change - // ----------------------------------------------------------------- - let stash_did = pallet_identity::Pallet::::get_identity(stash) - .ok_or(Error::::StashIdentityDoesNotExist)?; - let mut stash_did_prefs = Self::permissioned_identity(stash_did) - .ok_or(Error::::StashIdentityNotPermissioned)?; - // Only check limits if they are not already a validator. if !Validators::::contains_key(stash) { // If this error is reached, we need to adjust the `MinValidatorBond` and start @@ -1351,16 +1337,11 @@ pub mod pallet { Error::::TooManyValidators ); } - // Ensure the identity doesn't run more validators than the intended count - ensure!( - stash_did_prefs.running_count < stash_did_prefs.intended_count, - Error::::TooManyValidators - ); - stash_did_prefs.running_count += 1; - pallet_identity::Pallet::::add_account_key_ref_count(&stash); - PermissionedIdentity::::insert(stash_did, stash_did_prefs); + // Polymesh change + // ----------------------------------------------------------------- + Self::on_validate(stash, prefs.commission)?; + // ----------------------------------------------------------------- } - // ----------------------------------------------------------------- Self::do_remove_nominator(stash); Self::do_add_validator(stash, prefs.clone()); @@ -2089,42 +2070,7 @@ pub mod pallet { identity: IdentityId, intended_count: Option, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::permissioned_identity(&identity).is_none(), - Error::::IdentityIsAlreadyPermissioned - ); - - ensure!( - pallet_identity::Pallet::::has_valid_cdd(identity), - Error::::IdentityIsMissingCDD - ); - - match intended_count { - Some(intended_count) => { - ensure!( - intended_count < Self::maximum_number_of_validators_per_identity(), - Error::::IntendedCountIsExceedingConsensusLimit - ); - PermissionedIdentity::::insert( - &identity, - PermissionedIdentityPrefs::new(intended_count), - ); - } - None => { - PermissionedIdentity::::insert( - &identity, - PermissionedIdentityPrefs::default(), - ); - } - } - - Self::deposit_event(Event::::PermissionedIdentityAdded { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) + Self::base_add_permissioned_validator(origin, identity, intended_count) } /// Remove an identity from the pool of (wannabe) validator identities. Effects are known in the next session. @@ -2140,20 +2086,7 @@ pub mod pallet { origin: OriginFor, identity: IdentityId, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::permissioned_identity(&identity).is_some(), - Error::::IdentityNotFound - ); - - PermissionedIdentity::::remove(&identity); - - Self::deposit_event(Event::::PermissionedIdentityRemoved { - governance_councill_did: GC_DID, - validators_identity: identity, - }); - Ok(()) + Self::base_remove_permissioned_validator(origin, identity) } /// Validate the nominators CDD expiry time. @@ -2167,49 +2100,7 @@ pub mod pallet { origin: OriginFor, targets: Vec, ) -> DispatchResult { - ensure_root(origin.clone())?; - - ensure!(!targets.is_empty(), Error::::EmptyTargets); - - let mut expired_nominators = Vec::new(); - // Iterate provided list of accountIds (These accountIds should be stash type account). - for target in targets - .iter() - // Nominator must be vouching for someone. - .filter(|target| Self::nominators(target).is_some()) - // Access the DIDs of the nominators whose CDDs have expired. - .filter(|target| { - // Fetch all the claim values provided by the trusted service providers - // There is a possibility that nominator will have more than one claim for the same key, - // So we iterate all of them and if any one of the claim value doesn't expire then nominator posses - // valid CDD otherwise it will be removed from the pool of the nominators. - // If the target has no DID, it's also removed. - pallet_identity::Pallet::::get_identity(&target) - .filter(|did| pallet_identity::Pallet::::has_valid_cdd(*did)) - .is_none() - }) - { - // Un-bonding the balance that bonded with the controller account of a Stash account - // This unbonded amount only be accessible after completion of the BondingDuration - // Controller account need to call the dispatchable function `withdraw_unbond` to withdraw fund. - - let controller = Self::bonded(target).ok_or(Error::::NotStash)?; - let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; - let active_balance = ledger.active; - if ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize { - Self::unbond_balance(controller, &mut ledger, active_balance)?; - - expired_nominators.push(target.clone()); - // Free the nominator from the valid nominator list - >::remove(target); - } - } - Self::deposit_event(Event::::InvalidatedNominators { - governance_councill_did: GC_DID, - governance_councill_account: GC_PALLET_ID.into_account_truncating(), - expired_nominators: expired_nominators, - }); - Ok(()) + Self::base_validate_cdd_expiry_nominators(origin, targets) } #[pallet::call_index(29)] @@ -2221,8 +2112,7 @@ pub mod pallet { validator_stash: T::AccountId, era: EraIndex, ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; - Self::do_payout_stakers(validator_stash, era) + Self::base_payout_stakers_by_system(origin, validator_stash, era) } /// Switch slashing status on the basis of given `slashing_switch`. Can only be called by root. @@ -2232,10 +2122,7 @@ pub mod pallet { origin: OriginFor, slashing_switch: SlashingSwitch, ) -> DispatchResult { - ensure_root(origin)?; - SlashingAllowedFor::::put(slashing_switch); - Self::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); - Ok(()) + Self::base_change_slashing_allowed_for(origin, slashing_switch) } /// Sets the intended count to `new_intended_count` for the given `identity`. @@ -2246,18 +2133,11 @@ pub mod pallet { identity: IdentityId, new_intended_count: u32, ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!( - Self::maximum_number_of_validators_per_identity() > new_intended_count, - Error::::IntendedCountIsExceedingConsensusLimit - ); - - PermissionedIdentity::::try_mutate(&identity, |pref| { - pref.as_mut() - .ok_or_else(|| Error::::IdentityNotFound.into()) - .map(|p| p.intended_count = new_intended_count) - }) + Self::base_update_permissioned_validator_intended_count( + origin, + identity, + new_intended_count, + ) } /// Governance council forcefully chills a validator. Effects will be felt at the beginning of the next era. @@ -2279,28 +2159,7 @@ pub mod pallet { #[pallet::call_index(33)] #[pallet::weight(::WeightInfo::set_commission_cap(150))] pub fn set_commission_cap(origin: OriginFor, new_cap: Perbill) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin.clone())?; - - // Update the cap, assuming it changed, or error. - let old_cap = - ValidatorCommissionCap::::try_mutate(|cap| -> Result<_, DispatchError> { - ensure!(*cap != new_cap, Error::::CommissionUnchanged); - Ok(core::mem::replace(cap, new_cap)) - })?; - - // Update `commission` in each validator prefs to `min(comission, new_cap)`. - >::translate(|_, mut prefs: ValidatorPrefs| { - prefs.commission = prefs.commission.min(new_cap); - Some(prefs) - }); - - Self::deposit_event(Event::::CommissionCapUpdated { - governance_councill_did: GC_DID, - old_commission_cap: old_cap, - new_commission_cap: new_cap, - }); - - Ok(()) + Self::base_set_commission_cap(origin, new_cap) } // ----------------------------------------------------------------- } diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs new file mode 100644 index 0000000000..0213b0fee3 --- /dev/null +++ b/pallets/staking/src/permissioned.rs @@ -0,0 +1,386 @@ +use frame_support::{pallet_prelude::*, traits::Get}; +use frame_system::RawOrigin; +use frame_system::{ensure_root, pallet_prelude::*}; +use sp_runtime::traits::SaturatedConversion; +use sp_std::prelude::*; + +use frame_support::traits::schedule::Anon; +use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; + +use polymesh_primitives::constants::GC_PALLET_ID; +use polymesh_primitives::IdentityId; +use polymesh_primitives::GC_DID; + +use sp_runtime::traits::AccountIdConversion; + +use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch}; +use crate::*; + +/// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for +/// backwards compatibility. +pub struct PolymeshConvertCurve(sp_std::marker::PhantomData<(T, C)>); + +impl< + Balance: AtLeast32BitUnsigned + Clone, + T: Config, + C: Get<&'static PiecewiseLinear<'static>>, + > EraPayout for PolymeshConvertCurve +{ + fn era_payout( + total_staked: Balance, + total_issuance: Balance, + era_duration_millis: u64, + ) -> (Balance, Balance) { + let (validator_payout, max_payout) = inflation::compute_total_payout( + C::get(), + total_staked, + total_issuance, + // Duration of era; more than u64::MAX is rewarded as u64::MAX. + era_duration_millis, + T::MaxVariableInflationTotalIssuance::get(), + T::FixedYearlyReward::get(), + ); + let rest = max_payout.saturating_sub(validator_payout.clone()); + (validator_payout, rest) + } +} + +impl PermissionedStaking for Pallet { + fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { + // ensure their commission is correct. + ensure!( + commission <= Self::validator_commission_cap(), + Error::::CommissionTooHigh + ); + + let stash_did = pallet_identity::Pallet::::get_identity(stash) + .ok_or(Error::::StashIdentityDoesNotExist)?; + let mut stash_did_prefs = Self::permissioned_identity(stash_did) + .ok_or(Error::::StashIdentityNotPermissioned)?; + + // Ensure the identity doesn't run more validators than the intended count + ensure!( + stash_did_prefs.running_count < stash_did_prefs.intended_count, + Error::::TooManyValidators + ); + stash_did_prefs.running_count += 1; + pallet_identity::Pallet::::add_account_key_ref_count(&stash); + PermissionedIdentity::::insert(stash_did, stash_did_prefs); + + Ok(()) + } + + /// On chill hook. + fn on_chill(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// On nominate hook. + fn on_nominate(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// On unbond hook. + fn on_unbond(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// Returns `true` if `stash` has a valid cdd claim and is permissioned. Otherwise, returns `false`. + fn is_validator_compliant(stash: &T::AccountId) -> bool { + pallet_identity::Pallet::::get_identity(stash).map_or(false, |id| { + pallet_identity::Pallet::::has_valid_cdd(id) + && Self::permissioned_identity(id).is_some() + && Self::is_validator_active_balance_valid(stash) + }) + } + + /// Returns `true` if `who` has a valid cdd claim. Otherwise, returns `false`. + fn is_nominator_compliant(who: &T::AccountId) -> bool { + pallet_identity::Pallet::::get_identity(who) + .map_or(false, |id| pallet_identity::Pallet::::has_valid_cdd(id)) + } + + /// Schedule reward payouts. + fn schedule_payouts(active_era: &ActiveEraInfo) { + let next_block_number = >::block_number() + 1u32.into(); + for (index, validator_id) in T::SessionInterface::validators().into_iter().enumerate() { + let schedule_block_number = + next_block_number + index.saturated_into::(); + match T::RewardScheduler::schedule( + DispatchTime::At(schedule_block_number), + None, + HIGHEST_PRIORITY, + RawOrigin::Root.into(), + Call::::payout_stakers_by_system { + validator_stash: validator_id.clone(), + era: active_era.index, + }.into() + ) { + Ok(_) => log!( + info, + "💸 Rewards are successfully scheduled for validator id: {:?} at block number: {:?}", + &validator_id, + schedule_block_number, + ), + Err(error) => { + log!( + error, + "⛔ Detected error in scheduling the reward payment: {:?}", + error + ); + Self::deposit_event(Event::::RewardPaymentSchedulingInterrupted { + account_id: validator_id, + era: active_era.index, + error + }); + } + } + } + } +} + +impl Pallet { + /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. + pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { + if let Some(controller) = Self::bonded(&who) { + if let Some(ledger) = Self::ledger(&controller) { + return ledger.active >= MinValidatorBond::::get(); + } + } + false + } + + pub(crate) fn get_bonding_duration_period() -> u64 { + (T::SessionsPerEra::get() * T::BondingDuration::get()) as u64 // total session + * T::EpochDuration::get() // session length + * T::ExpectedBlockTime::get().saturated_into::() + } + + /// Decrease the running count of validators by 1 for the stash identity. + pub(crate) fn release_running_validator(stash: &T::AccountId) { + if !>::contains_key(stash) { + return; + } + + if let Some(did) = pallet_identity::Pallet::::get_identity(stash) { + PermissionedIdentity::::mutate(&did, |preferences| { + if let Some(p) = preferences { + if p.running_count > 0 { + p.running_count -= 1; + } + } + }); + pallet_identity::Pallet::::remove_account_key_ref_count(&stash); + } + } + + /// Returns the maximum number of validators per identiy + pub fn maximum_number_of_validators_per_identity() -> u32 { + (T::MaxValidatorPerIdentity::get() * Self::validator_count()).max(1) + } + + pub(crate) fn base_add_permissioned_validator( + origin: OriginFor, + identity: IdentityId, + intended_count: Option, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::permissioned_identity(&identity).is_none(), + Error::::IdentityIsAlreadyPermissioned + ); + + ensure!( + pallet_identity::Pallet::::has_valid_cdd(identity), + Error::::IdentityIsMissingCDD + ); + + match intended_count { + Some(intended_count) => { + ensure!( + intended_count < Self::maximum_number_of_validators_per_identity(), + Error::::IntendedCountIsExceedingConsensusLimit + ); + PermissionedIdentity::::insert( + &identity, + PermissionedIdentityPrefs::new(intended_count), + ); + } + None => { + PermissionedIdentity::::insert(&identity, PermissionedIdentityPrefs::default()); + } + } + + Self::deposit_event(Event::::PermissionedIdentityAdded { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } + + pub(crate) fn base_remove_permissioned_validator( + origin: OriginFor, + identity: IdentityId, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::permissioned_identity(&identity).is_some(), + Error::::IdentityNotFound + ); + + PermissionedIdentity::::remove(&identity); + + Self::deposit_event(Event::::PermissionedIdentityRemoved { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } + + pub(crate) fn base_validate_cdd_expiry_nominators( + origin: OriginFor, + targets: Vec, + ) -> DispatchResult { + ensure_root(origin.clone())?; + + ensure!(!targets.is_empty(), Error::::EmptyTargets); + + let mut expired_nominators = Vec::new(); + // Iterate provided list of accountIds (These accountIds should be stash type account). + for target in targets + .iter() + // Nominator must be vouching for someone. + .filter(|target| Self::nominators(target).is_some()) + // Access the DIDs of the nominators whose CDDs have expired. + .filter(|target| { + // Fetch all the claim values provided by the trusted service providers + // There is a possibility that nominator will have more than one claim for the same key, + // So we iterate all of them and if any one of the claim value doesn't expire then nominator posses + // valid CDD otherwise it will be removed from the pool of the nominators. + // If the target has no DID, it's also removed. + pallet_identity::Pallet::::get_identity(&target) + .filter(|did| pallet_identity::Pallet::::has_valid_cdd(*did)) + .is_none() + }) + { + // Un-bonding the balance that bonded with the controller account of a Stash account + // This unbonded amount only be accessible after completion of the BondingDuration + // Controller account need to call the dispatchable function `withdraw_unbond` to withdraw fund. + + let controller = Self::bonded(target).ok_or(Error::::NotStash)?; + let mut ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; + let active_balance = ledger.active; + if ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize { + Self::unbond_balance(controller, &mut ledger, active_balance)?; + + expired_nominators.push(target.clone()); + // Free the nominator from the valid nominator list + >::remove(target); + } + } + Self::deposit_event(Event::::InvalidatedNominators { + governance_councill_did: GC_DID, + governance_councill_account: GC_PALLET_ID.into_account_truncating(), + expired_nominators: expired_nominators, + }); + Ok(()) + } + + pub(crate) fn base_payout_stakers_by_system( + origin: OriginFor, + validator_stash: T::AccountId, + era: EraIndex, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + Self::do_payout_stakers(validator_stash, era) + } + + pub(crate) fn base_change_slashing_allowed_for( + origin: OriginFor, + slashing_switch: SlashingSwitch, + ) -> DispatchResult { + ensure_root(origin)?; + SlashingAllowedFor::::put(slashing_switch); + Self::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); + Ok(()) + } + + pub(crate) fn base_update_permissioned_validator_intended_count( + origin: OriginFor, + identity: IdentityId, + new_intended_count: u32, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + ensure!( + Self::maximum_number_of_validators_per_identity() > new_intended_count, + Error::::IntendedCountIsExceedingConsensusLimit + ); + + PermissionedIdentity::::try_mutate(&identity, |pref| { + pref.as_mut() + .ok_or_else(|| Error::::IdentityNotFound.into()) + .map(|p| p.intended_count = new_intended_count) + }) + } + + pub(crate) fn base_set_commission_cap( + origin: OriginFor, + new_cap: Perbill, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin.clone())?; + + // Update the cap, assuming it changed, or error. + let old_cap = ValidatorCommissionCap::::try_mutate(|cap| -> Result<_, DispatchError> { + ensure!(*cap != new_cap, Error::::CommissionUnchanged); + Ok(core::mem::replace(cap, new_cap)) + })?; + + // Update `commission` in each validator prefs to `min(comission, new_cap)`. + >::translate(|_, mut prefs: ValidatorPrefs| { + prefs.commission = prefs.commission.min(new_cap); + Some(prefs) + }); + + Self::deposit_event(Event::::CommissionCapUpdated { + governance_councill_did: GC_DID, + old_commission_cap: old_cap, + new_commission_cap: new_cap, + }); + + Ok(()) + } + pub(crate) fn base_chill_from_governance( + origin: T::RuntimeOrigin, + identity: IdentityId, + stash_keys: Vec, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + for key in &stash_keys { + let key_did = pallet_identity::Pallet::::get_identity(&key); + // Checks if the stash key identity is the same as the identity given. + ensure!(key_did == Some(identity), Error::::NotStash); + // Checks if the key is a validator if not returns an error. + ensure!( + >::contains_key(&key), + Error::::ValidatorNotFound + ); + } + + for key in stash_keys { + Self::chill_stash(&key); + } + + // Change identity status to be Non-Permissioned + PermissionedIdentity::::remove(&identity); + + Self::deposit_event(Event::::PermissionedIdentityRemoved { + governance_councill_did: GC_DID, + validators_identity: identity, + }); + Ok(()) + } +} diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index 2377d56c79..bccf922bdf 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -2,8 +2,51 @@ use sp_runtime::{Deserialize, Serialize}; use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::dispatch::DispatchResult; use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; +use sp_runtime::{Perbill, RuntimeDebug}; + +use crate::{ActiveEraInfo, Config}; + +/// A trait used by the staking pallet for permissioned staking. +/// +/// A permissioned Substrate network can be configured to allow only a set of +/// identities to participate in staking. This trait is used to define the +/// behavior of the staking pallet in such a network. +pub trait PermissionedStaking { + /// On validate hook. + fn on_validate(_who: &T::AccountId, _commission: Perbill) -> DispatchResult { + Ok(()) + } + + /// On chill hook. + fn on_chill(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// On nominate hook. + fn on_nominate(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// On unbond hook. + fn on_unbond(_who: &T::AccountId) -> DispatchResult { + Ok(()) + } + + /// Is the validator still compliant? + fn is_validator_compliant(_who: &T::AccountId) -> bool { + true + } + + /// Is the nominator still compliant? + fn is_nominator_compliant(_who: &T::AccountId) -> bool { + true + } + + /// Schedule reward payouts. + fn schedule_payouts(_active_era: &ActiveEraInfo) {} +} /// Preference of an identity regarding validation. #[derive(Decode, Encode, RuntimeDebug, TypeInfo)] From 09c3d061f17ba041462638d7479246abb27b3051 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Fri, 4 Apr 2025 23:46:03 +0800 Subject: [PATCH 02/10] Refactor chill/nominate code. --- pallets/staking/src/pallet/impls.rs | 4 +++- pallets/staking/src/pallet/mod.rs | 14 ++++---------- pallets/staking/src/permissioned.rs | 20 +++++++++++++------- pallets/staking/src/types.rs | 9 +-------- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index 572bd1f61f..a970081876 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -117,6 +117,8 @@ impl Pallet { ledger = ledger.consolidate_unlocked(current_era) } + // Polymesh change: + // ledger.active <= minimum_balance() let used_weight = if ledger.unlocking.is_empty() && ledger.active <= T::Currency::minimum_balance() { // This account must have called `unbond()` with some value that caused the active @@ -320,7 +322,7 @@ impl Pallet { pub fn chill_stash(stash: &T::AccountId) { // Polymesh change // ----------------------------------------------------------------- - Self::release_running_validator(stash); + Self::on_chill(stash); // ----------------------------------------------------------------- let chilled_as_validator = Self::do_remove_validator(stash); let chilled_as_nominator = Self::do_remove_nominator(stash); diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index 96648ba68e..4c1835adc0 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -1417,18 +1417,10 @@ pub mod pallet { // Polymesh change // ----------------------------------------------------------------- + Self::on_nominate(&stash)?; + let nominator_did = pallet_identity::Pallet::::get_identity(&stash) .ok_or(Error::::StashIdentityDoesNotExist)?; - let bounding_duration_period = Self::get_bonding_duration_period() as u32; - - if let None = pallet_identity::Pallet::::fetch_cdd( - nominator_did, - bounding_duration_period.into(), - ) { - return Err(Error::::StashIdentityNotCDDed.into()); - } - - Self::release_running_validator(&stash); Self::deposit_event(Event::::Nominated { nominator_identity: nominator_did, stash: ledger.stash.clone(), @@ -1827,6 +1819,8 @@ pub mod pallet { let _ = ensure_signed(origin)?; let ed = T::Currency::minimum_balance(); + // Polymesh change: + // total_balance(stash) <= ed let reapable = T::Currency::total_balance(&stash) <= ed || Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) .map(|l| l.total) diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index 0213b0fee3..f32ecad4ed 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -71,17 +71,23 @@ impl PermissionedStaking for Pallet { } /// On chill hook. - fn on_chill(_who: &T::AccountId) -> DispatchResult { - Ok(()) + fn on_chill(stash: &T::AccountId) { + Self::release_running_validator(stash); } /// On nominate hook. - fn on_nominate(_who: &T::AccountId) -> DispatchResult { - Ok(()) - } + fn on_nominate(stash: &T::AccountId) -> DispatchResult { + let nominator_did = pallet_identity::Pallet::::get_identity(&stash) + .ok_or(Error::::StashIdentityDoesNotExist)?; + let bounding_duration_period = Self::get_bonding_duration_period() as u32; + + if let None = + pallet_identity::Pallet::::fetch_cdd(nominator_did, bounding_duration_period.into()) + { + return Err(Error::::StashIdentityNotCDDed.into()); + } - /// On unbond hook. - fn on_unbond(_who: &T::AccountId) -> DispatchResult { + Self::release_running_validator(&stash); Ok(()) } diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index bccf922bdf..bd99fe2a64 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -20,20 +20,13 @@ pub trait PermissionedStaking { } /// On chill hook. - fn on_chill(_who: &T::AccountId) -> DispatchResult { - Ok(()) - } + fn on_chill(_who: &T::AccountId) {} /// On nominate hook. fn on_nominate(_who: &T::AccountId) -> DispatchResult { Ok(()) } - /// On unbond hook. - fn on_unbond(_who: &T::AccountId) -> DispatchResult { - Ok(()) - } - /// Is the validator still compliant? fn is_validator_compliant(_who: &T::AccountId) -> bool { true From 8b86b30172e69de2a510a020f497531bdb7bfa02 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Mon, 7 Apr 2025 21:01:24 +0800 Subject: [PATCH 03/10] Refactor slashing switch. --- pallets/staking/src/pallet/impls.rs | 7 +++--- pallets/staking/src/permissioned.rs | 8 ++++++- pallets/staking/src/slashing.rs | 8 +++---- pallets/staking/src/types.rs | 36 +++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index a970081876..194a0af52e 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -57,7 +57,6 @@ use frame_support::traits::DefensiveSaturating; use polymesh_primitives::traits::IdentityFnTrait; -use crate::pallet::SlashingSwitch; use crate::types::PermissionedStaking; use crate::UnlockChunk; // ----------------------------------------------------------------- @@ -1436,10 +1435,10 @@ where // Polymesh change // ----------------------------------------------------------------- let slash_fraction_none = vec![Perbill::from_parts(0); slash_fraction.len()]; - let slash_fraction = if SlashingAllowedFor::::get() == SlashingSwitch::None { - slash_fraction_none.as_slice() - } else { + let slash_fraction = if Self::is_slashing_enabled() { slash_fraction + } else { + slash_fraction_none.as_slice() }; // ----------------------------------------------------------------- diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index f32ecad4ed..a93d13eb3e 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -13,7 +13,7 @@ use polymesh_primitives::GC_DID; use sp_runtime::traits::AccountIdConversion; -use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch}; +use crate::types::{PermissionedIdentityPrefs, PermissionedStaking, SlashingSwitch, WhoToSlash}; use crate::*; /// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for @@ -143,6 +143,12 @@ impl PermissionedStaking for Pallet { } } } + + /// Who should be slashed? + /// Returns `None` if no one should be slashed. + fn who_to_slash() -> Option { + SlashingAllowedFor::::get().into() + } } impl Pallet { diff --git a/pallets/staking/src/slashing.rs b/pallets/staking/src/slashing.rs index 5314bb97a2..1953981f7b 100644 --- a/pallets/staking/src/slashing.rs +++ b/pallets/staking/src/slashing.rs @@ -50,8 +50,8 @@ //! Based on research at use crate::{ - types::SlashingSwitch, BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, Pallet, - Perbill, SessionInterface, SlashingAllowedFor, Store, UnappliedSlash, + types::PermissionedStaking, BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, Pallet, + Perbill, SessionInterface, Store, UnappliedSlash, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ @@ -303,7 +303,7 @@ pub(crate) fn compute_slash( // Polymesh change // ----------------------------------------------------------------- let mut nominators_slashed = Vec::new(); - if SlashingAllowedFor::::get() == SlashingSwitch::ValidatorAndNominator { + if Pallet::::slash_nominators() { reward_payout += slash_nominators::(params.clone(), prior_slash_p, &mut nominators_slashed); } @@ -665,7 +665,7 @@ pub(crate) fn apply_slash( // Polymesh change // ----------------------------------------------------------------- - if SlashingAllowedFor::::get() == SlashingSwitch::ValidatorAndNominator { + if Pallet::::slash_nominators() { for &(ref nominator, nominator_slash) in &unapplied_slash.others { do_slash::( nominator, diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index bd99fe2a64..498fa19dc0 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -39,6 +39,21 @@ pub trait PermissionedStaking { /// Schedule reward payouts. fn schedule_payouts(_active_era: &ActiveEraInfo) {} + + /// Who should be slashed? + fn who_to_slash() -> Option { + Some(WhoToSlash::ValidatorAndNominator) + } + + /// Is slashing enabled? + fn is_slashing_enabled() -> bool { + Self::who_to_slash().is_some() + } + + /// Slash nominators? + fn slash_nominators() -> bool { + Self::who_to_slash() == Some(WhoToSlash::ValidatorAndNominator) + } } /// Preference of an identity regarding validation. @@ -74,6 +89,17 @@ impl PermissionedIdentityPrefs { } } +/// Who should be slashed. +#[derive(Decode, Encode, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[derive(Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum WhoToSlash { + /// Allow validators but not nominators to get slashed. + Validator, + /// Allow both validators and nominators to get slashed. + ValidatorAndNominator, +} + /// Switch used to change the "victim" for slashing. Victims can be /// validators, both validators and nominators, or no-one. #[derive(Decode, Encode, MaxEncodedLen, RuntimeDebug, TypeInfo)] @@ -88,3 +114,13 @@ pub enum SlashingSwitch { #[default] None, } + +impl From for Option { + fn from(value: SlashingSwitch) -> Self { + match value { + SlashingSwitch::Validator => Some(WhoToSlash::Validator), + SlashingSwitch::ValidatorAndNominator => Some(WhoToSlash::ValidatorAndNominator), + SlashingSwitch::None => None, + } + } +} From 603ecc552206979c1e4cbc87884b40941608c7c3 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Mon, 7 Apr 2025 21:27:58 +0800 Subject: [PATCH 04/10] Add missing "Polymesh added" notes. --- pallets/staking/src/pallet/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index 4c1835adc0..983221f5cc 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -1096,6 +1096,7 @@ pub mod pallet { return Err(Error::::AlreadyBonded.into()); } + /// Polymesh added: // An existing controller cannot become a stash. if >::contains_key(&stash) { return Err(Error::::AlreadyPaired.into()); @@ -1508,6 +1509,7 @@ pub mod pallet { return Err(Error::::AlreadyPaired.into()); } + /// Polymesh added: // Prevents stashes which are controllers of another ledger from calling the extrinsic if old_controller != stash { if >::contains_key(&stash) { From e83e55b23c68615cd429f712f0705f863e35cf17 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Mon, 7 Apr 2025 21:34:02 +0800 Subject: [PATCH 05/10] FIXUP --- pallets/staking/src/pallet/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index 983221f5cc..1ef29a93af 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -1096,7 +1096,7 @@ pub mod pallet { return Err(Error::::AlreadyBonded.into()); } - /// Polymesh added: + // Polymesh added: // An existing controller cannot become a stash. if >::contains_key(&stash) { return Err(Error::::AlreadyPaired.into()); @@ -1509,7 +1509,7 @@ pub mod pallet { return Err(Error::::AlreadyPaired.into()); } - /// Polymesh added: + // Polymesh added: // Prevents stashes which are controllers of another ledger from calling the extrinsic if old_controller != stash { if >::contains_key(&stash) { From 6a2a1595745bb00fe95f43bbdb1e0edacedc116f Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Tue, 8 Apr 2025 19:50:15 +0800 Subject: [PATCH 06/10] Access Polymesh staking logic through an associated type. --- pallets/runtime/common/src/runtime.rs | 3 ++- pallets/staking/src/lib.rs | 2 +- pallets/staking/src/pallet/impls.rs | 12 ++++----- pallets/staking/src/pallet/mod.rs | 7 +++-- pallets/staking/src/permissioned.rs | 39 ++++++++++++++++----------- pallets/staking/src/slashing.rs | 4 +-- 6 files changed, 40 insertions(+), 27 deletions(-) diff --git a/pallets/runtime/common/src/runtime.rs b/pallets/runtime/common/src/runtime.rs index 1dcec0edbc..13ce9666b0 100644 --- a/pallets/runtime/common/src/runtime.rs +++ b/pallets/runtime/common/src/runtime.rs @@ -259,7 +259,7 @@ macro_rules! misc_pallet_impls { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = polymesh_primitives::EnsureRoot; type SessionInterface = Self; - type EraPayout = pallet_staking::ConvertCurve; + type EraPayout = pallet_staking::PolymeshConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; @@ -275,6 +275,7 @@ macro_rules! misc_pallet_impls { type Call = RuntimeCall; type PalletsOrigin = OriginCaller; type RewardScheduler = Scheduler; + type Permissioned = pallet_staking::PolymeshStaking; } impl pallet_authority_discovery::Config for Runtime { diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 9c4dac9b20..155298fef7 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -292,7 +292,7 @@ pub mod slashing; pub mod weights; pub mod permissioned; -pub use permissioned::PolymeshConvertCurve; +pub use permissioned::{PolymeshConvertCurve, PolymeshStaking}; pub mod types; pub mod pallet; diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index 194a0af52e..430aa9d8a0 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -321,7 +321,7 @@ impl Pallet { pub fn chill_stash(stash: &T::AccountId) { // Polymesh change // ----------------------------------------------------------------- - Self::on_chill(stash); + T::Permissioned::on_chill(stash); // ----------------------------------------------------------------- let chilled_as_validator = Self::do_remove_validator(stash); let chilled_as_nominator = Self::do_remove_nominator(stash); @@ -500,7 +500,7 @@ impl Pallet { // Polymesh change // ----------------------------------------------------------------- // Schedule rewards - Self::schedule_payouts(&active_era); + T::Permissioned::schedule_payouts(&active_era); // ----------------------------------------------------------------- Self::deposit_event(Event::::EraPaid { @@ -850,7 +850,7 @@ impl Pallet { if let Some(Nominations { targets, .. }) = >::get(&voter) { nominators_seen.saturating_inc(); - if Self::is_nominator_compliant(&voter) { + if T::Permissioned::is_nominator_compliant(&voter) { let voter_weight = weight_of(&voter); if !targets.is_empty() { all_voters.push((voter.clone(), voter_weight, targets)); @@ -863,7 +863,7 @@ impl Pallet { } } else if Validators::::contains_key(&voter) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&voter) { + if T::Permissioned::is_validator_compliant(&voter) { // if this voter is a validator: let self_vote = ( voter.clone(), @@ -938,7 +938,7 @@ impl Pallet { if Validators::::contains_key(&target) { validators_seen.saturating_inc(); - if Self::is_validator_compliant(&target) { + if T::Permissioned::is_validator_compliant(&target) { all_targets.push(target); } } @@ -1435,7 +1435,7 @@ where // Polymesh change // ----------------------------------------------------------------- let slash_fraction_none = vec![Perbill::from_parts(0); slash_fraction.len()]; - let slash_fraction = if Self::is_slashing_enabled() { + let slash_fraction = if T::Permissioned::is_slashing_enabled() { slash_fraction } else { slash_fraction_none.as_slice() diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index 1ef29a93af..aaf93d6c5d 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -327,6 +327,9 @@ pub mod pallet { // Polymesh change // ----------------------------------------------------------------- + /// Permissioned staking. + type Permissioned: PermissionedStaking; + /// Maximum amount of validators that can run by an identity. /// It will be MaxValidatorPerIdentity * Self::validator_count(). #[pallet::constant] @@ -1340,7 +1343,7 @@ pub mod pallet { } // Polymesh change // ----------------------------------------------------------------- - Self::on_validate(stash, prefs.commission)?; + T::Permissioned::on_validate(stash, prefs.commission)?; // ----------------------------------------------------------------- } @@ -1418,7 +1421,7 @@ pub mod pallet { // Polymesh change // ----------------------------------------------------------------- - Self::on_nominate(&stash)?; + T::Permissioned::on_nominate(&stash)?; let nominator_did = pallet_identity::Pallet::::get_identity(&stash) .ok_or(Error::::StashIdentityDoesNotExist)?; diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index a93d13eb3e..8f31ab9b2d 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -45,17 +45,19 @@ impl< } } -impl PermissionedStaking for Pallet { +pub struct PolymeshStaking(sp_std::marker::PhantomData); + +impl PermissionedStaking for PolymeshStaking { fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { // ensure their commission is correct. ensure!( - commission <= Self::validator_commission_cap(), + commission <= ValidatorCommissionCap::::get(), Error::::CommissionTooHigh ); let stash_did = pallet_identity::Pallet::::get_identity(stash) .ok_or(Error::::StashIdentityDoesNotExist)?; - let mut stash_did_prefs = Self::permissioned_identity(stash_did) + let mut stash_did_prefs = PermissionedIdentity::::get(stash_did) .ok_or(Error::::StashIdentityNotPermissioned)?; // Ensure the identity doesn't run more validators than the intended count @@ -95,7 +97,7 @@ impl PermissionedStaking for Pallet { fn is_validator_compliant(stash: &T::AccountId) -> bool { pallet_identity::Pallet::::get_identity(stash).map_or(false, |id| { pallet_identity::Pallet::::has_valid_cdd(id) - && Self::permissioned_identity(id).is_some() + && PermissionedIdentity::::get(id).is_some() && Self::is_validator_active_balance_valid(stash) }) } @@ -134,7 +136,7 @@ impl PermissionedStaking for Pallet { "⛔ Detected error in scheduling the reward payment: {:?}", error ); - Self::deposit_event(Event::::RewardPaymentSchedulingInterrupted { + Pallet::::deposit_event(Event::::RewardPaymentSchedulingInterrupted { account_id: validator_id, era: active_era.index, error @@ -151,11 +153,11 @@ impl PermissionedStaking for Pallet { } } -impl Pallet { +impl PolymeshStaking { /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { - if let Some(controller) = Self::bonded(&who) { - if let Some(ledger) = Self::ledger(&controller) { + if let Some(controller) = Bonded::::get(&who) { + if let Some(ledger) = Ledger::::get(&controller) { return ledger.active >= MinValidatorBond::::get(); } } @@ -188,7 +190,14 @@ impl Pallet { /// Returns the maximum number of validators per identiy pub fn maximum_number_of_validators_per_identity() -> u32 { - (T::MaxValidatorPerIdentity::get() * Self::validator_count()).max(1) + (T::MaxValidatorPerIdentity::get() * ValidatorCount::::get()).max(1) + } +} + +impl Pallet { + /// Returns the maximum number of validators per identiy + pub fn maximum_number_of_validators_per_identity() -> u32 { + (T::MaxValidatorPerIdentity::get() * ValidatorCount::::get()).max(1) } pub(crate) fn base_add_permissioned_validator( @@ -224,7 +233,7 @@ impl Pallet { } } - Self::deposit_event(Event::::PermissionedIdentityAdded { + Pallet::::deposit_event(Event::::PermissionedIdentityAdded { governance_councill_did: GC_DID, validators_identity: identity, }); @@ -244,7 +253,7 @@ impl Pallet { PermissionedIdentity::::remove(&identity); - Self::deposit_event(Event::::PermissionedIdentityRemoved { + Pallet::::deposit_event(Event::::PermissionedIdentityRemoved { governance_councill_did: GC_DID, validators_identity: identity, }); @@ -292,7 +301,7 @@ impl Pallet { >::remove(target); } } - Self::deposit_event(Event::::InvalidatedNominators { + Pallet::::deposit_event(Event::::InvalidatedNominators { governance_councill_did: GC_DID, governance_councill_account: GC_PALLET_ID.into_account_truncating(), expired_nominators: expired_nominators, @@ -315,7 +324,7 @@ impl Pallet { ) -> DispatchResult { ensure_root(origin)?; SlashingAllowedFor::::put(slashing_switch); - Self::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); + Pallet::::deposit_event(Event::::SlashingAllowedForChanged { slashing_switch }); Ok(()) } @@ -356,7 +365,7 @@ impl Pallet { Some(prefs) }); - Self::deposit_event(Event::::CommissionCapUpdated { + Pallet::::deposit_event(Event::::CommissionCapUpdated { governance_councill_did: GC_DID, old_commission_cap: old_cap, new_commission_cap: new_cap, @@ -389,7 +398,7 @@ impl Pallet { // Change identity status to be Non-Permissioned PermissionedIdentity::::remove(&identity); - Self::deposit_event(Event::::PermissionedIdentityRemoved { + Pallet::::deposit_event(Event::::PermissionedIdentityRemoved { governance_councill_did: GC_DID, validators_identity: identity, }); diff --git a/pallets/staking/src/slashing.rs b/pallets/staking/src/slashing.rs index 1953981f7b..e7ca928177 100644 --- a/pallets/staking/src/slashing.rs +++ b/pallets/staking/src/slashing.rs @@ -303,7 +303,7 @@ pub(crate) fn compute_slash( // Polymesh change // ----------------------------------------------------------------- let mut nominators_slashed = Vec::new(); - if Pallet::::slash_nominators() { + if T::Permissioned::slash_nominators() { reward_payout += slash_nominators::(params.clone(), prior_slash_p, &mut nominators_slashed); } @@ -665,7 +665,7 @@ pub(crate) fn apply_slash( // Polymesh change // ----------------------------------------------------------------- - if Pallet::::slash_nominators() { + if T::Permissioned::slash_nominators() { for &(ref nominator, nominator_slash) in &unapplied_slash.others { do_slash::( nominator, From 90c8e44f67995e85c5cf6dd65304e99685855ab1 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Tue, 8 Apr 2025 20:06:36 +0800 Subject: [PATCH 07/10] Impl PermissionedStaking on the pallet. --- pallets/runtime/common/src/runtime.rs | 2 +- pallets/runtime/tests/src/staking/mock.rs | 5 +++-- pallets/staking/src/lib.rs | 2 +- pallets/staking/src/pallet/impls.rs | 3 +++ pallets/staking/src/permissioned.rs | 13 ++----------- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/pallets/runtime/common/src/runtime.rs b/pallets/runtime/common/src/runtime.rs index 13ce9666b0..76a5a4a86a 100644 --- a/pallets/runtime/common/src/runtime.rs +++ b/pallets/runtime/common/src/runtime.rs @@ -275,7 +275,7 @@ macro_rules! misc_pallet_impls { type Call = RuntimeCall; type PalletsOrigin = OriginCaller; type RewardScheduler = Scheduler; - type Permissioned = pallet_staking::PolymeshStaking; + type Permissioned = Staking; } impl pallet_authority_discovery::Config for Runtime { diff --git a/pallets/runtime/tests/src/staking/mock.rs b/pallets/runtime/tests/src/staking/mock.rs index 8dd52527e6..1d234484ae 100644 --- a/pallets/runtime/tests/src/staking/mock.rs +++ b/pallets/runtime/tests/src/staking/mock.rs @@ -40,7 +40,7 @@ use sp_staking::{EraIndex, SessionIndex}; use pallet_balances::AccountData; use pallet_balances::BlockRewardConfig; use pallet_staking::types::SlashingSwitch; -use pallet_staking::{self as pallet_staking, *}; +use pallet_staking::*; use polymesh_primitives::asset::AssetId; use polymesh_primitives::constants::currency::POLY; use polymesh_primitives::identity_id::GenesisIdentityRecord; @@ -599,7 +599,7 @@ impl pallet_staking::Config for Test { type SlashDeferDuration = SlashDeferDuration; type AdminOrigin = frame_system::EnsureRoot; type SessionInterface = Self; - type EraPayout = ConvertCurve; + type EraPayout = PolymeshConvertCurve; type NextNewSession = Session; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; @@ -615,6 +615,7 @@ impl pallet_staking::Config for Test { type Call = RuntimeCall; type PalletsOrigin = OriginCaller; type RewardScheduler = Scheduler; + type Permissioned = Staking; } impl frame_system::offchain::SendTransactionTypes for Test diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index 155298fef7..9c4dac9b20 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -292,7 +292,7 @@ pub mod slashing; pub mod weights; pub mod permissioned; -pub use permissioned::{PolymeshConvertCurve, PolymeshStaking}; +pub use permissioned::PolymeshConvertCurve; pub mod types; pub mod pallet; diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index 430aa9d8a0..066a68175c 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -850,6 +850,7 @@ impl Pallet { if let Some(Nominations { targets, .. }) = >::get(&voter) { nominators_seen.saturating_inc(); + // Polymesh change: check if the nominator is compliant if T::Permissioned::is_nominator_compliant(&voter) { let voter_weight = weight_of(&voter); if !targets.is_empty() { @@ -863,6 +864,7 @@ impl Pallet { } } else if Validators::::contains_key(&voter) { validators_seen.saturating_inc(); + // Polymesh change: check if the validator is compliant if T::Permissioned::is_validator_compliant(&voter) { // if this voter is a validator: let self_vote = ( @@ -938,6 +940,7 @@ impl Pallet { if Validators::::contains_key(&target) { validators_seen.saturating_inc(); + // Polymesh change: check if the validator is compliant if T::Permissioned::is_validator_compliant(&target) { all_targets.push(target); } diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index 8f31ab9b2d..8c5d6aa4da 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -45,9 +45,7 @@ impl< } } -pub struct PolymeshStaking(sp_std::marker::PhantomData); - -impl PermissionedStaking for PolymeshStaking { +impl PermissionedStaking for Pallet { fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { // ensure their commission is correct. ensure!( @@ -153,7 +151,7 @@ impl PermissionedStaking for PolymeshStaking { } } -impl PolymeshStaking { +impl Pallet { /// Returns `true` if active balance is above [`MinValidatorBond`]. Otherwise, returns `false`. pub(crate) fn is_validator_active_balance_valid(who: &T::AccountId) -> bool { if let Some(controller) = Bonded::::get(&who) { @@ -192,13 +190,6 @@ impl PolymeshStaking { pub fn maximum_number_of_validators_per_identity() -> u32 { (T::MaxValidatorPerIdentity::get() * ValidatorCount::::get()).max(1) } -} - -impl Pallet { - /// Returns the maximum number of validators per identiy - pub fn maximum_number_of_validators_per_identity() -> u32 { - (T::MaxValidatorPerIdentity::get() * ValidatorCount::::get()).max(1) - } pub(crate) fn base_add_permissioned_validator( origin: OriginFor, From 7b8aee4788f7cce628b9e6f3ca261e86f043a83f Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Thu, 10 Apr 2025 01:16:20 +0800 Subject: [PATCH 08/10] Refactor some of our logic out of the benchmarking and testing code. --- pallets/staking/src/benchmarking.rs | 29 +++++++++++++-------------- pallets/staking/src/permissioned.rs | 30 ++++++++++++++++++++++++++++ pallets/staking/src/testing_utils.rs | 22 +++++--------------- pallets/staking/src/types.rs | 8 ++++++++ 4 files changed, 57 insertions(+), 32 deletions(-) diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs index 6e01352dfa..c753fa4664 100644 --- a/pallets/staking/src/benchmarking.rs +++ b/pallets/staking/src/benchmarking.rs @@ -52,9 +52,9 @@ type MaxNominators = <::BenchmarkingConfig as BenchmarkingConfig use pallet_identity::benchmarking::{User, UserBuilder}; use polymesh_primitives::identity_claim::ClaimType; -use polymesh_primitives::{IdentityId, Permissions}; +use polymesh_primitives::Permissions; -use crate::types::SlashingSwitch; +use crate::types::{PermissionedStaking, SlashingSwitch}; // ----------------------------------------------------------------- // Polymesh change @@ -66,11 +66,10 @@ macro_rules! whitelist_account { }; } -fn add_permissioned_validator_(id: IdentityId, intended_count: Option) { +fn add_permissioned_validator_(stash: &T::AccountId) { Staking::::set_validator_count(RawOrigin::Root.into(), 10) .expect("Failed to set the validator count"); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), id, intended_count) - .expect("Failed to add permissioned validator"); + T::Permissioned::permission_validator(stash); } // ----------------------------------------------------------------- @@ -120,7 +119,7 @@ where }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); Staking::::set_validator_count(RawOrigin::Root.into(), 10)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), v_stash.did(), Some(2))?; + T::Permissioned::permission_validator(&v_stash.account()); Staking::::validate(v_controller.origin().into(), validator_prefs)?; let stash_lookup = v_stash.lookup(); @@ -299,7 +298,7 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- - add_permissioned_validator_::(stash.did(), Some(2)); + add_permissioned_validator_::(&stash.account()); // ----------------------------------------------------------------- whitelist_account!(controller); @@ -328,7 +327,7 @@ benchmarks! { )?; let stash_lookup = stash.lookup(); - add_permissioned_validator_::(stash.did(), Some(2)); + add_permissioned_validator_::(&stash.account()); // they start validating. Staking::::validate(controller.origin().into(), Default::default())?; @@ -405,7 +404,7 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; assert!(T::VoterList::contains(&stash.account())); assert!(Validators::::contains_key(&stash.account())); @@ -473,7 +472,7 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(USER_SEED, 10_000_000, Default::default())?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; add_slashing_spans::(&stash.account(), s); assert!(T::VoterList::contains(&stash.account())); @@ -620,7 +619,7 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; add_slashing_spans::(&stash.account(), s); @@ -822,7 +821,7 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); Staking::::validate(controller.origin().into(), validator_prefs)?; assert!(T::VoterList::contains(&stash.account())); @@ -852,7 +851,7 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); Staking::::validate(controller.origin().into(), validator_prefs)?; // Sanity check @@ -897,7 +896,7 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(stash.did(), Some(1)); + add_permissioned_validator_::(&stash.account()); }: _(RawOrigin::Root, stash.did()) verify { let identity_preferences = Staking::::permissioned_identity(stash.did()); @@ -913,7 +912,7 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(stash.did(), Some(1)); + add_permissioned_validator_::(&stash.account()); }: _(RawOrigin::Root, stash.did(), 2) verify { assert_eq!(Staking::::permissioned_identity(stash.did()).unwrap().intended_count, 2); diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index 8c5d6aa4da..188a0076a1 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -10,6 +10,8 @@ use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; use polymesh_primitives::constants::GC_PALLET_ID; use polymesh_primitives::IdentityId; use polymesh_primitives::GC_DID; +#[cfg(feature = "runtime-benchmarks")] +use polymesh_primitives::{AuthorizationData, Permissions, Signatory}; use sp_runtime::traits::AccountIdConversion; @@ -46,6 +48,34 @@ impl< } impl PermissionedStaking for Pallet { + /// Permission a validator. + #[cfg(feature = "runtime-benchmarks")] + fn permission_validator(stash: &T::AccountId) { + let did = + pallet_identity::Pallet::::get_identity(stash).expect("Failed to get identity"); + Pallet::::add_permissioned_validator(RawOrigin::Root.into(), did, Some(2)) + .expect("Failed to add permissioned validator"); + } + + /// Setup stash and controller. + #[cfg(feature = "runtime-benchmarks")] + fn setup_stash_and_controller(stash: &T::AccountId, controller: &T::AccountId) { + let did = + pallet_identity::Pallet::::get_identity(stash).expect("Failed to get identity"); + let auth_id = pallet_identity::Pallet::::add_auth( + did, + Signatory::Account(controller.clone()), + AuthorizationData::JoinIdentity(Permissions::default()), + None, + ) + .expect("Failed to add auth"); + pallet_identity::Pallet::::join_identity_as_key( + RawOrigin::Signed(controller.clone()).into(), + auth_id, + ) + .expect("Failed to join identity as key"); + } + fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { // ensure their commission is correct. ensure!( diff --git a/pallets/staking/src/testing_utils.rs b/pallets/staking/src/testing_utils.rs index 089673a73b..0fc339d3f1 100644 --- a/pallets/staking/src/testing_utils.rs +++ b/pallets/staking/src/testing_utils.rs @@ -36,8 +36,8 @@ const SEED: u32 = 0; // Polymesh change // ----------------------------------------------------------------- +use crate::types::PermissionedStaking; use pallet_identity::benchmarking::{User, UserBuilder}; -use polymesh_primitives::{AuthorizationData, Permissions, Signatory}; // ----------------------------------------------------------------- /// This function removes all validators and nominators from storage. @@ -87,13 +87,7 @@ where // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - let auth_id = pallet_identity::Pallet::::add_auth( - stash.did(), - Signatory::Account(controller.account()), - AuthorizationData::JoinIdentity(Permissions::default()), - None, - )?; - pallet_identity::Pallet::::join_identity_as_key(controller.origin().into(), auth_id)?; + T::Permissioned::setup_stash_and_controller(&stash.account(), &controller.account()); // ----------------------------------------------------------------- let controller_lookup = controller.lookup(); @@ -128,13 +122,7 @@ where // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - let auth_id = pallet_identity::Pallet::::add_auth( - stash.did(), - Signatory::Account(controller.account()), - AuthorizationData::JoinIdentity(Permissions::default()), - None, - )?; - pallet_identity::Pallet::::join_identity_as_key(controller.origin().into(), auth_id)?; + T::Permissioned::setup_stash_and_controller(&stash.account(), &controller.account()); // ----------------------------------------------------------------- let controller_lookup = controller.lookup(); @@ -178,7 +166,7 @@ where }; // Polymesh change // ----------------------------------------------------------------- - Staking::::add_permissioned_validator(RawOrigin::Root.into(), stash.did(), Some(2))?; + T::Permissioned::permission_validator(&stash.account()); // ----------------------------------------------------------------- Staking::::validate(controller.origin().into(), validator_prefs)?; validators.push(stash.lookup()); @@ -227,7 +215,7 @@ where }; let (v_stash, v_controller) = create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; - Staking::::add_permissioned_validator(RawOrigin::Root.into(), v_stash.did(), Some(2))?; + T::Permissioned::permission_validator(&v_stash.account()); let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index 498fa19dc0..2de2a5a0ad 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -14,6 +14,14 @@ use crate::{ActiveEraInfo, Config}; /// identities to participate in staking. This trait is used to define the /// behavior of the staking pallet in such a network. pub trait PermissionedStaking { + /// Permission a validator. + #[cfg(feature = "runtime-benchmarks")] + fn permission_validator(_who: &T::AccountId) {} + + /// Setup stash and controller. + #[cfg(feature = "runtime-benchmarks")] + fn setup_stash_and_controller(_stash: &T::AccountId, _controller: &T::AccountId) {} + /// On validate hook. fn on_validate(_who: &T::AccountId, _commission: Perbill) -> DispatchResult { Ok(()) From 84af13fa34f34c014a15f8d7fba86a44476cb6d5 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Thu, 10 Apr 2025 02:00:21 +0800 Subject: [PATCH 09/10] Avoid using our User type. --- .../develop/src/benchmarks/pallet_session.rs | 3 +- pallets/staking/src/benchmarking.rs | 299 +++++++++--------- pallets/staking/src/testing_utils.rs | 61 ++-- 3 files changed, 180 insertions(+), 183 deletions(-) diff --git a/pallets/runtime/develop/src/benchmarks/pallet_session.rs b/pallets/runtime/develop/src/benchmarks/pallet_session.rs index 7a4cfa13b5..b0a01519bc 100644 --- a/pallets/runtime/develop/src/benchmarks/pallet_session.rs +++ b/pallets/runtime/develop/src/benchmarks/pallet_session.rs @@ -69,8 +69,7 @@ impl ValidatorInfo { Some(balance), ) .unwrap() - .0 - .account(); + .0; let controller = pallet_staking::Pallet::::bonded(&stash).expect("not stash"); let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap(); diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs index c753fa4664..92f9972229 100644 --- a/pallets/staking/src/benchmarking.rs +++ b/pallets/staking/src/benchmarking.rs @@ -36,7 +36,7 @@ use sp_staking::SessionIndex; use sp_std::prelude::*; pub use frame_benchmarking::v1::{ - account, benchmarks, impl_benchmark_test_suite, whitelisted_caller, + account, benchmarks, impl_benchmark_test_suite, whitelist_account, whitelisted_caller, }; use frame_system::RawOrigin; @@ -50,21 +50,15 @@ type MaxNominators = <::BenchmarkingConfig as BenchmarkingConfig // Polymesh change // ----------------------------------------------------------------- -use pallet_identity::benchmarking::{User, UserBuilder}; +use pallet_identity::benchmarking::UserBuilder; use polymesh_primitives::identity_claim::ClaimType; -use polymesh_primitives::Permissions; +use polymesh_primitives::{IdentityId, Permissions}; use crate::types::{PermissionedStaking, SlashingSwitch}; // ----------------------------------------------------------------- // Polymesh change // ----------------------------------------------------------------- -macro_rules! whitelist_account { - ($acc:expr) => { - let x = $acc.account(); - frame_benchmarking::v1::whitelist_account!(x); - }; -} fn add_permissioned_validator_(stash: &T::AccountId) { Staking::::set_validator_count(RawOrigin::Root.into(), 10) @@ -72,6 +66,10 @@ fn add_permissioned_validator_(stash: &T::AccountId) { T::Permissioned::permission_validator(stash); } +fn get_did(who: &T::AccountId) -> IdentityId { + pallet_identity::Pallet::::get_identity(who).expect("Failed to get identity id") +} + // ----------------------------------------------------------------- // Add slashing spans to a user account. Not relevant for actual use, only to benchmark @@ -101,7 +99,7 @@ pub fn create_validator_with_nominators( dead: bool, destination: RewardDestination, balance: Option, -) -> Result<(User, Vec<(User, User)>), &'static str> +) -> Result<(T::AccountId, Vec<(T::AccountId, T::AccountId)>), &'static str> where T: Config, { @@ -119,12 +117,12 @@ where }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); Staking::::set_validator_count(RawOrigin::Root.into(), 10)?; - T::Permissioned::permission_validator(&v_stash.account()); - Staking::::validate(v_controller.origin().into(), validator_prefs)?; - let stash_lookup = v_stash.lookup(); + T::Permissioned::permission_validator(&v_stash); + Staking::::validate(RawOrigin::Signed(v_controller).into(), validator_prefs)?; + let stash_lookup = T::Lookup::unlookup(v_stash.clone()); points_total += 10; - points_individual.push((v_stash.account().clone(), 10)); + points_individual.push((v_stash.clone(), 10)); let original_nominator_count = Nominators::::count(); let mut nominators = Vec::new(); @@ -138,7 +136,7 @@ where }; if i < n { Staking::::nominate( - RawOrigin::Signed(n_controller.account()).into(), + RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()], )?; nominators.push((n_stash, n_controller)); @@ -152,8 +150,7 @@ where assert_eq!(new_validators.len(), 1); assert_eq!( - new_validators[0], - v_stash.account(), + new_validators[0], v_stash, "Our validator was not selected!" ); assert_ne!(Validators::::count(), 0); @@ -184,13 +181,13 @@ benchmarks! { bond { let stash = create_funded_user::("stash", USER_SEED, 100); let controller = create_funded_user::("controller", USER_SEED, 100); - let controller_lookup = controller.lookup(); + let controller_lookup = T::Lookup::unlookup(controller.clone()); let reward_destination = RewardDestination::Staked; whitelist_account!(stash); - }: _(stash.origin(), controller_lookup, 2_000_000u32.into(), reward_destination) + }: _(RawOrigin::Signed(stash.clone()), controller_lookup, 2_000_000u32.into(), reward_destination) verify { - assert!(Bonded::::contains_key(stash.account())); - assert!(Ledger::::contains_key(controller.account())); + assert!(Bonded::::contains_key(stash)); + assert!(Ledger::::contains_key(controller)); } bond_extra { @@ -205,12 +202,12 @@ benchmarks! { let (stash, controller) = create_stash_controller::(USER_SEED, 100, RewardDestination::Staked).unwrap(); let original_bonded = - Ledger::::get(controller.account()).map(|l| l.active).ok_or("ledger not created")?; + Ledger::::get(controller.clone()).map(|l| l.active).ok_or("ledger not created")?; whitelist_account!(stash); - }: _(stash.origin(), 2_000_000u32.into()) + }: _(RawOrigin::Signed(stash), 2_000_000u32.into()) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded < new_bonded); } @@ -227,12 +224,12 @@ benchmarks! { let (stash, controller) = create_stash_controller::(USER_SEED, 100, RewardDestination::Staked).unwrap(); let original_bonded = - Ledger::::get(controller.account()).map(|l| l.active).ok_or("ledger not created")?; + Ledger::::get(controller.clone()).map(|l| l.active).ok_or("ledger not created")?; whitelist_account!(controller); - }: _(controller.origin(), 20u32.into()) + }: _(RawOrigin::Signed(controller.clone()), 20u32.into()) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded > new_bonded); } @@ -246,16 +243,16 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - add_slashing_spans::(&stash.account(), s); - Staking::::unbond(controller.origin().into(), 50u32.into())?; + add_slashing_spans::(&stash, s); + Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), 50u32.into())?; CurrentEra::::put(EraIndex::max_value()); let original_total = - Ledger::::get(controller.account()).map(|l| l.total).ok_or("ledger not created before")?; + Ledger::::get(controller.clone()).map(|l| l.total).ok_or("ledger not created before")?; whitelist_account!(controller); - }: withdraw_unbonded(controller.origin(), s) + }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { - let ledger = Ledger::::get(controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(controller).ok_or("ledger not created after")?; let new_total: BalanceOf = ledger.total; assert!(original_total > new_total); } @@ -273,16 +270,16 @@ benchmarks! { // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - add_slashing_spans::(&stash.account(), s); - Staking::::unbond(controller.origin().into(), 10_000_000u32.into())?; + add_slashing_spans::(&stash, s); + Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), 10_000_000u32.into())?; CurrentEra::::put(EraIndex::max_value()); - let _ = Ledger::::get(&controller.account()).expect("ledger not created before"); + let _ = Ledger::::get(&controller).expect("ledger not created before"); whitelist_account!(controller); - }: withdraw_unbonded(controller.origin(), s) + }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { - assert!(!Ledger::::contains_key(controller.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Ledger::::contains_key(controller)); + assert!(!T::VoterList::contains(&stash)); } validate { @@ -294,18 +291,18 @@ benchmarks! { Default::default(), )?; // because it is chilled. - assert!(!T::VoterList::contains(&stash.account())); + assert!(!T::VoterList::contains(&stash)); // Polymesh change // ----------------------------------------------------------------- - add_permissioned_validator_::(&stash.account()); + add_permissioned_validator_::(&stash); // ----------------------------------------------------------------- whitelist_account!(controller); - }: _(controller.origin(), ValidatorPrefs::default()) + }: _(RawOrigin::Signed(controller), ValidatorPrefs::default()) verify { - assert!(Validators::::contains_key(stash.account())); - assert!(T::VoterList::contains(&stash.account())); + assert!(Validators::::contains_key(stash.clone())); + assert!(T::VoterList::contains(&stash)); } kick { @@ -325,12 +322,12 @@ benchmarks! { 100, Default::default(), )?; - let stash_lookup = stash.lookup(); + let stash_lookup = T::Lookup::unlookup(stash.clone()); - add_permissioned_validator_::(&stash.account()); + add_permissioned_validator_::(&stash); // they start validating. - Staking::::validate(controller.origin().into(), Default::default())?; + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), Default::default())?; // we now create the nominators. there will be `k` of them; each will nominate all // validators. we will then kick each of the `k` nominators from the main validator. @@ -349,14 +346,14 @@ benchmarks! { // optimisations/pessimisations. nominations.insert(i as usize % (nominations.len() + 1), stash_lookup.clone()); // then we nominate. - Staking::::nominate(n_controller.origin().into(), nominations)?; + Staking::::nominate(RawOrigin::Signed(n_controller).into(), nominations)?; - nominator_stashes.push(n_stash.account()); + nominator_stashes.push(n_stash); } // all nominators now should be nominating our validator... for n in nominator_stashes.iter() { - assert!(Nominators::::get(n).unwrap().targets.contains(&stash.account())); + assert!(Nominators::::get(n).unwrap().targets.contains(&stash)); } // we need the unlookuped version of the nominator stash for the kick. @@ -365,11 +362,11 @@ benchmarks! { .collect::>(); whitelist_account!(controller); - }: _(controller.origin(), kicks) + }: _(RawOrigin::Signed(controller), kicks) verify { // all nominators now should *not* be nominating our validator... for n in nominator_stashes.iter() { - assert!(!Nominators::::get(n).unwrap().targets.contains(&stash.account())); + assert!(!Nominators::::get(n).unwrap().targets.contains(&stash)); } } @@ -385,16 +382,16 @@ benchmarks! { 10_000_000, Default::default(), )?; - assert!(!Nominators::::contains_key(&stash.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Nominators::::contains_key(&stash)); + assert!(!T::VoterList::contains(&stash)); let validators = create_validators::(n, 100).unwrap(); whitelist_account!(controller); - }: _(controller.origin(), validators) + }: _(RawOrigin::Signed(controller), validators) verify { - assert!(Nominators::::contains_key(&stash.account())); - assert!(T::VoterList::contains(&stash.account())) + assert!(Nominators::::contains_key(&stash)); + assert!(T::VoterList::contains(&stash)) } chill { @@ -404,35 +401,36 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(0, 10_000_000, Default::default())?; - T::Permissioned::permission_validator(&stash.account()); - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; - assert!(T::VoterList::contains(&stash.account())); - assert!(Validators::::contains_key(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; + assert!(T::VoterList::contains(&stash)); + assert!(Validators::::contains_key(&stash)); // ----------------------------------------------------------------- whitelist_account!(controller); - }: _(controller.origin()) + }: _(RawOrigin::Signed(controller)) verify { - assert!(!T::VoterList::contains(&stash.account())); - assert!(!Validators::::contains_key(&stash.account())); + assert!(!T::VoterList::contains(&stash)); + assert!(!Validators::::contains_key(&stash)); } set_payee { let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; - assert_eq!(Payee::::get(&stash.account()), RewardDestination::Staked); + assert_eq!(Payee::::get(&stash), RewardDestination::Staked); whitelist_account!(controller); - }: _(controller.origin(), RewardDestination::Controller) + }: _(RawOrigin::Signed(controller), RewardDestination::Controller) verify { - assert_eq!(Payee::::get(&stash.account()), RewardDestination::Controller); + assert_eq!(Payee::::get(&stash), RewardDestination::Controller); } set_controller { let (stash, _) = create_stash_controller::(USER_SEED, 100, Default::default())?; let new_controller = create_funded_user::("new_controller", USER_SEED, 100); + let new_controller_lookup = T::Lookup::unlookup(new_controller.clone()); whitelist_account!(stash); - }: _(stash.origin(), new_controller.lookup()) + }: _(RawOrigin::Signed(stash), new_controller_lookup) verify { - assert!(Ledger::::contains_key(&new_controller.account())); + assert!(Ledger::::contains_key(&new_controller)); } set_validator_count { @@ -472,18 +470,18 @@ benchmarks! { // Polymesh change // ----------------------------------------------------------------- let (stash, controller) = create_stash_controller::(USER_SEED, 10_000_000, Default::default())?; - T::Permissioned::permission_validator(&stash.account()); - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; - add_slashing_spans::(&stash.account(), s); - assert!(T::VoterList::contains(&stash.account())); - assert!(Validators::::contains_key(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; + add_slashing_spans::(&stash, s); + assert!(T::VoterList::contains(&stash)); + assert!(Validators::::contains_key(&stash)); // ----------------------------------------------------------------- - }: _(RawOrigin::Root, stash.account(), s) + }: _(RawOrigin::Root, stash.clone(), s) verify { - assert!(!Ledger::::contains_key(&controller.account())); - assert!(!T::VoterList::contains(&stash.account())); - assert!(!Validators::::contains_key(&stash.account())); + assert!(!Ledger::::contains_key(&controller)); + assert!(!T::VoterList::contains(&stash)); + assert!(!Validators::::contains_key(&stash)); } cancel_deferred_slash { @@ -514,19 +512,19 @@ benchmarks! { let current_era = CurrentEra::::get().unwrap(); // set the commission for this particular era as well. - >::insert(current_era, validator.account().clone(), >::validators(&validator.account())); + >::insert(current_era, validator.clone(), >::validators(&validator)); - let validator_controller = >::get(&validator.account()).unwrap(); + let validator_controller = >::get(&validator).unwrap(); let balance_before = T::Currency::free_balance(&validator_controller); for (_, controller) in &nominators { - let balance = T::Currency::free_balance(&controller.account()); + let balance = T::Currency::free_balance(&controller); ensure!(balance.is_zero(), "Controller has balance, but should be dead."); } - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - let caller_key = frame_system::Account::::hashed_key_for(&caller.account()); + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: payout_stakers(caller.origin(), validator.account(), current_era) + }: payout_stakers(RawOrigin::Signed(caller), validator, current_era) verify { let balance_after = T::Currency::free_balance(&validator_controller); ensure!( @@ -534,7 +532,7 @@ benchmarks! { "Balance of validator controller should have increased after payout.", ); for (_, controller) in &nominators { - let balance = T::Currency::free_balance(&controller.account()); + let balance = T::Currency::free_balance(&controller); ensure!(!balance.is_zero(), "Payout not given to controller."); } } @@ -554,29 +552,29 @@ benchmarks! { // set the commission for this particular era as well. >::insert( current_era, - validator.account().clone(), - >::validators(&validator.account()) + validator.clone(), + >::validators(&validator) ); - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - let caller_key = frame_system::Account::::hashed_key_for(&caller.account()); + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - let balance_before = T::Currency::free_balance(&validator.account()); + let balance_before = T::Currency::free_balance(&validator); let mut nominator_balances_before = Vec::new(); for (stash, _) in &nominators { - let balance = T::Currency::free_balance(&stash.account()); + let balance = T::Currency::free_balance(&stash); nominator_balances_before.push(balance); } - }: payout_stakers(caller.origin(), validator.account().clone(), current_era) + }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) verify { - let balance_after = T::Currency::free_balance(&validator.account()); + let balance_after = T::Currency::free_balance(&validator); ensure!( balance_before < balance_after, "Balance of validator stash should have increased after payout.", ); for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter()) { - let balance_after = T::Currency::free_balance(&stash.account()); + let balance_after = T::Currency::free_balance(&stash); ensure!( balance_before < &balance_after, "Balance of nominator stash should have increased after payout.", @@ -593,7 +591,7 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 10_000_000, RewardDestination::Staked)?; - let mut staking_ledger = Ledger::::get(controller.account()).unwrap(); + let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1u32.into(), era: EraIndex::zero(), @@ -601,13 +599,13 @@ benchmarks! { for _ in 0 .. l { staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap() } - Ledger::::insert(controller.account(), staking_ledger.clone()); + Ledger::::insert(controller.clone(), staking_ledger.clone()); let original_bonded: BalanceOf = staking_ledger.active; whitelist_account!(controller); - }: _(controller.origin(), 101u32.into()) + }: _(RawOrigin::Signed(controller.clone()), 101u32.into()) verify { - let ledger = Ledger::::get(&controller.account()).ok_or("ledger not created after")?; + let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; let new_bonded: BalanceOf = ledger.active; assert!(original_bonded < new_bonded); } @@ -619,28 +617,28 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - T::Permissioned::permission_validator(&stash.account()); - Staking::::validate(controller.origin().into(), ValidatorPrefs::default())?; + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), ValidatorPrefs::default())?; - add_slashing_spans::(&stash.account(), s); + add_slashing_spans::(&stash, s); let l = StakingLedger { - stash: stash.account().clone(), + stash: stash.clone(), active: T::Currency::minimum_balance(), total: T::Currency::minimum_balance(), unlocking: Default::default(), claimed_rewards: Default::default(), }; - T::Currency::make_free_balance_be(&stash.account(), 0u32.into()); - Ledger::::insert(&controller.account(), l); + T::Currency::make_free_balance_be(&stash, 0u32.into()); + Ledger::::insert(&controller, l); - assert!(Bonded::::contains_key(&stash.account())); - assert!(T::VoterList::contains(&stash.account())); + assert!(Bonded::::contains_key(&stash)); + assert!(T::VoterList::contains(&stash)); whitelist_account!(controller); - }: _(controller.origin(), stash.account(), s) + }: _(RawOrigin::Signed(controller), stash.clone(), s) verify { - assert!(!Bonded::::contains_key(&stash.account())); - assert!(!T::VoterList::contains(&stash.account())); + assert!(!Bonded::::contains_key(&stash)); + assert!(!T::VoterList::contains(&stash)); } new_era { @@ -716,7 +714,7 @@ benchmarks! { do_slash { let l in 1 .. T::MaxUnlockingChunks::get() as u32; let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; - let mut staking_ledger = Ledger::::get(controller.account()).unwrap(); + let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1u32.into(), era: EraIndex::zero(), @@ -724,18 +722,18 @@ benchmarks! { for _ in 0 .. l { staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap(); } - Ledger::::insert(controller.account(), staking_ledger); - let balance_before = T::Currency::free_balance(&stash.account()); + Ledger::::insert(controller, staking_ledger); + let balance_before = T::Currency::free_balance(&stash); }: { crate::slashing::do_slash::( - &stash.account(), + &stash, 10u32.into(), &mut BalanceOf::::zero(), &mut NegativeImbalanceOf::::zero(), EraIndex::zero() ); } verify { - let balance_after = T::Currency::free_balance(&stash.account()); + let balance_after = T::Currency::free_balance(&stash); assert!(balance_before > balance_after); } @@ -821,9 +819,9 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - T::Permissioned::permission_validator(&stash.account()); - Staking::::validate(controller.origin().into(), validator_prefs)?; - assert!(T::VoterList::contains(&stash.account())); + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller.clone()).into(), validator_prefs)?; + assert!(T::VoterList::contains(&stash)); Staking::::set_staking_configs( RawOrigin::Root.into(), @@ -835,10 +833,10 @@ benchmarks! { ConfigOp::Set(Zero::zero()), )?; - let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller"); - }: _(caller.origin(), controller.account()) + let caller = UserBuilder::::default().seed(SEED).generate_did().build("caller").account(); + }: _(RawOrigin::Signed(caller), controller) verify { - assert!(!T::VoterList::contains(&stash.account())); + assert!(!T::VoterList::contains(&stash)); } force_apply_min_commission { @@ -851,22 +849,23 @@ benchmarks! { let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; Staking::::set_commission_cap(RawOrigin::Root.into(), Perbill::from_percent(60)).unwrap(); - T::Permissioned::permission_validator(&stash.account()); - Staking::::validate(controller.origin().into(), validator_prefs)?; + T::Permissioned::permission_validator(&stash); + Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; // Sanity check assert_eq!( - Validators::::get(&stash.account()), + Validators::::get(&stash), ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() } ); // Set the min commission to 75% MinCommission::::set(Perbill::from_percent(75)); - }: _(stash.origin(), stash.account().clone()) + let caller = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), stash.clone()) verify { // The validators commission has been bumped to 75% assert_eq!( - Validators::::get(&stash.account()), + Validators::::get(&stash), ValidatorPrefs { commission: Perbill::from_percent(75), ..Default::default() } ); } @@ -885,9 +884,10 @@ benchmarks! { let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; Staking::::set_validator_count(RawOrigin::Root.into(), 10).unwrap(); - }: _(RawOrigin::Root, stash.did(), Some(1)) + let did = get_did::(&stash); + }: _(RawOrigin::Root, did, Some(1)) verify { - let identity_preferences = Staking::::permissioned_identity(stash.did()); + let identity_preferences = Staking::::permissioned_identity(did); assert!(identity_preferences.is_some()); assert_eq!(identity_preferences.unwrap().intended_count, 1); } @@ -896,10 +896,11 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(&stash.account()); - }: _(RawOrigin::Root, stash.did()) + add_permissioned_validator_::(&stash); + let did = get_did::(&stash); + }: _(RawOrigin::Root, did) verify { - let identity_preferences = Staking::::permissioned_identity(stash.did()); + let identity_preferences = Staking::::permissioned_identity(did); assert!(identity_preferences.is_none()); } @@ -912,10 +913,11 @@ benchmarks! { clear_validators_and_nominators::(); let (stash, controller) = create_stash_controller::(1, 1, RewardDestination::Staked)?; - add_permissioned_validator_::(&stash.account()); - }: _(RawOrigin::Root, stash.did(), 2) + add_permissioned_validator_::(&stash); + let did = get_did::(&stash); + }: _(RawOrigin::Root, did, 2) verify { - assert_eq!(Staking::::permissioned_identity(stash.did()).unwrap().intended_count, 2); + assert_eq!(Staking::::permissioned_identity(did).unwrap().intended_count, 2); } chill_from_governance { @@ -926,9 +928,10 @@ benchmarks! { Staking::::set_validator_count(RawOrigin::Root.into(), 1_000).unwrap(); assert_eq!(Staking::::validator_count(), 1_000); + let did = get_did::(&validator); Staking::::add_permissioned_validator( RawOrigin::Root.into(), - validator.did(), + did, Some(100) ).unwrap(); @@ -936,28 +939,31 @@ benchmarks! { let mut signatories = Vec::new(); for x in 0 .. s { - let key = UserBuilder::::default().seed(x).balance(10_000u32).build("key"); + let key = UserBuilder::::default().seed(x).balance(10_000u32).build("key").account(); + let key_lookup = T::Lookup::unlookup(key.clone()); let _ = T::Currency::issue(10_000u32.into()); + let did = get_did::(&validator); pallet_identity::Pallet::::unsafe_join_identity( - validator.did(), + did, Permissions::default(), - key.account.clone() + key.clone() ); Staking::::bond( - key.origin().into(), - key.lookup(), + RawOrigin::Signed(key.clone()).into(), + key_lookup, 2_000_000u32.into(), RewardDestination::Staked ) .unwrap(); whitelist_account!(key); - Staking::::validate(key.origin().into(), ValidatorPrefs::default()).unwrap(); - assert_eq!(>::contains_key(&key.account), true); - signatories.push(key.account.clone()); + Staking::::validate(RawOrigin::Signed(key.clone()).into(), ValidatorPrefs::default()).unwrap(); + assert_eq!(>::contains_key(&key), true); + signatories.push(key.clone()); } - }: _(RawOrigin::Root, validator.did(), signatories.clone()) + let did = get_did::(&validator); + }: _(RawOrigin::Root, did, signatories.clone()) verify { for key in signatories { assert!(!>::contains_key(&key)); @@ -970,9 +976,9 @@ benchmarks! { let mut stashes = Vec::with_capacity(m as usize); for i in 0 .. m { let stash = create_funded_user::("stash", i, 1000); - stashes.push(stash.account()); + stashes.push(stash.clone()); Validators::::insert( - stash.account(), + stash, ValidatorPrefs { commission: Perbill::from_percent(70), ..Default::default() @@ -1003,14 +1009,15 @@ benchmarks! { )?; for nominator in &nominators { + let did = get_did::(&nominator.0); let claim_first = pallet_identity::Claim1stKey { - target: nominator.0.did(), + target: did, claim_type: ClaimType::CustomerDueDiligence }; let _ = pallet_identity::Claims::::clear_prefix(claim_first, 1, None); } - let nominators: Vec = nominators.iter().map(|x| x.0.account()).collect(); + let nominators: Vec = nominators.iter().map(|x| x.0.clone()).collect(); for nominator in &nominators { assert!(Nominators::::contains_key(nominator)); } diff --git a/pallets/staking/src/testing_utils.rs b/pallets/staking/src/testing_utils.rs index 0fc339d3f1..d4267fbe9f 100644 --- a/pallets/staking/src/testing_utils.rs +++ b/pallets/staking/src/testing_utils.rs @@ -37,7 +37,7 @@ const SEED: u32 = 0; // Polymesh change // ----------------------------------------------------------------- use crate::types::PermissionedStaking; -use pallet_identity::benchmarking::{User, UserBuilder}; +use pallet_identity::benchmarking::UserBuilder; // ----------------------------------------------------------------- /// This function removes all validators and nominators from storage. @@ -54,10 +54,7 @@ pub fn clear_validators_and_nominators() { } /// Grab a funded user. -pub fn create_funded_user(string: &'static str, n: u32, balance: u32) -> User -where - T: Config, -{ +pub fn create_funded_user(string: &'static str, n: u32, balance: u32) -> T::AccountId { // Polymesh change // ----------------------------------------------------------------- let _ = T::Currency::issue(balance.into()); @@ -66,33 +63,32 @@ where .seed(n) .generate_did() .build(string) + .account() // ----------------------------------------------------------------- } /// Create a stash and controller pair. -pub fn create_stash_controller( +pub fn create_stash_controller( n: u32, balance: u32, destination: RewardDestination, -) -> Result<(User, User), &'static str> -where - T: Config, -{ +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance); let controller = UserBuilder::::default() .balance(balance) .seed(n) - .build("controller"); + .build("controller") + .account(); + let controller_lookup = T::Lookup::unlookup(controller.clone()); // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - T::Permissioned::setup_stash_and_controller(&stash.account(), &controller.account()); + T::Permissioned::setup_stash_and_controller(&stash, &controller); // ----------------------------------------------------------------- - let controller_lookup = controller.lookup(); Staking::::bond( - stash.origin().into(), + RawOrigin::Signed(stash.clone()).into(), controller_lookup, (balance / 10).into(), destination, @@ -106,28 +102,19 @@ pub fn create_stash_and_dead_controller( n: u32, balance: u32, destination: RewardDestination, -) -> Result<(User, User), &'static str> -where - T: Config, -{ +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance); - let controller_account: T::AccountId = account("controller", n, 100); - let controller = User { - account: controller_account.clone(), - origin: RawOrigin::Signed(controller_account), - did: None, - secret: None, - }; + let controller: T::AccountId = account("controller", n, 100); + let controller_lookup = T::Lookup::unlookup(controller.clone()); // Polymesh change // ----------------------------------------------------------------- // Attach the controller key as secondary key of the stash - T::Permissioned::setup_stash_and_controller(&stash.account(), &controller.account()); + T::Permissioned::setup_stash_and_controller(&stash, &controller); // ----------------------------------------------------------------- - let controller_lookup = controller.lookup(); Staking::::bond( - stash.origin().into(), + RawOrigin::Signed(stash.clone()).into(), controller_lookup, (balance / 10).into(), destination, @@ -166,10 +153,11 @@ where }; // Polymesh change // ----------------------------------------------------------------- - T::Permissioned::permission_validator(&stash.account()); + T::Permissioned::permission_validator(&stash); // ----------------------------------------------------------------- - Staking::::validate(controller.origin().into(), validator_prefs)?; - validators.push(stash.lookup()); + Staking::::validate(RawOrigin::Signed(controller).into(), validator_prefs)?; + let stash_lookup = T::Lookup::unlookup(stash); + validators.push(stash_lookup); } Ok(validators) @@ -215,13 +203,16 @@ where }; let (v_stash, v_controller) = create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; - T::Permissioned::permission_validator(&v_stash.account()); + T::Permissioned::permission_validator(&v_stash); let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() }; - Staking::::validate(v_controller.origin().into(), validator_prefs)?; - let stash_lookup = v_stash.lookup(); + Staking::::validate( + RawOrigin::Signed(v_controller.clone()).into(), + validator_prefs, + )?; + let stash_lookup = T::Lookup::unlookup(v_stash.clone()); validators_stash.push(stash_lookup.clone()); } @@ -248,7 +239,7 @@ where let validator = available_validators.remove(selected); selected_validators.push(validator); } - Staking::::nominate(n_controller.origin().into(), selected_validators)?; + Staking::::nominate(RawOrigin::Signed(n_controller).into(), selected_validators)?; } ValidatorCount::::put(validators); From 7a229bfc074f5d7300d09a966675e94a146deac0 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Tue, 15 Apr 2025 23:53:37 +0800 Subject: [PATCH 10/10] Add ED check to trait. --- pallets/staking/src/pallet/impls.rs | 34 ++++++++++++++--------------- pallets/staking/src/pallet/mod.rs | 14 ++++++------ pallets/staking/src/permissioned.rs | 6 +++++ pallets/staking/src/types.rs | 8 ++++++- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/pallets/staking/src/pallet/impls.rs b/pallets/staking/src/pallet/impls.rs index 066a68175c..d4e8c2de30 100644 --- a/pallets/staking/src/pallet/impls.rs +++ b/pallets/staking/src/pallet/impls.rs @@ -117,24 +117,24 @@ impl Pallet { } // Polymesh change: - // ledger.active <= minimum_balance() - let used_weight = - if ledger.unlocking.is_empty() && ledger.active <= T::Currency::minimum_balance() { - // This account must have called `unbond()` with some value that caused the active - // portion to fall below existential deposit + will have no more unlocking chunks - // left. We can now safely remove all staking-related information. - Self::kill_stash(&stash, num_slashing_spans)?; - // Remove the lock. - T::Currency::remove_lock(STAKING_ID, &stash); - - ::WeightInfo::withdraw_unbonded_kill(num_slashing_spans) - } else { - // This was the consequence of a partial unbond. just update the ledger and move on. - Self::update_ledger(&controller, &ledger); + // use T::Permissioned::reapable(amount) + let used_weight = if ledger.unlocking.is_empty() && T::Permissioned::reapable(ledger.active) + { + // This account must have called `unbond()` with some value that caused the active + // portion to fall below existential deposit + will have no more unlocking chunks + // left. We can now safely remove all staking-related information. + Self::kill_stash(&stash, num_slashing_spans)?; + // Remove the lock. + T::Currency::remove_lock(STAKING_ID, &stash); + + ::WeightInfo::withdraw_unbonded_kill(num_slashing_spans) + } else { + // This was the consequence of a partial unbond. just update the ledger and move on. + Self::update_ledger(&controller, &ledger); - // This is only an update, so we use less overall weight. - ::WeightInfo::withdraw_unbonded_update(num_slashing_spans) - }; + // This is only an update, so we use less overall weight. + ::WeightInfo::withdraw_unbonded_update(num_slashing_spans) + }; // `old_total` should never be less than the new total because // `consolidate_unlocked` strictly subtracts balance. diff --git a/pallets/staking/src/pallet/mod.rs b/pallets/staking/src/pallet/mod.rs index aaf93d6c5d..5c70afad56 100644 --- a/pallets/staking/src/pallet/mod.rs +++ b/pallets/staking/src/pallet/mod.rs @@ -1823,14 +1823,14 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let _ = ensure_signed(origin)?; - let ed = T::Currency::minimum_balance(); // Polymesh change: - // total_balance(stash) <= ed - let reapable = T::Currency::total_balance(&stash) <= ed - || Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) - .map(|l| l.total) - .unwrap_or_default() - < ed; + // use T::Permissioned::reapable(amount) + let reapable = T::Permissioned::reapable(T::Currency::total_balance(&stash)) + || T::Permissioned::reapable( + Self::ledger(Self::bonded(stash.clone()).ok_or(Error::::NotStash)?) + .map(|l| l.total) + .unwrap_or_default(), + ); ensure!(reapable, Error::::FundedTarget); Self::kill_stash(&stash, num_slashing_spans)?; diff --git a/pallets/staking/src/permissioned.rs b/pallets/staking/src/permissioned.rs index 188a0076a1..fee9473252 100644 --- a/pallets/staking/src/permissioned.rs +++ b/pallets/staking/src/permissioned.rs @@ -6,6 +6,7 @@ use sp_std::prelude::*; use frame_support::traits::schedule::Anon; use frame_support::traits::schedule::{DispatchTime, HIGHEST_PRIORITY}; +use frame_support::traits::Currency; use polymesh_primitives::constants::GC_PALLET_ID; use polymesh_primitives::IdentityId; @@ -76,6 +77,11 @@ impl PermissionedStaking for Pallet { .expect("Failed to join identity as key"); } + /// Check if amount is under the existential deposit. + fn reapable(amount: BalanceOf) -> bool { + amount <= T::Currency::minimum_balance() + } + fn on_validate(stash: &T::AccountId, commission: Perbill) -> DispatchResult { // ensure their commission is correct. ensure!( diff --git a/pallets/staking/src/types.rs b/pallets/staking/src/types.rs index 2de2a5a0ad..2a36226f5f 100644 --- a/pallets/staking/src/types.rs +++ b/pallets/staking/src/types.rs @@ -3,10 +3,11 @@ use sp_runtime::{Deserialize, Serialize}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::dispatch::DispatchResult; +use frame_support::traits::Currency; use scale_info::TypeInfo; use sp_runtime::{Perbill, RuntimeDebug}; -use crate::{ActiveEraInfo, Config}; +use crate::{ActiveEraInfo, BalanceOf, Config}; /// A trait used by the staking pallet for permissioned staking. /// @@ -22,6 +23,11 @@ pub trait PermissionedStaking { #[cfg(feature = "runtime-benchmarks")] fn setup_stash_and_controller(_stash: &T::AccountId, _controller: &T::AccountId) {} + /// Check if amount is under the existential deposit. + fn reapable(amount: BalanceOf) -> bool { + amount < T::Currency::minimum_balance() + } + /// On validate hook. fn on_validate(_who: &T::AccountId, _commission: Perbill) -> DispatchResult { Ok(())