Skip to content

Commit

Permalink
Bound some storage items for pallet staking and clean up deprecated…
Browse files Browse the repository at this point in the history
… exposures (#7483)

Building from #6445 on top of #7282 

**Changes**

- [x] Bound `Invulnerables`, vector of validators invulnerable to
slashing.
- Add `MaxInvulnerables` to bound `Invulnerables` Vec -> `BoundedVec`.
- Set to constant 20 in the pallet (must be >= 17 for backward
compatibility with runtime `westend`).
- [x] Bound `Disabled Validators`, vector of validators that have
offended in a given era and have been disabled.
- Add `MaxDisabledValidators` to bound `DisabledValidators` Vec ->
`BoundedVec`.
- Set to constant 100 in the pallet (it should be <= 1/3 *
`MaxValidatorsCount` according to the current disabling strategy).
- [x] Remove `ErasStakers` and `ErasStakersClipped` (see #433 ),
non-paged validators exposures.
- They were deprecated in v14 and could have been removed since staking
era 1504 (now it's > 1700).
    - They are already empty on Polkadot and Kusama.
    - Completing the task from #5986.

Migrating pallet `staking` storage to v17 to apply all changes.  

**TO DO** (in a follow-up PR)
- [ ] Bound `ErasStakersPaged`
    - this needs bounding `ExposurePage.others` vector
- [ ] Bound `BondedEras` vector
- [ ] Bound `ClaimedRewards` pages vector
- [ ] Bound `ErasRewardPoints`
    - this needs bounding `EraRewardPoints.individual` BTreeMap 
- [ ] Bound `UnappliedSlashes`
- [ ] Bound `SlashingSpans` 
    - this needs bounding `SlashingSpans.prior` vector

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: kianenigma <kian@parity.io>
  • Loading branch information
3 people authored Feb 10, 2025
1 parent bb1f89a commit 0fa010d
Show file tree
Hide file tree
Showing 23 changed files with 176 additions and 626 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
use sp_consensus_babe::AuthorityId as BabeId;
use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
use sp_core::storage::Storage;
use sp_runtime::Perbill;
use sp_runtime::{BoundedVec, Perbill};

// Polkadot
use polkadot_primitives::{AssignmentId, ValidatorId};
Expand Down Expand Up @@ -87,7 +87,13 @@ pub fn genesis() -> Storage {
.iter()
.map(|x| (x.0.clone(), x.1.clone(), STASH, pallet_staking::StakerStatus::Validator))
.collect(),
invulnerables: validators::initial_authorities().iter().map(|x| x.0.clone()).collect(),
invulnerables: BoundedVec::try_from(
validators::initial_authorities()
.iter()
.map(|x| x.0.clone())
.collect::<Vec<_>>(),
)
.expect("Limit for staking invulnerables must be less than initial authorities."),
force_era: pallet_staking::Forcing::ForceNone,
slash_reward_fraction: Perbill::from_percent(10),
..Default::default()
Expand Down
2 changes: 1 addition & 1 deletion polkadot/runtime/common/src/try_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ where

let all_stakers = Ledger::<T>::iter().map(|(ctrl, l)| (ctrl, l.stash)).collect::<BTreeSet<_>>();
let mut all_exposed = BTreeSet::new();
ErasStakers::<T>::iter().for_each(|(_, val, expo)| {
ErasStakersPaged::<T>::iter().for_each(|((_era, val, _page), expo)| {
all_exposed.insert(val);
all_exposed.extend(expo.others.iter().map(|ie| ie.who.clone()))
});
Expand Down
2 changes: 2 additions & 0 deletions polkadot/runtime/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ impl pallet_staking::Config for Runtime {
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
type MaxValidatorSet = MaxAuthorities;
type MaxInvulnerables = ConstU32<20>;
type MaxDisabledValidators = ConstU32<100>;
}

parameter_types! {
Expand Down
12 changes: 9 additions & 3 deletions polkadot/runtime/westend/src/genesis_config_presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::{crypto::get_public_from_string_or_panic, sr25519};
use sp_genesis_builder::PresetId;
use sp_keyring::Sr25519Keyring;
use sp_runtime::Perbill;
use sp_runtime::{BoundedVec, Perbill};
use westend_runtime_constants::currency::UNITS as WND;

/// Helper function to generate stash, controller and session key from seed
Expand Down Expand Up @@ -202,7 +202,10 @@ fn westend_testnet_genesis(
.iter()
.map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::<AccountId>::Validator))
.collect::<Vec<_>>(),
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
invulnerables: BoundedVec::try_from(
initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>()
)
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
force_era: Forcing::NotForcing,
slash_reward_fraction: Perbill::from_percent(10),
},
Expand Down Expand Up @@ -373,7 +376,10 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value {
.iter()
.map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::<AccountId>::Validator))
.collect::<Vec<_>>(),
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
invulnerables: BoundedVec::try_from(
initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>()
)
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
force_era: Forcing::ForceNone,
slash_reward_fraction: Perbill::from_percent(10),
},
Expand Down
3 changes: 2 additions & 1 deletion polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ impl pallet_staking::Config for Runtime {
type EventListeners = (NominationPools, DelegatedStaking);
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
type MaxInvulnerables = frame_support::traits::ConstU32<20>;
type MaxDisabledValidators = ConstU32<100>;
}

impl pallet_fast_unstake::Config for Runtime {
Expand Down Expand Up @@ -1867,7 +1869,6 @@ pub mod migrations {
parachains_shared::migration::MigrateToV1<Runtime>,
parachains_scheduler::migration::MigrateV2ToV3<Runtime>,
pallet_staking::migrations::v16::MigrateV15ToV16<Runtime>,
pallet_staking::migrations::v17::MigrateV16ToV17<Runtime>,
// permanent
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
);
Expand Down
2 changes: 0 additions & 2 deletions polkadot/runtime/westend/src/weights/pallet_fast_unstake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ impl<T: frame_system::Config> pallet_fast_unstake::WeightInfo for WeightInfo<T>
/// Proof Skipped: ElectionProviderMultiPhase CurrentPhase (max_values: Some(1), max_size: None, mode: Measured)
/// Storage: Staking CurrentEra (r:1 w:0)
/// Proof: Staking CurrentEra (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
/// Storage: Staking ErasStakers (r:257 w:0)
/// Proof Skipped: Staking ErasStakers (max_values: None, max_size: None, mode: Measured)
/// The range of component `v` is `[1, 256]`.
/// The range of component `b` is `[1, 64]`.
fn on_idle_check(v: u32, b: u32, ) -> Weight {
Expand Down
2 changes: 0 additions & 2 deletions polkadot/runtime/westend/src/weights/pallet_staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,6 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::Ledger` (r:65 w:65)
/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStakersClipped` (r:1 w:0)
/// Proof: `Staking::ErasStakersClipped` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ErasStakersOverview` (r:1 w:0)
/// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`)
/// Storage: `Staking::ClaimedRewards` (r:1 w:1)
Expand Down
2 changes: 2 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,8 @@ impl pallet_staking::Config for Runtime {
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type BenchmarkingConfig = StakingBenchmarkingConfig;
type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
type MaxInvulnerables = ConstU32<20>;
type MaxDisabledValidators = ConstU32<100>;
}

impl pallet_fast_unstake::Config for Runtime {
Expand Down
5 changes: 3 additions & 2 deletions substrate/bin/node/testing/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use kitchensink_runtime::{
RuntimeGenesisConfig, SessionConfig, SocietyConfig, StakerStatus, StakingConfig,
};
use sp_keyring::Ed25519Keyring;
use sp_runtime::Perbill;
use sp_runtime::{BoundedVec, Perbill};

/// Create genesis runtime configuration for tests.
pub fn config() -> RuntimeGenesisConfig {
Expand Down Expand Up @@ -65,7 +65,8 @@ pub fn config_endowed(extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig {
validator_count: 3,
minimum_validator_count: 0,
slash_reward_fraction: Perbill::from_percent(10),
invulnerables: vec![alice(), bob(), charlie()],
invulnerables: BoundedVec::try_from(vec![alice(), bob(), charlie()])
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
..Default::default()
},
society: SocietyConfig { pot: 0 },
Expand Down
4 changes: 2 additions & 2 deletions substrate/frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use sp_runtime::{
impl_opaque_keys,
testing::{Digest, DigestItem, Header, TestXt},
traits::{Header as _, OpaqueKeys},
BuildStorage, Perbill,
BoundedVec, BuildStorage, Perbill,
};
use sp_staking::{EraIndex, SessionIndex};

Expand Down Expand Up @@ -345,7 +345,7 @@ pub fn new_test_ext_raw_authorities(authorities: Vec<AuthorityId>) -> sp_io::Tes
validator_count: 8,
force_era: pallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
invulnerables: BoundedVec::new(),
..Default::default()
};

Expand Down
4 changes: 2 additions & 2 deletions substrate/frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use sp_runtime::{
impl_opaque_keys,
testing::TestXt,
traits::{Header as HeaderT, OpaqueKeys},
BuildStorage, Perbill,
BoundedVec, BuildStorage, Perbill,
};
use sp_staking::{EraIndex, SessionIndex};
use sp_state_machine::BasicExternalities;
Expand Down Expand Up @@ -315,7 +315,7 @@ impl ExtBuilder {
validator_count: 2,
force_era: pallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
invulnerables: BoundedVec::new(),
..Default::default()
};

Expand Down
4 changes: 2 additions & 2 deletions substrate/frame/delegated-staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use frame_support::{
PalletId,
};

use sp_runtime::{traits::IdentityLookup, BuildStorage, Perbill};
use sp_runtime::{traits::IdentityLookup, BoundedVec, BuildStorage, Perbill};

use frame_election_provider_support::{
bounds::{ElectionBounds, ElectionBoundsBuilder},
Expand Down Expand Up @@ -221,7 +221,7 @@ impl ExtBuilder {
// ideal validator count
validator_count: 2,
minimum_validator_count: 1,
invulnerables: vec![],
invulnerables: BoundedVec::new(),
slash_reward_fraction: Perbill::from_percent(10),
min_nominator_bond: ExistentialDeposit::get(),
min_validator_bond: ExistentialDeposit::get(),
Expand Down
4 changes: 2 additions & 2 deletions substrate/frame/grandpa/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use sp_runtime::{
impl_opaque_keys,
testing::{TestXt, UintAuthorityId},
traits::OpaqueKeys,
BuildStorage, DigestItem, Perbill,
BoundedVec, BuildStorage, DigestItem, Perbill,
};
use sp_staking::{EraIndex, SessionIndex};

Expand Down Expand Up @@ -261,7 +261,7 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx
validator_count: 8,
force_era: pallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
invulnerables: BoundedVec::new(),
..Default::default()
};

Expand Down
4 changes: 2 additions & 2 deletions substrate/frame/root-offences/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl Config for Test {
pub struct ExtBuilder {
validator_count: u32,
minimum_validator_count: u32,
invulnerables: Vec<AccountId>,
invulnerables: BoundedVec<AccountId, <Test as pallet_staking::Config>::MaxInvulnerables>,
balance_factor: Balance,
}

Expand All @@ -192,7 +192,7 @@ impl Default for ExtBuilder {
Self {
validator_count: 2,
minimum_validator_count: 0,
invulnerables: vec![],
invulnerables: BoundedVec::new(),
balance_factor: 1,
}
}
Expand Down
2 changes: 1 addition & 1 deletion substrate/frame/staking/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ mod benchmarks {

#[benchmark]
// Worst case scenario, the list of invulnerables is very long.
fn set_invulnerables(v: Linear<0, { BenchMaxValidators::<T>::get() }>) {
fn set_invulnerables(v: Linear<0, { T::MaxInvulnerables::get() }>) {
let mut invulnerables = Vec::new();
for i in 0..v {
invulnerables.push(account("invulnerable", i, SEED));
Expand Down
87 changes: 10 additions & 77 deletions substrate/frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,44 +1109,12 @@ where
pub struct EraInfo<T>(core::marker::PhantomData<T>);
impl<T: Config> EraInfo<T> {
/// Returns true if validator has one or more page of era rewards not claimed yet.
// Also looks at legacy storage that can be cleaned up after #433.
pub fn pending_rewards(era: EraIndex, validator: &T::AccountId) -> bool {
let page_count = if let Some(overview) = <ErasStakersOverview<T>>::get(&era, validator) {
overview.page_count
} else {
if <ErasStakers<T>>::contains_key(era, validator) {
// this means non paged exposure, and we treat them as single paged.
1
} else {
// if no exposure, then no rewards to claim.
return false
}
};

// check if era is marked claimed in legacy storage.
if <Ledger<T>>::get(validator)
.map(|l| l.legacy_claimed_rewards.contains(&era))
.unwrap_or_default()
{
return false
}

ClaimedRewards::<T>::get(era, validator).len() < page_count as usize
}

/// Temporary function which looks at both (1) passed param `T::StakingLedger` for legacy
/// non-paged rewards, and (2) `T::ClaimedRewards` for paged rewards. This function can be
/// removed once `T::HistoryDepth` eras have passed and none of the older non-paged rewards
/// are relevant/claimable.
// Refer tracker issue for cleanup: https://github.com/paritytech/polkadot-sdk/issues/433
pub(crate) fn is_rewards_claimed_with_legacy_fallback(
era: EraIndex,
ledger: &StakingLedger<T>,
validator: &T::AccountId,
page: Page,
) -> bool {
ledger.legacy_claimed_rewards.binary_search(&era).is_ok() ||
Self::is_rewards_claimed(era, validator, page)
<ErasStakersOverview<T>>::get(&era, validator)
.map(|overview| {
ClaimedRewards::<T>::get(era, validator).len() < overview.page_count as usize
})
.unwrap_or(false)
}

/// Check if the rewards for the given era and page index have been claimed.
Expand All @@ -1167,20 +1135,7 @@ impl<T: Config> EraInfo<T> {
validator: &T::AccountId,
page: Page,
) -> Option<PagedExposure<T::AccountId, BalanceOf<T>>> {
let overview = <ErasStakersOverview<T>>::get(&era, validator);

// return clipped exposure if page zero and paged exposure does not exist
// exists for backward compatibility and can be removed as part of #13034
if overview.is_none() && page == 0 {
return Some(PagedExposure::from_clipped(<ErasStakersClipped<T>>::get(era, validator)))
}

// no exposure for this validator
if overview.is_none() {
return None
}

let overview = overview.expect("checked above; qed");
let overview = <ErasStakersOverview<T>>::get(&era, validator)?;

// validator stake is added only in page zero
let validator_stake = if page == 0 { overview.own } else { Zero::zero() };
Expand All @@ -1201,13 +1156,9 @@ impl<T: Config> EraInfo<T> {
era: EraIndex,
validator: &T::AccountId,
) -> Exposure<T::AccountId, BalanceOf<T>> {
let overview = <ErasStakersOverview<T>>::get(&era, validator);

if overview.is_none() {
return ErasStakers::<T>::get(era, validator)
}

let overview = overview.expect("checked above; qed");
let Some(overview) = <ErasStakersOverview<T>>::get(&era, validator) else {
return Exposure::default();
};

let mut others = Vec::with_capacity(overview.nominator_count as usize);
for page in 0..overview.page_count {
Expand Down Expand Up @@ -1238,20 +1189,7 @@ impl<T: Config> EraInfo<T> {
}

/// Returns the next page that can be claimed or `None` if nothing to claim.
pub(crate) fn get_next_claimable_page(
era: EraIndex,
validator: &T::AccountId,
ledger: &StakingLedger<T>,
) -> Option<Page> {
if Self::is_non_paged_exposure(era, validator) {
return match ledger.legacy_claimed_rewards.binary_search(&era) {
// already claimed
Ok(_) => None,
// Non-paged exposure is considered as a single page
Err(_) => Some(0),
}
}

pub(crate) fn get_next_claimable_page(era: EraIndex, validator: &T::AccountId) -> Option<Page> {
// Find next claimable page of paged exposure.
let page_count = Self::get_page_count(era, validator);
let all_claimable_pages: Vec<Page> = (0..page_count).collect();
Expand All @@ -1260,11 +1198,6 @@ impl<T: Config> EraInfo<T> {
all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p))
}

/// Checks if exposure is paged or not.
fn is_non_paged_exposure(era: EraIndex, validator: &T::AccountId) -> bool {
<ErasStakersClipped<T>>::contains_key(&era, validator)
}

/// Returns validator commission for this era and page.
pub(crate) fn get_validator_commission(
era: EraIndex,
Expand Down
Loading

0 comments on commit 0fa010d

Please sign in to comment.