Skip to content

Commit a025562

Browse files
kianenigmagpestanaAnk4ngui1117re-gius
authored
[AHM] Multi-block staking election pallet (#7282)
## Multi Block Election Pallet This PR adds the first iteration of the multi-block staking pallet. From this point onwards, the staking and its election provider pallets are being customized to work in AssetHub. While usage in solo-chains is still possible, it is not longer the main focus of this pallet. For a safer usage, please fork and user an older version of this pallet. --- ## Replaces - [x] #6034 - [x] #5272 ## Related PRs: - [x] #7483 - [ ] #7357 - [ ] #7424 - [ ] paritytech/polkadot-staking-miner#955 This branch can be periodically merged into #7358 -> #6996 ## TODOs: - [x] rebase to master - Benchmarking for staking critical path - [x] snapshot - [x] election result - Benchmarking for EPMB critical path - [x] snapshot - [x] verification - [x] submission - [x] unsigned submission - [ ] election results fetching - [ ] Fix deletion weights. Either of - [ ] Garbage collector + lazy removal of all paged storage items - [ ] Confirm that deletion is small PoV footprint. - [ ] Move election prediction to be push based. @tdimitrov - [ ] integrity checks for bounds - [ ] Properly benchmark this as a part of CI -- for now I will remove them as they are too slow - [x] add try-state to all pallets - [x] Staking to allow genesis dev accounts to be created internally - [x] Decouple miner config so @niklasad1 can work on the miner 72841b7 - [x] duplicate snapshot page reported by @niklasad1 - [ ] #6520 or equivalent -- during snapshot, `VoterList` must be locked - [ ] Move target snapshot to a separate block --------- Co-authored-by: Gonçalo Pestana <[email protected]> Co-authored-by: Ankan <[email protected]> Co-authored-by: command-bot <> Co-authored-by: Guillaume Thiolliere <[email protected]> Co-authored-by: Giuseppe Re <[email protected]> Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent c1915af commit a025562

File tree

118 files changed

+19048
-1751
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+19048
-1751
lines changed

.github/workflows/runtimes-matrix.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"header": "substrate/HEADER-APACHE2",
77
"template": "substrate/.maintain/frame-weight-template.hbs",
88
"bench_features": "runtime-benchmarks",
9-
"bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic,pallet_nomination_pools,pallet_remark,pallet_transaction_storage",
9+
"bench_flags": "--genesis-builder-policy=none --exclude-pallets=pallet_xcm,pallet_xcm_benchmarks::fungible,pallet_xcm_benchmarks::generic,pallet_nomination_pools,pallet_remark,pallet_transaction_storage,pallet_election_provider_multi_block,pallet_election_provider_multi_block::signed,pallet_election_provider_multi_block::unsigned,pallet_election_provider_multi_block::verifier",
1010
"uri": null,
1111
"is_relay": false
1212
},

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- name: Checkout
3333
uses: actions/checkout@v4
3434
- name: script
35-
run: forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks --quiet -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet
35+
run: forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks --quiet -- benchmark pallet --chain dev --pallet "*" --exclude-pallets=pallet_election_provider_multi_block,pallet_election_provider_multi_block::signed,pallet_election_provider_multi_block::unsigned,pallet_election_provider_multi_block::verifier --extrinsic "*" --steps 2 --repeat 1 --quiet
3636

3737
# cf https://github.com/paritytech/polkadot-sdk/issues/1652
3838
test-syscalls:

