Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 6 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
6 changes: 6 additions & 0 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ frame_election_provider_support::generate_solution_type!(
parameter_types! {
pub MaxNominations: u32 = <NposSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub MaxElectingVoters: u32 = 10_000;
pub MaxBackersPerWinner: u32 = u32::max_value();
Comment thread
georgesdib marked this conversation as resolved.
Outdated
pub MaxWinnersPerPage: u32 = u32::max_value();
}

/// The numbers configured here could always be more than the the maximum limits of staking pallet
Expand Down Expand Up @@ -652,6 +654,8 @@ impl onchain::Config for OnChainSeqPhragmen {
>;
type DataProvider = <Runtime as pallet_election_provider_multi_phase::Config>::DataProvider;
type WeightInfo = frame_election_provider_support::weights::SubstrateWeight<Runtime>;
type MaxBackersPerWinner = MaxBackersPerWinner;
type MaxWinnersPerPage = MaxWinnersPerPage;
}

impl onchain::BoundedConfig for OnChainSeqPhragmen {
Expand Down Expand Up @@ -686,6 +690,8 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type ForceOrigin = EnsureRootOrHalfCouncil;
type MaxElectableTargets = ConstU16<{ u16::MAX }>;
type MaxElectingVoters = MaxElectingVoters;
type MaxBackersPerWinner = MaxBackersPerWinner;
type MaxWinnersPerPage = MaxWinnersPerPage;
type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
}
Expand Down
2 changes: 2 additions & 0 deletions frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ impl onchain::Config for OnChainSeqPhragmen {
type Solver = SequentialPhragmen<DummyValidatorId, Perbill>;
type DataProvider = Staking;
type WeightInfo = ();
type MaxBackersPerWinner = ConstU32<{ u32::MAX }>;
type MaxWinnersPerPage = ConstU32<{ u32::MAX }>;
}

impl pallet_staking::Config for Test {
Expand Down
2 changes: 1 addition & 1 deletion frame/election-provider-multi-phase/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ frame_benchmarking::benchmarks! {
let initial_balance = T::Currency::minimum_balance() * 10u32.into();
T::Currency::make_free_balance_be(&receiver, initial_balance);
let ready = ReadySolution {
supports: vec![],
supports: Default::default(),
score: Default::default(),
compute: Default::default()
};
Expand Down
84 changes: 63 additions & 21 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,17 @@

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

use codec::{Decode, Encode};
use codec::{Decode, Encode, MaxEncodedLen};
use frame_election_provider_support::{
ElectionDataProvider, ElectionProvider, InstantElectionProvider, NposSolution,
BoundedSupports, BoundedSupportsOf, ElectionDataProvider, ElectionProvider,
InstantElectionProvider, NposSolution, TryIntoBoundedSupports,
};
use frame_support::{
dispatch::DispatchResultWithPostInfo,
ensure,
traits::{Currency, Get, OnUnbalanced, ReservableCurrency},
weights::{DispatchClass, Weight},
DefaultNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use frame_system::{ensure_none, offchain::SendTransactionTypes};
use scale_info::TypeInfo;
Expand All @@ -246,7 +248,7 @@ use sp_arithmetic::{
UpperOf,
};
use sp_npos_elections::{
assignment_ratio_to_staked_normalized, ElectionScore, EvaluateSupport, Supports, VoteWeight,
assignment_ratio_to_staked_normalized, ElectionScore, EvaluateSupport, VoteWeight,
};
use sp_runtime::{
transaction_validity::{
Expand Down Expand Up @@ -315,21 +317,23 @@ impl<T: Config> ElectionProvider for NoFallback<T> {
type BlockNumber = T::BlockNumber;
type DataProvider = T::DataProvider;
type Error = &'static str;
type MaxBackersPerWinner = T::MaxBackersPerWinner;
type MaxWinnersPerPage = T::MaxWinnersPerPage;

fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
fn elect() -> Result<BoundedSupportsOf<Self>, Self::Error> {
// Do nothing, this will enable the emergency phase.
Err("NoFallback.")
}
}

impl<T: Config> InstantElectionProvider for NoFallback<T> {
fn elect_with_bounds(_: usize, _: usize) -> Result<Supports<T::AccountId>, Self::Error> {
fn elect_with_bounds(_: usize, _: usize) -> Result<BoundedSupportsOf<Self>, Self::Error> {
Err("NoFallback.")
}
}

/// Current phase of the pallet.
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum Phase<Bn> {
/// Nothing, the election is not happening.
Off,
Expand Down Expand Up @@ -391,7 +395,7 @@ impl<Bn: PartialEq + Eq> Phase<Bn> {
}

/// The type of `Computation` that provided this election data.
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]
pub enum ElectionCompute {
/// Election was computed on-chain.
OnChain,
Expand All @@ -417,7 +421,9 @@ impl Default for ElectionCompute {
///
/// Such a solution should never become effective in anyway before being checked by the
/// `Pallet::feasibility_check`.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, PartialOrd, Ord, TypeInfo)]
#[derive(
PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, PartialOrd, Ord, TypeInfo, MaxEncodedLen,
)]
pub struct RawSolution<S> {
/// the solution itself.
pub solution: S,
Expand All @@ -435,13 +441,24 @@ impl<C: Default> Default for RawSolution<C> {
}

/// A checked solution, ready to be enacted.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Default, TypeInfo)]
pub struct ReadySolution<A> {
#[derive(
PartialEqNoBound,
Eq,
Clone,
Encode,
Decode,
RuntimeDebugNoBound,
DefaultNoBound,
TypeInfo,
MaxEncodedLen,
)]
#[scale_info(skip_type_params(T))]
pub struct ReadySolution<T: Config> {
/// The final supports of the solution.
///
/// This is target-major vector, storing each winners, total backing, and each individual
/// backer.
pub supports: Supports<A>,
pub supports: BoundedSupports<T::AccountId, T::MaxWinnersPerPage, T::MaxBackersPerWinner>,
Comment thread
georgesdib marked this conversation as resolved.
Outdated
/// The score of the solution.
///
/// This is needed to potentially challenge the solution.
Expand Down Expand Up @@ -469,7 +486,7 @@ pub struct RoundSnapshot<T: Config> {
/// This is stored automatically on-chain, and it contains the **size of the entire snapshot**.
/// This is also used in dispatchables as weight witness data and should **only contain the size of
/// the presented solution**, not the entire snapshot.
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, Default, TypeInfo)]
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, Default, TypeInfo, MaxEncodedLen)]
pub struct SolutionOrSnapshotSize {
/// The length of voters.
#[codec(compact)]
Expand Down Expand Up @@ -649,6 +666,18 @@ pub mod pallet {
#[pallet::constant]
type MaxElectableTargets: Get<SolutionTargetIndexOf<Self>>;

/// Maximum number of backers that a single winner may have in the election.
Comment thread
georgesdib marked this conversation as resolved.
Outdated
///
/// By "backer per winner", this interface means "all backers in all pages" per winner.
/// Thus, hypothetically, a user of an election provider should be able to safely aggregate
/// all backers of each winner in a bounded vector with this bound.
#[pallet::constant]
type MaxBackersPerWinner: Get<u32>;

/// Maximum number of winner that can be returned, per page (i.e. per call to `elect`).
#[pallet::constant]
type MaxWinnersPerPage: Get<u32>;

/// Handler for the slashed deposits.
type SlashHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;

Expand Down Expand Up @@ -677,13 +706,16 @@ pub mod pallet {
+ sp_std::fmt::Debug
+ Ord
+ NposSolution
+ TypeInfo;
+ TypeInfo
+ MaxEncodedLen;

/// Configuration for the fallback.
type Fallback: InstantElectionProvider<
AccountId = Self::AccountId,
BlockNumber = Self::BlockNumber,
DataProvider = Self::DataProvider,
MaxBackersPerWinner = Self::MaxBackersPerWinner,
MaxWinnersPerPage = Self::MaxWinnersPerPage,
>;

/// Configuration of the governance-only fallback.
Expand All @@ -694,6 +726,8 @@ pub mod pallet {
AccountId = Self::AccountId,
BlockNumber = Self::BlockNumber,
DataProvider = Self::DataProvider,
MaxBackersPerWinner = Self::MaxBackersPerWinner,
MaxWinnersPerPage = Self::MaxWinnersPerPage,
>;

/// OCW election solution miner algorithm implementation.
Expand Down Expand Up @@ -932,7 +966,7 @@ pub mod pallet {
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
pub fn set_emergency_election_result(
origin: OriginFor<T>,
supports: Supports<T::AccountId>,
supports: BoundedSupports<T::AccountId, T::MaxWinnersPerPage, T::MaxBackersPerWinner>,
Comment thread
georgesdib marked this conversation as resolved.
Outdated
) -> DispatchResult {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed);
Expand Down Expand Up @@ -1180,7 +1214,7 @@ pub mod pallet {
/// Current best solution, signed or unsigned, queued to be returned upon `elect`.
#[pallet::storage]
#[pallet::getter(fn queued_solution)]
pub type QueuedSolution<T: Config> = StorageValue<_, ReadySolution<T::AccountId>>;
pub type QueuedSolution<T: Config> = StorageValue<_, ReadySolution<T>>;

/// Snapshot data of the round.
///
Expand Down Expand Up @@ -1414,7 +1448,7 @@ impl<T: Config> Pallet<T> {
pub fn feasibility_check(
raw_solution: RawSolution<SolutionOf<T>>,
compute: ElectionCompute,
) -> Result<ReadySolution<T::AccountId>, FeasibilityError> {
) -> Result<ReadySolution<T>, FeasibilityError> {
let RawSolution { solution, score, round } = raw_solution;

// First, check round.
Expand Down Expand Up @@ -1490,6 +1524,9 @@ impl<T: Config> Pallet<T> {
let known_score = supports.evaluate();
ensure!(known_score == score, FeasibilityError::InvalidScore);

let supports = supports
.try_into_bounded_supports()
.map_err(|_| FeasibilityError::WrongWinnerCount)?;
Ok(ReadySolution { supports, compute, score })
}

Expand All @@ -1509,7 +1546,10 @@ impl<T: Config> Pallet<T> {
Self::kill_snapshot();
}

fn do_elect() -> Result<Supports<T::AccountId>, ElectionError<T>> {
fn do_elect() -> Result<
BoundedSupports<T::AccountId, T::MaxWinnersPerPage, T::MaxBackersPerWinner>,
ElectionError<T>,
> {
// We have to unconditionally try finalizing the signed phase here. There are only two
// possibilities:
//
Expand Down Expand Up @@ -1544,7 +1584,7 @@ impl<T: Config> Pallet<T> {
}

/// record the weight of the given `supports`.
fn weigh_supports(supports: &Supports<T::AccountId>) {
fn weigh_supports(supports: &BoundedSupportsOf<Self>) {
let active_voters = supports
.iter()
.map(|(_, x)| x)
Expand All @@ -1559,8 +1599,10 @@ impl<T: Config> ElectionProvider for Pallet<T> {
type BlockNumber = T::BlockNumber;
type Error = ElectionError<T>;
type DataProvider = T::DataProvider;
type MaxBackersPerWinner = T::MaxBackersPerWinner;
type MaxWinnersPerPage = T::MaxWinnersPerPage;

fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
fn elect() -> Result<BoundedSupportsOf<Self>, Self::Error> {
match Self::do_elect() {
Ok(supports) => {
// All went okay, record the weight, put sign to be Off, clean snapshot, etc.
Expand Down Expand Up @@ -1794,7 +1836,7 @@ mod tests {
},
Phase,
};
use frame_election_provider_support::ElectionProvider;
use frame_election_provider_support::{bounded_supports_to_supports, ElectionProvider};
use frame_support::{assert_noop, assert_ok};
use sp_npos_elections::Support;

Expand Down Expand Up @@ -2016,7 +2058,7 @@ mod tests {
let supports = MultiPhase::elect().unwrap();

assert_eq!(
supports,
bounded_supports_to_supports(supports),
vec![
(30, Support { total: 40, voters: vec![(2, 5), (4, 5), (30, 30)] }),
(40, Support { total: 60, voters: vec![(2, 5), (3, 10), (4, 5), (40, 40)] })
Expand Down
10 changes: 8 additions & 2 deletions frame/election-provider-multi-phase/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ impl onchain::Config for OnChainSeqPhragmen {
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>, Balancing>;
type DataProvider = StakingMock;
type WeightInfo = ();
type MaxBackersPerWinner = MaxElectingVoters;
type MaxWinnersPerPage = MaxElectableTargets;
}

pub struct MockFallback;
Expand All @@ -289,8 +291,10 @@ impl ElectionProvider for MockFallback {
type BlockNumber = u64;
type Error = &'static str;
type DataProvider = StakingMock;
type MaxBackersPerWinner = MaxElectingVoters;
type MaxWinnersPerPage = MaxElectableTargets;

fn elect() -> Result<Supports<AccountId>, Self::Error> {
fn elect() -> Result<BoundedSupportsOf<Self>, Self::Error> {
Self::elect_with_bounds(Bounded::max_value(), Bounded::max_value())
}
}
Expand All @@ -299,7 +303,7 @@ impl InstantElectionProvider for MockFallback {
fn elect_with_bounds(
max_voters: usize,
max_targets: usize,
) -> Result<Supports<Self::AccountId>, Self::Error> {
) -> Result<BoundedSupportsOf<Self>, Self::Error> {
if OnChainFallback::get() {
onchain::UnboundedExecution::<OnChainSeqPhragmen>::elect_with_bounds(
max_voters,
Expand Down Expand Up @@ -425,6 +429,8 @@ impl crate::Config for Runtime {
type SignedDepositWeight = ();
type SignedMaxWeight = SignedMaxWeight;
type SignedMaxSubmissions = SignedMaxSubmissions;
type MaxBackersPerWinner = MaxElectingVoters;
type MaxWinnersPerPage = MaxElectableTargets;
type SlashHandler = ();
type RewardHandler = ();
type DataProvider = StakingMock;
Expand Down
8 changes: 5 additions & 3 deletions frame/election-provider-multi-phase/src/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
SignedSubmissionIndices, SignedSubmissionNextIndex, SignedSubmissionsMap, SolutionOf,
SolutionOrSnapshotSize, Weight, WeightInfo,
};
use codec::{Decode, Encode, HasCompact};
use codec::{Decode, Encode, HasCompact, MaxEncodedLen};
use frame_election_provider_support::NposSolution;
use frame_support::{
storage::bounded_btree_map::BoundedBTreeMap,
Expand All @@ -43,7 +43,9 @@ use sp_std::{
/// A raw, unchecked signed submission.
///
/// This is just a wrapper around [`RawSolution`] and some additional info.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)]
#[derive(
PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen,
)]
pub struct SignedSubmission<AccountId, Balance: HasCompact, Solution> {
/// Who submitted this solution.
pub who: AccountId,
Expand Down Expand Up @@ -421,7 +423,7 @@ impl<T: Config> Pallet<T> {
///
/// Infallible
pub fn finalize_signed_phase_accept_solution(
ready_solution: ReadySolution<T::AccountId>,
ready_solution: ReadySolution<T>,
who: &T::AccountId,
deposit: BalanceOf<T>,
reward: BalanceOf<T>,
Expand Down
Loading