Bound Election and Staking by MaxActiveValidators (#12436)

* bounding election provider with kian

* multi phase implement bounded election provider

* election provider blanket implementation

* staking compiles

* fix test for election provider support

* fmt

* fixing epmp tests, does not compile yet

* fix epmp tests

* fix staking tests

* fmt

* fix runtime tests

* fmt

* remove outdated wip tags

* add enum error

* sort and truncate supports

* comment

* error when unsupported number of election winners

* compiling wip after kian's suggestions

* fix TODOs

* remove,fix tags

* ensure validator count does not exceed maxwinners

* clean up

* some more clean up and todos

* handle too many winners

* rename parameter for mock

* todo

* add sort and truncate rule if there are too many winners

* fmt

* fail, not swallow emergency result bound not met

* remove too many winners resolution as it can be guaranteed to be bounded

* fix benchmark

* give MaxWinners more contextual name

* make ready solution generic over T

* kian feedback

* fix stuff

* Kian's way of solvign this

* comment fix

* fix compile

* remove use of BoundedExecution

* fmt

* comment out failing integrity test

* cap validator count increment to max winners

* dont panic

* add test for bad data provider

* Update frame/staking/src/pallet/impls.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* fix namespace conflict and add test for onchain max winners less than desired targets

* defensive unwrap

* early convert to bounded vec

* fix syntax

* fmt

* fix doc

* fix rustdoc

* fmt

* fix maxwinner count for benchmarking

* add instant election for noelection

* fmt

* fix compile

* pr feedbacks

* always error at validator count exceeding max winners

* add useful error message

* pr comments

* import fix

* add checked_desired_targets

* fmt

* fmt

* fix rust doc

Co-authored-by: parity-processbot <>
Co-authored-by: kianenigma <kian@parity.io>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Ankan
2022-11-09 10:11:51 +01:00
committed by GitHub
parent 535c6f2e94
commit 657d99202c
21 changed files with 544 additions and 318 deletions
@@ -19,7 +19,7 @@ use super::*;
use crate::{self as multi_phase, unsigned::MinerConfig};
use frame_election_provider_support::{
data_provider,
onchain::{self, UnboundedExecution},
onchain::{self},
ElectionDataProvider, NposSolution, SequentialPhragmen,
};
pub use frame_support::{assert_noop, assert_ok, pallet_prelude::GetDefault};
@@ -297,6 +297,7 @@ parameter_types! {
pub static MockWeightInfo: MockedWeightInfo = MockedWeightInfo::Real;
pub static MaxElectingVoters: VoterIndex = u32::max_value();
pub static MaxElectableTargets: TargetIndex = TargetIndex::max_value();
pub static MaxWinners: u32 = 200;
pub static EpochLength: u64 = 30;
pub static OnChainFallback: bool = true;
@@ -308,6 +309,9 @@ impl onchain::Config for OnChainSeqPhragmen {
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
type DataProvider = StakingMock;
type WeightInfo = ();
type MaxWinners = MaxWinners;
type VotersBound = ConstU32<{ u32::MAX }>;
type TargetsBound = ConstU32<{ u32::MAX }>;
}
pub struct MockFallback;
@@ -316,30 +320,19 @@ impl ElectionProviderBase for MockFallback {
type BlockNumber = u64;
type Error = &'static str;
type DataProvider = StakingMock;
fn ongoing() -> bool {
false
}
}
impl ElectionProvider for MockFallback {
fn elect() -> Result<Supports<AccountId>, Self::Error> {
Self::elect_with_bounds(Bounded::max_value(), Bounded::max_value())
}
type MaxWinners = MaxWinners;
}
impl InstantElectionProvider for MockFallback {
fn elect_with_bounds(
max_voters: usize,
max_targets: usize,
) -> Result<Supports<Self::AccountId>, Self::Error> {
fn instant_elect(
max_voters: Option<u32>,
max_targets: Option<u32>,
) -> Result<BoundedSupportsOf<Self>, Self::Error> {
if OnChainFallback::get() {
onchain::UnboundedExecution::<OnChainSeqPhragmen>::elect_with_bounds(
max_voters,
max_targets,
)
.map_err(|_| "onchain::UnboundedExecution failed.")
onchain::OnChainExecution::<OnChainSeqPhragmen>::instant_elect(max_voters, max_targets)
.map_err(|_| "onchain::OnChainExecution failed.")
} else {
super::NoFallback::<Runtime>::elect_with_bounds(max_voters, max_targets)
Err("NoFallback.")
}
}
}
@@ -404,10 +397,12 @@ impl crate::Config for Runtime {
type WeightInfo = ();
type BenchmarkingConfig = TestBenchmarkingConfig;
type Fallback = MockFallback;
type GovernanceFallback = UnboundedExecution<OnChainSeqPhragmen>;
type GovernanceFallback =
frame_election_provider_support::onchain::OnChainExecution<OnChainSeqPhragmen>;
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
type MaxElectingVoters = MaxElectingVoters;
type MaxElectableTargets = MaxElectableTargets;
type MaxWinners = MaxWinners;
type MinerConfig = Self;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
}
@@ -424,6 +419,8 @@ pub type Extrinsic = sp_runtime::testing::TestXt<RuntimeCall, ()>;
parameter_types! {
pub MaxNominations: u32 = <TestNposSolution as NposSolution>::LIMIT as u32;
// only used in testing to manipulate mock behaviour
pub static DataProviderAllowBadData: bool = false;
}
#[derive(Default)]
@@ -438,7 +435,9 @@ impl ElectionDataProvider for StakingMock {
fn electable_targets(maybe_max_len: Option<usize>) -> data_provider::Result<Vec<AccountId>> {
let targets = Targets::get();
if maybe_max_len.map_or(false, |max_len| targets.len() > max_len) {
if !DataProviderAllowBadData::get() &&
maybe_max_len.map_or(false, |max_len| targets.len() > max_len)
{
return Err("Targets too big")
}
@@ -449,8 +448,10 @@ impl ElectionDataProvider for StakingMock {
maybe_max_len: Option<usize>,
) -> data_provider::Result<Vec<VoterOf<Runtime>>> {
let mut voters = Voters::get();
if let Some(max_len) = maybe_max_len {
voters.truncate(max_len)
if !DataProviderAllowBadData::get() {
if let Some(max_len) = maybe_max_len {
voters.truncate(max_len)
}
}
Ok(voters)