Cargo.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ members = [
344344
"substrate/frame/core-fellowship",
345345
"substrate/frame/delegated-staking",
346346
"substrate/frame/democracy",
347+
"substrate/frame/election-provider-multi-block",
347348
"substrate/frame/election-provider-multi-phase",
348349
"substrate/frame/election-provider-multi-phase/test-staking-e2e",
349350
"substrate/frame/election-provider-support",

cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
1919
use sp_consensus_babe::AuthorityId as BabeId;
2020
use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId;
2121
use sp_core::storage::Storage;
22-
use sp_runtime::Perbill;
22+
use sp_runtime::{BoundedVec, Perbill};
2323

2424
// Polkadot
2525
use polkadot_primitives::{AssignmentId, ValidatorId};
@@ -87,7 +87,13 @@ pub fn genesis() -> Storage {
8787
.iter()
8888
.map(|x| (x.0.clone(), x.1.clone(), STASH, pallet_staking::StakerStatus::Validator))
8989
.collect(),
90-
invulnerables: validators::initial_authorities().iter().map(|x| x.0.clone()).collect(),
90+
invulnerables: BoundedVec::try_from(
91+
validators::initial_authorities()
92+
.iter()
93+
.map(|x| x.0.clone())
94+
.collect::<Vec<_>>(),
95+
)
96+
.expect("Limit for staking invulnerables must be less than initial authorities."),
9197
force_era: pallet_staking::Forcing::ForceNone,
9298
slash_reward_fraction: Perbill::from_percent(10),
9399
..Default::default()

polkadot/runtime/common/src/try_runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ where
3636

3737
let all_stakers = Ledger::<T>::iter().map(|(ctrl, l)| (ctrl, l.stash)).collect::<BTreeSet<_>>();
3838
let mut all_exposed = BTreeSet::new();
39-
ErasStakers::<T>::iter().for_each(|(_, val, expo)| {
39+
ErasStakersPaged::<T>::iter().for_each(|((_era, val, _page), expo)| {
4040
all_exposed.insert(val);
4141
all_exposed.extend(expo.others.iter().map(|ie| ie.who.clone()))
4242
});

polkadot/runtime/parachains/src/disputes/slashing/benchmarking.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,12 @@ where
8282

8383
pallet_session::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
8484
initializer::Pallet::<T>::on_initialize(BlockNumberFor::<T>::one());
85+
8586
// skip sessions until the new validator set is enacted
8687
while pallet_session::Pallet::<T>::validators().len() < n as usize {
88+
// initialize stakers in pallet_staking. This is suboptimal, but an easy way to avoid this
89+
// being an infinite loop.
90+
pallet_staking::Pallet::<T>::populate_staking_election_testing_benchmarking_only().unwrap();
8791
pallet_session::Pallet::<T>::rotate_session();
8892
}
8993
initializer::Pallet::<T>::on_finalize(BlockNumberFor::<T>::one());

polkadot/runtime/test-runtime/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ use polkadot_runtime_common::{
7979
use polkadot_runtime_parachains::reward_points::RewardValidatorsWithEraPoints;
8080
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
8181
use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature};
82-
use sp_core::{ConstU32, OpaqueMetadata};
82+
use sp_core::{ConstBool, ConstU32, OpaqueMetadata};
8383
use sp_mmr_primitives as mmr;
8484
use sp_runtime::{
8585
curve::PiecewiseLinear,
@@ -348,7 +348,7 @@ parameter_types! {
348348
pub const MaxExposurePageSize: u32 = 64;
349349
pub const MaxNominators: u32 = 256;
350350
pub const MaxAuthorities: u32 = 100_000;
351-
pub const OnChainMaxWinners: u32 = u32::MAX;
351+
pub const OnChainMaxWinners: u32 = MaxAuthorities::get();
352352
// Unbounded number of election targets and voters.
353353
pub ElectionBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build();
354354
}
@@ -361,7 +361,9 @@ impl onchain::Config for OnChainSeqPhragmen {
361361
type DataProvider = Staking;
362362
type WeightInfo = ();
363363
type Bounds = ElectionBoundsOnChain;
364-
type MaxWinners = OnChainMaxWinners;
364+
type MaxWinnersPerPage = OnChainMaxWinners;
365+
type MaxBackersPerWinner = ConstU32<{ u32::MAX }>;
366+
type Sort = ConstBool<true>;
365367
}
366368

367369
/// Upper limit on the number of NPOS nominations.
@@ -400,6 +402,9 @@ impl pallet_staking::Config for Runtime {
400402
type EventListeners = ();
401403
type WeightInfo = ();
402404
type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
405+
type MaxValidatorSet = MaxAuthorities;
406+
type MaxInvulnerables = ConstU32<20>;
407+
type MaxDisabledValidators = ConstU32<100>;
403408
}
404409

405410
parameter_types! {

polkadot/runtime/westend/src/genesis_config_presets.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use sp_consensus_grandpa::AuthorityId as GrandpaId;
3333
use sp_core::{crypto::get_public_from_string_or_panic, sr25519};
3434
use sp_genesis_builder::PresetId;
3535
use sp_keyring::Sr25519Keyring;
36-
use sp_runtime::Perbill;
36+
use sp_runtime::{BoundedVec, Perbill};
3737
use westend_runtime_constants::currency::UNITS as WND;
3838

3939
/// Helper function to generate stash, controller and session key from seed
@@ -202,7 +202,10 @@ fn westend_testnet_genesis(
202202
.iter()
203203
.map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::<AccountId>::Validator))
204204
.collect::<Vec<_>>(),
205-
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
205+
invulnerables: BoundedVec::try_from(
206+
initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>()
207+
)
208+
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
206209
force_era: Forcing::NotForcing,
207210
slash_reward_fraction: Perbill::from_percent(10),
208211
},
@@ -373,7 +376,10 @@ fn westend_staging_testnet_config_genesis() -> serde_json::Value {
373376
.iter()
374377
.map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::<AccountId>::Validator))
375378
.collect::<Vec<_>>(),
376-
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
379+
invulnerables: BoundedVec::try_from(
380+
initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>()
381+
)
382+
.expect("Too many invulnerable validators: upper limit is MaxInvulnerables from pallet staking config"),
377383
force_era: Forcing::ForceNone,
378384
slash_reward_fraction: Perbill::from_percent(10),
379385
},

polkadot/runtime/westend/src/lib.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ use sp_consensus_beefy::{
9696
ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature},
9797
mmr::{BeefyDataProvider, MmrLeafVersion},
9898
};
99-
use sp_core::{ConstU8, OpaqueMetadata, RuntimeDebug, H256};
99+
use sp_core::{ConstBool, ConstU8, OpaqueMetadata, RuntimeDebug, H256};
100100
use sp_runtime::{
101101
generic, impl_opaque_keys,
102102
traits::{
@@ -585,7 +585,10 @@ parameter_types! {
585585
ElectionBoundsBuilder::default().voters_count(MaxElectingVoters::get().into()).build();
586586
// Maximum winners that can be chosen as active validators
587587
pub const MaxActiveValidators: u32 = 1000;
588-
588+
// One page only, fill the whole page with the `MaxActiveValidators`.
589+
pub const MaxWinnersPerPage: u32 = MaxActiveValidators::get();
590+
// Unbonded, thus the max backers per winner maps to the max electing voters limit.
591+
pub const MaxBackersPerWinner: u32 = MaxElectingVoters::get();
589592
}
590593

591594
frame_election_provider_support::generate_solution_type!(
@@ -600,12 +603,14 @@ frame_election_provider_support::generate_solution_type!(
600603

601604
pub struct OnChainSeqPhragmen;
602605
impl onchain::Config for OnChainSeqPhragmen {
606+
type Sort = ConstBool<true>;
603607
type System = Runtime;
604608
type Solver = SequentialPhragmen<AccountId, OnChainAccuracy>;
605609
type DataProvider = Staking;
606610
type WeightInfo = weights::frame_election_provider_support::WeightInfo<Runtime>;
607-
type MaxWinners = MaxActiveValidators;
608611
type Bounds = ElectionBounds;
612+
type MaxBackersPerWinner = MaxBackersPerWinner;
613+
type MaxWinnersPerPage = MaxWinnersPerPage;
609614
}
610615

611616
impl pallet_election_provider_multi_phase::MinerConfig for Runtime {
@@ -618,7 +623,8 @@ impl pallet_election_provider_multi_phase::MinerConfig for Runtime {
618623
as
619624
frame_election_provider_support::ElectionDataProvider
620625
>::MaxVotesPerVoter;
621-
type MaxWinners = MaxActiveValidators;
626+
type MaxBackersPerWinner = MaxBackersPerWinner;
627+
type MaxWinners = MaxWinnersPerPage;
622628

623629
// The unsigned submissions have to respect the weight of the submit_unsigned call, thus their
624630
// weight estimate function is wired to this call's weight.
@@ -652,6 +658,8 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
652658
type BetterSignedThreshold = ();
653659
type OffchainRepeat = OffchainRepeat;
654660
type MinerTxPriority = NposSolutionPriority;
661+
type MaxWinners = MaxWinnersPerPage;
662+
type MaxBackersPerWinner = MaxBackersPerWinner;
655663
type DataProvider = Staking;
656664
#[cfg(any(feature = "fast-runtime", feature = "runtime-benchmarks"))]
657665
type Fallback = onchain::OnChainExecution<OnChainSeqPhragmen>;
@@ -660,7 +668,8 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
660668
AccountId,
661669
BlockNumber,
662670
Staking,
663-
MaxActiveValidators,
671+
MaxWinnersPerPage,
672+
MaxBackersPerWinner,
664673
)>;
665674
type GovernanceFallback = onchain::OnChainExecution<OnChainSeqPhragmen>;
666675
type Solver = SequentialPhragmen<
@@ -671,7 +680,6 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
671680
type BenchmarkingConfig = polkadot_runtime_common::elections::BenchmarkConfig;
672681
type ForceOrigin = EnsureRoot<AccountId>;
673682
type WeightInfo = weights::pallet_election_provider_multi_phase::WeightInfo<Self>;
674-
type MaxWinners = MaxActiveValidators;
675683
type ElectionBounds = ElectionBounds;
676684
}
677685

@@ -753,6 +761,7 @@ impl pallet_staking::Config for Runtime {
753761
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
754762
type VoterList = VoterList;
755763
type TargetList = UseValidatorsMap<Self>;
764+
type MaxValidatorSet = MaxActiveValidators;
756765
type NominationsQuota = pallet_staking::FixedNominationsQuota<{ MaxNominations::get() }>;
757766
type MaxUnlockingChunks = frame_support::traits::ConstU32<32>;
758767
type HistoryDepth = frame_support::traits::ConstU32<84>;
@@ -761,6 +770,8 @@ impl pallet_staking::Config for Runtime {
761770
type EventListeners = (NominationPools, DelegatedStaking);
762771
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
763772
type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
773+
type MaxInvulnerables = frame_support::traits::ConstU32<20>;
774+
type MaxDisabledValidators = ConstU32<100>;
764775
}
765776

766777
impl pallet_fast_unstake::Config for Runtime {

0 commit comments

Comments
 (0)