Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add a bounded fallback on failed elections #10988

Merged
merged 45 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
922f655
Allow `pallet-election-provider` to accept smaller
Feb 23, 2022
5e63199
Fixing a typo
Feb 23, 2022
d0177c0
Adding some more tests
Feb 25, 2022
46de469
making it a URL
Feb 25, 2022
6ad51ab
Updating test name as per suggestion
georgesdib Mar 1, 2022
0506bd1
Updating documentation to be more explicit
georgesdib Mar 1, 2022
a604cea
Fixing formatting
Mar 1, 2022
a7bf404
`Fallback` now of type `InstantElectionProvider`
Mar 1, 2022
8f3e8b4
Allow `pallet-election-provider` to accept smaller
Feb 23, 2022
80ce9cb
Fixing a typo
Feb 23, 2022
1960035
Adding some more tests
Feb 25, 2022
3f87102
making it a URL
Feb 25, 2022
f683841
Updating test name as per suggestion
georgesdib Mar 1, 2022
3e3752f
Updating documentation to be more explicit
georgesdib Mar 1, 2022
521e4d2
Fixing formatting
Mar 1, 2022
d94ac40
`Fallback` now of type `InstantElectionProvider`
Mar 1, 2022
d389ee3
Merge branch 'election-fallback' of https://github.com/georgesdib/sub…
Mar 1, 2022
6e8c419
Merging types into one type with generics
Mar 6, 2022
de45d73
Merge upstream into current branch
Mar 6, 2022
47dc9ef
Removing `ConstUSize` and use `ConstU32`
Mar 8, 2022
bcc76e5
cleaning up the code
Mar 9, 2022
b7a7dbf
Merge branch 'paritytech:master' into election-fallback
georgesdib Mar 10, 2022
ee3848a
deprecating `OnChainSequentialPhragmen`
Mar 11, 2022
c760262
Merge branch 'election-fallback' of https://github.com/georgesdib/sub…
Mar 11, 2022
27477c0
Merge branch 'paritytech:master' into election-fallback
georgesdib Mar 11, 2022
d2616bc
Amending docs
Mar 12, 2022
3001d58
Merge branch 'election-fallback' of https://github.com/georgesdib/sub…
Mar 12, 2022
726bfc1
Merge branch 'paritytech:master' into election-fallback
georgesdib Mar 17, 2022
ff9fa7e
Adding some explicit imports
Mar 17, 2022
04eeb7f
Adding some explicit imports
Mar 17, 2022
38e204c
Implementing generic `BoundedOnchainExecution`
Mar 18, 2022
75a459f
Use the right Balancing strategy
Mar 19, 2022
afe8385
Merge branch 'paritytech:master' into election-fallback
georgesdib Mar 19, 2022
598ce52
Refactoring `onchain::Config`
Mar 19, 2022
086ea2d
Merge branch 'master' into election-fallback
Mar 19, 2022
9fdc250
Merge master
Mar 19, 2022
a09a7d7
fmt
Mar 19, 2022
e60e026
Name cleanups after review suggestions
Mar 20, 2022
cd42c4e
cosmetics
Mar 20, 2022
2d698d3
renaming `instant_elect` to `elect_with_bounds`
Mar 20, 2022
7c7c1ba
`BoundedOnchainExecution` -> `BoundedExecution`
Mar 20, 2022
762966f
feedback from kian
kianenigma Mar 21, 2022
5aae3e2
Merge pull request #47 from georgesdib/kiz-election-fallback
georgesdib Mar 21, 2022
ae86c59
fmt + unneeded import
Mar 21, 2022
f816a67
Merge branch 'master' into election-fallback
Mar 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#![recursion_limit = "256"]

