Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 10 additions & 6 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@

use codec::{Decode, Encode};
use frame_election_provider_support::{
ElectionDataProvider, ElectionProvider, InstantElectionProvider, NposSolution,
ElectionDataProvider, ElectionProvider, ElectionProviderBase, InstantElectionProvider,
NposSolution,
};
use frame_support::{
dispatch::DispatchClass,
Expand Down Expand Up @@ -289,7 +290,7 @@ pub type SolutionTargetIndexOf<T> = <SolutionOf<T> as NposSolution>::TargetIndex
pub type SolutionAccuracyOf<T> =
<SolutionOf<<T as crate::Config>::MinerConfig> as NposSolution>::Accuracy;
/// The fallback election type.
pub type FallbackErrorOf<T> = <<T as crate::Config>::Fallback as ElectionProvider>::Error;
pub type FallbackErrorOf<T> = <<T as crate::Config>::Fallback as ElectionProviderBase>::Error;

/// Configuration for the benchmarks of the pallet.
pub trait BenchmarkingConfig {
Expand All @@ -312,7 +313,7 @@ pub trait BenchmarkingConfig {
/// A fallback implementation that transitions the pallet to the emergency phase.
pub struct NoFallback<T>(sp_std::marker::PhantomData<T>);

impl<T: Config> ElectionProvider for NoFallback<T> {
impl<T: Config> ElectionProviderBase for NoFallback<T> {
type AccountId = T::AccountId;
type BlockNumber = T::BlockNumber;
type DataProvider = T::DataProvider;
Expand All @@ -321,7 +322,9 @@ impl<T: Config> ElectionProvider for NoFallback<T> {
fn ongoing() -> bool {
false
}
}

impl<T: Config> ElectionProvider for NoFallback<T> {
fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
// Do nothing, this will enable the emergency phase.
Err("NoFallback.")
Expand Down Expand Up @@ -1563,7 +1566,7 @@ impl<T: Config> Pallet<T> {
<QueuedSolution<T>>::take()
.ok_or(ElectionError::<T>::NothingQueued)
.or_else(|_| {
T::Fallback::elect()
<T::Fallback as ElectionProvider>::elect()
.map(|supports| ReadySolution {
supports,
score: Default::default(),
Expand Down Expand Up @@ -1598,7 +1601,7 @@ impl<T: Config> Pallet<T> {
}
}

impl<T: Config> ElectionProvider for Pallet<T> {
impl<T: Config> ElectionProviderBase for Pallet<T> {
type AccountId = T::AccountId;
type BlockNumber = T::BlockNumber;
type Error = ElectionError<T>;
Expand All @@ -1610,7 +1613,9 @@ impl<T: Config> ElectionProvider for Pallet<T> {
_ => true,
}
}
}

impl<T: Config> ElectionProvider for Pallet<T> {
fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
match Self::do_elect() {
Ok(supports) => {
Expand All @@ -1627,7 +1632,6 @@ impl<T: Config> ElectionProvider for Pallet<T> {
}
}
}

/// convert a DispatchError to a custom InvalidTransaction with the inner code being the error
/// number.
pub fn dispatch_error_to_invalid(error: DispatchError) -> InvalidTransaction {
Expand Down
5 changes: 3 additions & 2 deletions frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl onchain::Config for OnChainSeqPhragmen {
}

pub struct MockFallback;
impl ElectionProvider for MockFallback {
impl ElectionProviderBase for MockFallback {
type AccountId = AccountId;
type BlockNumber = u64;
type Error = &'static str;
Expand All @@ -306,7 +306,8 @@ impl ElectionProvider for MockFallback {
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())
}
Expand Down
83 changes: 55 additions & 28 deletions frame/election-provider-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
//! This crate provides two traits that could interact to enable extensible election functionality
//! within FRAME pallets.
//!
//! Something that will provide the functionality of election will implement [`ElectionProvider`],
//! whilst needing an associated [`ElectionProvider::DataProvider`], which needs to be fulfilled by
//! an entity implementing [`ElectionDataProvider`]. Most often, *the data provider is* the receiver
//! of the election, resulting in a diagram as below:
//! Something that will provide the functionality of election will implement
//! [`ElectionProvider`] and its parent-trait [`ElectionProviderBase`], whilst needing an
//! associated [`ElectionProviderBase::DataProvider`], which needs to be
//! fulfilled by an entity implementing [`ElectionDataProvider`]. Most often, *the data provider is*
//! the receiver of the election, resulting in a diagram as below:
//!
//! ```ignore
//! ElectionDataProvider
Expand Down Expand Up @@ -131,12 +132,16 @@
//! type DataProvider: ElectionDataProvider<AccountId=AccountId, BlockNumber = BlockNumber>;
//! }
//!
//! impl<T: Config> ElectionProvider for GenericElectionProvider<T> {
//! impl<T: Config> ElectionProviderBase for GenericElectionProvider<T> {
//! type AccountId = AccountId;
//! type BlockNumber = BlockNumber;
//! type Error = &'static str;
//! type DataProvider = T::DataProvider;
//! fn ongoing() -> bool { false }
//!
//! }
//!
//! impl<T: Config> ElectionProvider for GenericElectionProvider<T> {
//! fn elect() -> Result<Supports<AccountId>, Self::Error> {
//! Self::DataProvider::electable_targets(None)
//! .map_err(|_| "failed to elect")
Expand Down Expand Up @@ -177,8 +182,8 @@ pub use frame_support::{traits::Get, weights::Weight, BoundedVec, RuntimeDebug};
/// Re-export some type as they are used in the interface.
pub use sp_arithmetic::PerThing;
pub use sp_npos_elections::{
Assignment, BalancingConfig, ElectionResult, Error, ExtendedBalance, IdentifierT, PerThing128,
Support, Supports, VoteWeight,
Assignment, BalancingConfig, BoundedSupports, ElectionResult, Error, ExtendedBalance,
IdentifierT, PerThing128, Support, Supports, VoteWeight,
};
pub use traits::NposSolution;

Expand Down Expand Up @@ -349,12 +354,12 @@ pub trait ElectionDataProvider {
fn clear() {}
}

/// Something that can compute the result of an election and pass it back to the caller.
/// Base trait for [`ElectionProvider`] and [`BoundedElectionProvider`]. It is
/// meant to be used only with an extension trait that adds an election
/// functionality.
///
/// This trait only provides an interface to _request_ an election, i.e.
/// [`ElectionProvider::elect`]. That data required for the election need to be passed to the
/// implemented of this trait through [`ElectionProvider::DataProvider`].
pub trait ElectionProvider {
/// Data can be bounded or unbounded and is fetched from [`Self::DataProvider`].
pub trait ElectionProviderBase {
/// The account identifier type.
type AccountId;

Expand All @@ -372,24 +377,39 @@ pub trait ElectionProvider {

/// Indicate if this election provider is currently ongoing an asynchronous election or not.
fn ongoing() -> bool;
}

/// 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*.
///
/// 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.
/// Elect a new set of winners, bounded by `MaxWinners`.
///
/// Returns a result in bounded, target major format, namely as
/// *BoundedVec<(AccountId, Vec<Support>), MaxWinners>*.
pub trait BoundedElectionProvider: ElectionProviderBase {
/// The upper bound on election winners.
type MaxWinners: Get<u32>;
/// Performs the election. 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.
fn elect() -> Result<BoundedSupports<Self::AccountId, Self::MaxWinners>, Self::Error>;
}

/// Same a [`BoundedElectionProvider`], but no bounds are imposed on the number
/// of winners.
///
/// The result is returned in a target major format, namely as
///*Vec<(AccountId, Vec<Support>)>*.
pub trait ElectionProvider: ElectionProviderBase {
/// Performs the election. This should be implemented as a self-weighing
/// function, similar to [`BoundedElectionProvider::elect()`].
fn elect() -> Result<Supports<Self::AccountId>, Self::Error>;
}

/// A sub-trait of the [`ElectionProvider`] for cases where we need to be sure an election needs to
/// happen instantly, not asynchronously.
/// A sub-trait of the [`ElectionProvider`] for cases where we need to be sure
/// an election needs to happen instantly, not asynchronously.
///
/// The same `DataProvider` is assumed to be used.
///
/// Consequently, allows for control over the amount of data that is being fetched from the
/// [`ElectionProvider::DataProvider`].
/// Consequently, allows for control over the amount of data that is being
/// fetched from the [`ElectionProviderBase::DataProvider`].
pub trait InstantElectionProvider: ElectionProvider {
/// 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
Expand All @@ -410,7 +430,7 @@ pub trait InstantElectionProvider: ElectionProvider {
pub struct NoElection<X>(sp_std::marker::PhantomData<X>);

#[cfg(feature = "std")]
impl<AccountId, BlockNumber, DataProvider> ElectionProvider
impl<AccountId, BlockNumber, DataProvider> ElectionProviderBase
for NoElection<(AccountId, BlockNumber, DataProvider)>
where
DataProvider: ElectionDataProvider<AccountId = AccountId, BlockNumber = BlockNumber>,
Expand All @@ -420,15 +440,22 @@ where
type Error = &'static str;
type DataProvider = DataProvider;

fn elect() -> Result<Supports<AccountId>, Self::Error> {
Err("<NoElection as ElectionProvider> cannot do anything.")
}

fn ongoing() -> bool {
false
}
}

#[cfg(feature = "std")]
impl<AccountId, BlockNumber, DataProvider> ElectionProvider
for NoElection<(AccountId, BlockNumber, DataProvider)>
where
DataProvider: ElectionDataProvider<AccountId = AccountId, BlockNumber = BlockNumber>,
{
fn elect() -> Result<Supports<AccountId>, Self::Error> {
Err("<NoElection as ElectionProvider> cannot do anything.")
}
}

/// A utility trait for something to implement `ElectionDataProvider` in a sensible way.
///
/// This is generic over `AccountId` and it can represent a validator, a nominator, or any other
Expand Down
27 changes: 16 additions & 11 deletions frame/election-provider-support/src/onchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
//! careful when using it onchain.

use crate::{
Debug, ElectionDataProvider, ElectionProvider, InstantElectionProvider, NposSolver, WeightInfo,
Debug, ElectionDataProvider, ElectionProvider, ElectionProviderBase, InstantElectionProvider,
NposSolver, WeightInfo,
};
use frame_support::{dispatch::DispatchClass, traits::Get};
use sp_npos_elections::*;
Expand Down Expand Up @@ -133,15 +134,6 @@ fn elect_with<T: Config>(
}

impl<T: Config> ElectionProvider for UnboundedExecution<T> {
type AccountId = <T::System as frame_system::Config>::AccountId;
type BlockNumber = <T::System as frame_system::Config>::BlockNumber;
type Error = Error;
type DataProvider = T::DataProvider;

fn ongoing() -> bool {
false
}

fn elect() -> Result<Supports<Self::AccountId>, Self::Error> {
// This should not be called if not in `std` mode (and therefore neither in genesis nor in
// testing)
Expand All @@ -156,6 +148,17 @@ impl<T: Config> ElectionProvider for UnboundedExecution<T> {
}
}

impl<T: Config> ElectionProviderBase for UnboundedExecution<T> {
type AccountId = <T::System as frame_system::Config>::AccountId;
type BlockNumber = <T::System as frame_system::Config>::BlockNumber;
type Error = Error;
type DataProvider = T::DataProvider;

fn ongoing() -> bool {
false
}
}

impl<T: Config> InstantElectionProvider for UnboundedExecution<T> {
fn elect_with_bounds(
max_voters: usize,
Expand All @@ -165,7 +168,7 @@ impl<T: Config> InstantElectionProvider for UnboundedExecution<T> {
}
}

impl<T: BoundedConfig> ElectionProvider for BoundedExecution<T> {
impl<T: BoundedConfig> ElectionProviderBase for BoundedExecution<T> {
type AccountId = <T::System as frame_system::Config>::AccountId;
type BlockNumber = <T::System as frame_system::Config>::BlockNumber;
type Error = Error;
Expand All @@ -174,7 +177,9 @@ impl<T: BoundedConfig> ElectionProvider for BoundedExecution<T> {
fn ongoing() -> bool {
false
}
}

impl<T: BoundedConfig> ElectionProvider for BoundedExecution<T> {
fn elect() -> Result<Supports<Self::AccountId>, Self::Error> {
elect_with::<T>(Some(T::VotersBound::get() as usize), Some(T::TargetsBound::get() as usize))
}
Expand Down
5 changes: 3 additions & 2 deletions frame/fast-unstake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ macro_rules! log {
pub mod pallet {
use super::*;
use crate::types::*;
use frame_election_provider_support::ElectionProvider;
use frame_election_provider_support::ElectionProviderBase;
use frame_support::{
pallet_prelude::*,
traits::{Defensive, ReservableCurrency},
Expand Down Expand Up @@ -330,7 +330,8 @@ pub mod pallet {
}
}

if <T as pallet_staking::Config>::ElectionProvider::ongoing() {
if <<T as pallet_staking::Config>::ElectionProvider as ElectionProviderBase>::ongoing()
{
// NOTE: we assume `ongoing` does not consume any weight.
// there is an ongoing election -- we better not do anything. Imagine someone is not
// exposed anywhere in the last era, and the snapshot for the election is already
Expand Down
4 changes: 3 additions & 1 deletion frame/fast-unstake/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ parameter_types! {
}

pub struct MockElection;
impl frame_election_provider_support::ElectionProvider for MockElection {
impl frame_election_provider_support::ElectionProviderBase for MockElection {
type AccountId = AccountId;
type BlockNumber = BlockNumber;
type DataProvider = Staking;
Expand All @@ -113,7 +113,9 @@ impl frame_election_provider_support::ElectionProvider for MockElection {
fn ongoing() -> bool {
Ongoing::get()
}
}

impl frame_election_provider_support::ElectionProvider for MockElection {
fn elect() -> Result<frame_election_provider_support::Supports<AccountId>, Self::Error> {
Err(())
}
Expand Down
14 changes: 9 additions & 5 deletions primitives/npos-elections/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,16 @@

#![cfg_attr(not(feature = "std"), no_std)]

use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_arithmetic::{traits::Zero, Normalizable, PerThing, Rational128, ThresholdOrd};
use sp_core::RuntimeDebug;
use sp_core::{bounded::BoundedVec, RuntimeDebug};
use sp_std::{
cell::RefCell, cmp::Ordering, collections::btree_map::BTreeMap, prelude::*, rc::Rc, vec,
};

use codec::{Decode, Encode, MaxEncodedLen};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};

#[cfg(test)]
mod mock;
#[cfg(test)]
Expand Down Expand Up @@ -451,6 +450,11 @@ impl<AccountId> Default for Support<AccountId> {
/// The main advantage of this is that it is encodable.
pub type Supports<A> = Vec<(A, Support<A>)>;

/// Same as `Supports` bounded by `MaxWinners`.
///
/// To note, the inner `Support` is still unbounded.
pub type BoundedSupports<A, MaxWinners> = BoundedVec<(A, Support<A>), MaxWinners>;

/// Linkage from a winner to their [`Support`].
///
/// This is more helpful than a normal [`Supports`] as it allows faster error checking.
Expand Down