use codec::{Decode, Encode, MaxEncodedLen};
use frame_election_provider_support::{onchain, ExtendedBalance, VoteWeight};
use frame_election_provider_support::{onchain, ExtendedBalance, SequentialPhragmen, VoteWeight};
use frame_support::{
construct_runtime,
pallet_prelude::Get,
Expand Down Expand Up @@ -557,7 +557,7 @@ impl pallet_staking::Config for Runtime {
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type GenesisElectionProvider = onchain::UnboundedExecution<OnChainSeqPhragmen>;
type VoterList = BagsList;
type MaxUnlockingChunks = ConstU32<32>;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
Expand Down Expand Up @@ -642,9 +642,19 @@ impl Get<Option<(usize, ExtendedBalance)>> for OffchainRandomBalancing {
}
}

impl onchain::Config for Runtime {
type Accuracy = Perbill;
type DataProvider = <Self as pallet_election_provider_multi_phase::Config>::DataProvider;
pub struct OnChainSeqPhragmen;
impl onchain::ExecutionConfig for OnChainSeqPhragmen {
type System = Runtime;
type Solver = SequentialPhragmen<
AccountId,
pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>,
>;
type DataProvider = <Runtime as pallet_election_provider_multi_phase::Config>::DataProvider;
}

impl onchain::BoundedExecutionConfig for OnChainSeqPhragmen {
type VotersBound = ConstU32<20_000>;
type TargetsBound = ConstU32<2_000>;
}

impl pallet_election_provider_multi_phase::Config for Runtime {
Expand All @@ -668,13 +678,9 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type RewardHandler = (); // nothing to do upon rewards
type DataProvider = Staking;
type Solution = NposSolution16;
type Fallback = pallet_election_provider_multi_phase::NoFallback<Self>;
type GovernanceFallback = onchain::OnChainSequentialPhragmen<Self>;
type Solver = frame_election_provider_support::SequentialPhragmen<
AccountId,
SolutionAccuracyOf<Self>,
OffchainRandomBalancing,
>;
type Fallback = onchain::BoundedExecution<OnChainSeqPhragmen>;
type GovernanceFallback = onchain::BoundedExecution<OnChainSeqPhragmen>;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Self>, OffchainRandomBalancing>;
type ForceOrigin = EnsureRootOrHalfCouncil;
type MaxElectableTargets = ConstU16<{ u16::MAX }>;
type MaxElectingVoters = MaxElectingVoters;
Expand Down Expand Up @@ -1891,6 +1897,7 @@ impl_runtime_apis! {
#[cfg(test)]
mod tests {
use super::*;
use frame_election_provider_support::NposSolution;
use frame_system::offchain::CreateSignedTransaction;
use sp_runtime::UpperOf;

Expand All @@ -1907,7 +1914,8 @@ mod tests {

#[test]
fn perbill_as_onchain_accuracy() {
type OnChainAccuracy = <Runtime as onchain::Config>::Accuracy;
type OnChainAccuracy =
<<Runtime as pallet_election_provider_multi_phase::Config>::Solution as NposSolution>::Accuracy;
let maximum_chain_accuracy: Vec<UpperOf<OnChainAccuracy>> = (0..MaxNominations::get())
.map(|_| <UpperOf<OnChainAccuracy>>::from(OnChainAccuracy::one().deconstruct()))
.collect();
Expand Down
10 changes: 6 additions & 4 deletions frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

use crate::{self as pallet_babe, Config, CurrentSlot};
use codec::Encode;
use frame_election_provider_support::onchain;
use frame_election_provider_support::{onchain, SequentialPhragmen};
use frame_support::{
parameter_types,
traits::{ConstU128, ConstU32, ConstU64, GenesisBuild, KeyOwnerProofSystem, OnInitialize},
Expand Down Expand Up @@ -172,8 +172,10 @@ parameter_types! {
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(16);
}

impl onchain::Config for Test {
type Accuracy = Perbill;
pub struct OnChainSeqPhragmen;
impl onchain::ExecutionConfig for OnChainSeqPhragmen {
type System = Test;
type Solver = SequentialPhragmen<DummyValidatorId, Perbill>;
type DataProvider = Staking;
}

Expand All @@ -195,7 +197,7 @@ impl pallet_staking::Config for Test {
type MaxNominatorRewardedPerValidator = ConstU32<64>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainSequentialPhragmen<Self>;
type ElectionProvider = onchain::UnboundedExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
type VoterList = pallet_staking::UseNominatorsAndValidatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
Expand Down
26 changes: 12 additions & 14 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ use frame_support::{
use frame_system::{ensure_none, offchain::SendTransactionTypes};
use scale_info::TypeInfo;
use sp_arithmetic::{
traits::{CheckedAdd, Saturating, Zero},
traits::{Bounded, CheckedAdd, Saturating, Zero},
UpperOf,
};
use sp_npos_elections::{
Expand Down Expand Up @@ -323,10 +323,7 @@ impl<T: Config> ElectionProvider for NoFallback<T> {
}

impl<T: Config> InstantElectionProvider for NoFallback<T> {
fn instant_elect(
_: Option<usize>,
_: Option<usize>,
) -> Result<Supports<T::AccountId>, Self::Error> {
fn elect_with_bounds(_: usize, _: usize) -> Result<Supports<T::AccountId>, Self::Error> {
Err("NoFallback.")
}
}
Expand Down Expand Up @@ -683,7 +680,7 @@ pub mod pallet {
+ TypeInfo;

/// Configuration for the fallback.
type Fallback: ElectionProvider<
type Fallback: InstantElectionProvider<
AccountId = Self::AccountId,
BlockNumber = Self::BlockNumber,
DataProvider = Self::DataProvider,
Expand All @@ -692,7 +689,7 @@ pub mod pallet {
/// Configuration of the governance-only fallback.
///
/// As a side-note, it is recommend for test-nets to use `type ElectionProvider =
/// OnChainSeqPhragmen<_>` if the test-net is not expected to have thousands of nominators.
/// BoundedExecution<_>` if the test-net is not expected to have thousands of nominators.
type GovernanceFallback: InstantElectionProvider<
AccountId = Self::AccountId,
BlockNumber = Self::BlockNumber,
Expand Down Expand Up @@ -1040,13 +1037,14 @@ pub mod pallet {
let maybe_max_voters = maybe_max_voters.map(|x| x as usize);
let maybe_max_targets = maybe_max_targets.map(|x| x as usize);

let supports =
T::GovernanceFallback::instant_elect(maybe_max_voters, maybe_max_targets).map_err(
|e| {
log!(error, "GovernanceFallback failed: {:?}", e);
Error::<T>::FallbackFailed
},
)?;
let supports = T::GovernanceFallback::elect_with_bounds(
maybe_max_voters.unwrap_or(Bounded::max_value()),
maybe_max_targets.unwrap_or(Bounded::max_value()),
)
.map_err(|e| {
log!(error, "GovernanceFallback failed: {:?}", e);
Error::<T>::FallbackFailed
})?;

let solution = ReadySolution {
supports,
Expand Down
30 changes: 22 additions & 8 deletions frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,13 @@ parameter_types! {
pub static MaxElectableTargets: TargetIndex = TargetIndex::max_value();

pub static EpochLength: u64 = 30;
pub static OnChianFallback: bool = true;
pub static OnChainFallback: bool = true;
}

impl onchain::Config for Runtime {
type Accuracy = sp_runtime::Perbill;
pub struct OnChainSeqPhragmen;
impl onchain::ExecutionConfig for OnChainSeqPhragmen {
type System = Runtime;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
type DataProvider = StakingMock;
}

Expand All @@ -288,11 +290,23 @@ impl ElectionProvider for MockFallback {
type DataProvider = StakingMock;

fn elect() -> Result<Supports<AccountId>, Self::Error> {
if OnChianFallback::get() {
onchain::OnChainSequentialPhragmen::<Runtime>::elect()
.map_err(|_| "OnChainSequentialPhragmen failed")
Self::elect_with_bounds(Bounded::max_value(), Bounded::max_value())
}
}

impl InstantElectionProvider for MockFallback {
fn elect_with_bounds(
max_voters: usize,
max_targets: usize,
) -> Result<Supports<Self::AccountId>, Self::Error> {
if OnChainFallback::get() {
onchain::UnboundedExecution::<OnChainSeqPhragmen>::elect_with_bounds(
max_voters,
max_targets,
)
.map_err(|_| "UnboundedExecution failed")
} else {
super::NoFallback::<Runtime>::elect()
super::NoFallback::<Runtime>::elect_with_bounds(max_voters, max_targets)
}
}
}
Expand Down Expand Up @@ -532,7 +546,7 @@ impl ExtBuilder {
self
}
pub fn onchain_fallback(self, onchain: bool) -> Self {
<OnChianFallback>::set(onchain);
<OnChainFallback>::set(onchain);
self
}
pub fn miner_weight(self, weight: Weight) -> Self {
Expand Down
20 changes: 14 additions & 6 deletions frame/election-provider-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@

pub mod onchain;
pub mod traits;
#[cfg(feature = "std")]
use codec::{Decode, Encode};
use frame_support::{traits::Get, BoundedVec, RuntimeDebug};
use sp_runtime::traits::Bounded;
Expand Down Expand Up @@ -368,9 +369,10 @@ pub trait ElectionProvider {
BlockNumber = Self::BlockNumber,
>;

/// Elect a new set of winners.
/// Elect a new set of winners, without specifying any bounds on the amount of data fetched from
/// [`Self::DataProvider`]. An implementation could nonetheless impose its own custom limits.
///
/// The result is returned in a target major format, namely as vector of supports.
/// The result is returned in a target major format, namely as *vector of supports*.
///
/// This should be implemented as a self-weighing function. The implementor should register its
/// appropriate weight at the end of execution with the system pallet directly.
Expand All @@ -385,11 +387,17 @@ pub trait ElectionProvider {
/// Consequently, allows for control over the amount of data that is being fetched from the
/// [`ElectionProvider::DataProvider`].
pub trait InstantElectionProvider: ElectionProvider {
/// Elect a new set of winners, instantly, with the given given limits set on the
/// Elect a new set of winners, but unlike [`ElectionProvider::elect`] which cannot enforce
/// bounds, this trait method can enforce bounds on the amount of data provided by the
/// `DataProvider`.
fn instant_elect(
maybe_max_voters: Option<usize>,
maybe_max_targets: Option<usize>,
///
/// An implementing type, if itself bounded, should choose the minimum of the two bounds to
/// choose the final value of `max_voters` and `max_targets`. In other words, an implementation
/// should guarantee that `max_voter` and `max_targets` provided to this method are absolutely
/// respected.
fn elect_with_bounds(
max_voters: usize,
max_targets: usize,
) -> Result<Supports<Self::AccountId>, Self::Error>;
}

Expand Down
Loading