// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Primitive traits for providing election functionality. //! //! 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: //! //! ```ignore //! ElectionDataProvider //! <------------------------------------------+ //! | | //! v | //! +-----+----+ +------+---+ //! | | | | //! pezpallet-do-election | | | | pezpallet-needs-election //! | | | | //! | | | | //! +-----+----+ +------+---+ //! | ^ //! | | //! +------------------------------------------+ //! ElectionProvider //! ``` //! //! > It could also be possible that a third party pezpallet (C), provides the data of election to //! > an //! > election provider (B), which then passes the election result to another pezpallet (A). //! //! ## Election Types //! //! Typically, two types of elections exist: //! //! 1. **Stateless**: Election data is provided, and the election result is immediately ready. //! 2. **Stateful**: Election data is is queried ahead of time, and the election result might be //! ready some number of blocks in the future. //! //! To accommodate both type of elections in one trait, the traits lean toward **stateful //! election**, as it is more general than the stateless. This is why [`ElectionProvider::elect`] //! does not receive election data as an input. All value and type parameter must be provided by the //! [`ElectionDataProvider`] trait, even if the election happens immediately. //! //! ## Multi-page election support //! //! Both [`ElectionDataProvider`] and [`ElectionProvider`] traits are parameterized by page, //! supporting an election to be performed over multiple pages. This enables the //! [`ElectionDataProvider`] implementor to provide all the election data over multiple pages. //! Similarly [`ElectionProvider::elect`] is parameterized by page index. //! //! ## Election Data //! //! The data associated with an election, essentially what the [`ElectionDataProvider`] must convey //! is as follows: //! //! 1. A list of voters, with their stake. //! 2. A list of targets (i.e. _candidates_). //! 3. A number of desired targets to be elected (i.e. _winners_) //! //! In addition to that, the [`ElectionDataProvider`] must also hint [`ElectionProvider`] at when //! the next election might happen ([`ElectionDataProvider::next_election_prediction`]). A stateless //! election provider would probably ignore this. A stateful election provider can use this to //! prepare the election result in advance. //! //! Nonetheless, an [`ElectionProvider`] shan't rely on this and should preferably provide some //! means of fallback election as well, in case the `elect` was called immaturely early. //! //! ## Example //! //! ```rust //! # use pezframe_election_provider_support::{*, data_provider}; //! # use pezsp_npos_elections::{Support, Assignment}; //! # use pezframe_support::traits::ConstU32; //! # use pezsp_runtime::bounded_vec; //! //! type AccountId = u64; //! type Balance = u64; //! type BlockNumber = u32; //! //! mod data_provider_mod { //! use super::*; //! //! pub trait Config: Sized { //! type ElectionProvider: ElectionProvider< //! AccountId = AccountId, //! BlockNumber = BlockNumber, //! DataProvider = Pezpallet, //! >; //! } //! //! pub struct Pezpallet(std::marker::PhantomData); //! //! impl ElectionDataProvider for Pezpallet { //! type AccountId = AccountId; //! type BlockNumber = BlockNumber; //! type MaxVotesPerVoter = ConstU32<100>; //! //! fn desired_targets() -> data_provider::Result { //! Ok(1) //! } //! fn electing_voters(bounds: DataProviderBounds, _page: PageIndex) //! -> data_provider::Result>> //! { //! Ok(Default::default()) //! } //! fn electable_targets(bounds: DataProviderBounds, _page: PageIndex) -> data_provider::Result> { //! Ok(vec![10, 20, 30]) //! } //! fn next_election_prediction(now: BlockNumber) -> BlockNumber { //! 0 //! } //! } //! } //! //! //! mod generic_election_provider { //! use super::*; //! use pezsp_runtime::traits::Zero; //! //! pub struct GenericElectionProvider(std::marker::PhantomData); //! //! pub trait Config { //! type DataProvider: ElectionDataProvider; //! type MaxWinnersPerPage: Get; //! type MaxBackersPerWinner: Get; //! type Pages: Get; //! } //! //! impl ElectionProvider for GenericElectionProvider { //! type AccountId = AccountId; //! type BlockNumber = BlockNumber; //! type Error = &'static str; //! type MaxBackersPerWinner = T::MaxBackersPerWinner; //! type MaxBackersPerWinnerFinal = T::MaxBackersPerWinner; //! type MaxWinnersPerPage = T::MaxWinnersPerPage; //! type Pages = T::Pages; //! type DataProvider = T::DataProvider; //! //! fn duration() -> ::BlockNumber { todo!() } //! //! fn start() -> Result<(), ::Error> { todo!() } //! //! fn elect(page: PageIndex) -> Result, Self::Error> { //! unimplemented!() //! } //! //! fn status() -> Result { //! unimplemented!() //! } //! } //! } //! //! mod runtime { //! use pezframe_support::parameter_types; //! use super::generic_election_provider; //! use super::data_provider_mod; //! use super::AccountId; //! //! parameter_types! { //! pub static MaxWinnersPerPage: u32 = 10; //! pub static MaxBackersPerWinner: u32 = 20; //! pub static Pages: u32 = 2; //! } //! //! struct Runtime; //! impl generic_election_provider::Config for Runtime { //! type DataProvider = data_provider_mod::Pezpallet; //! type MaxWinnersPerPage = MaxWinnersPerPage; //! type MaxBackersPerWinner = MaxBackersPerWinner; //! type Pages = Pages; //! } //! //! impl data_provider_mod::Config for Runtime { //! type ElectionProvider = generic_election_provider::GenericElectionProvider; //! } //! //! } //! //! # fn main() {} //! ``` #![cfg_attr(not(feature = "std"), no_std)] pub mod bounds; pub mod onchain; pub mod traits; extern crate alloc; use alloc::{boxed::Box, vec::Vec}; use core::fmt::Debug; use pezframe_support::traits::{Defensive, DefensiveResult}; use pezsp_core::ConstU32; use pezsp_runtime::{ traits::{Bounded, Saturating, Zero}, RuntimeDebug, }; pub use bounds::DataProviderBounds; pub use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; /// Re-export the solution generation macro. pub use pezframe_election_provider_solution_type::generate_solution_type; pub use pezframe_support::{traits::Get, weights::Weight, BoundedVec, DefaultNoBound}; /// Re-export some type as they are used in the interface. pub use pezsp_arithmetic::PerThing; pub use pezsp_npos_elections::{ Assignment, BalancingConfig, ElectionResult, Error, ExtendedBalance, IdentifierT, PerThing128, Support, Supports, VoteWeight, }; use scale_info::TypeInfo; pub use traits::NposSolution; #[cfg(feature = "try-runtime")] use pezsp_runtime::TryRuntimeError; // re-export for the solution macro, with the dependencies of the macro. #[doc(hidden)] pub mod private { pub use alloc::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, vec::Vec, }; pub use codec; pub use pezsp_arithmetic; pub use scale_info; // Simple Extension trait to easily convert `None` from index closures to `Err`. // // This is only generated and re-exported for the solution code to use. pub trait __OrInvalidIndex { fn or_invalid_index(self) -> Result; } impl __OrInvalidIndex for Option { fn or_invalid_index(self) -> Result { self.ok_or(crate::Error::SolutionInvalidIndex) } } } use private::__OrInvalidIndex; pub mod weights; pub use weights::WeightInfo; #[cfg(test)] mod mock; #[cfg(test)] mod tests; /// A page index for the multi-block elections pagination. pub type PageIndex = u32; /// The [`IndexAssignment`] type is an intermediate between the assignments list /// ([`&[Assignment]`][Assignment]) and `SolutionOf`. /// /// The voter and target identifiers have already been replaced with appropriate indices, /// making it fast to repeatedly encode into a `SolutionOf`. This property turns out /// to be important when trimming for solution length. #[derive(RuntimeDebug, Clone, Default)] #[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))] pub struct IndexAssignment { /// Index of the voter among the voters list. pub who: VoterIndex, /// The distribution of the voter's stake among winning targets. /// /// Targets are identified by their index in the canonical list. pub distribution: Vec<(TargetIndex, P)>, } impl IndexAssignment { pub fn new( assignment: &Assignment, voter_index: impl Fn(&AccountId) -> Option, target_index: impl Fn(&AccountId) -> Option, ) -> Result { Ok(Self { who: voter_index(&assignment.who).or_invalid_index()?, distribution: assignment .distribution .iter() .map(|(target, proportion)| Some((target_index(target)?, *proportion))) .collect::>>() .or_invalid_index()?, }) } } /// A type alias for [`IndexAssignment`] made from [`NposSolution`]. pub type IndexAssignmentOf = IndexAssignment< ::VoterIndex, ::TargetIndex, ::Accuracy, >; /// Types that are used by the data provider trait. pub mod data_provider { /// Alias for the result type of the election data provider. pub type Result = core::result::Result; } /// Something that can provide the data to an [`ElectionProvider`]. pub trait ElectionDataProvider { /// The account identifier type. type AccountId: Encode; /// The block number type. type BlockNumber; /// Maximum number of votes per voter that this data provider is providing. type MaxVotesPerVoter: Get; /// Returns the possible targets for the election associated with the provided `page`, i.e. the /// targets that could become elected, thus "electable". /// /// This should be implemented as a self-weighing function. The implementor should register its /// appropriate weight at the end of execution with the system pezpallet directly. fn electable_targets( bounds: DataProviderBounds, page: PageIndex, ) -> data_provider::Result>; /// A state-less version of [`Self::electable_targets`]. /// /// An election-provider that only uses 1 page should use this. fn electable_targets_stateless( bounds: DataProviderBounds, ) -> data_provider::Result> { Self::electable_targets(bounds, 0) } /// All the voters that participate in the election associated with page `page`, thus /// "electing". /// /// Note that if a notion of self-vote exists, it should be represented here. /// /// This should be implemented as a self-weighing function. The implementor should register its /// appropriate weight at the end of execution with the system pezpallet directly. fn electing_voters( bounds: DataProviderBounds, page: PageIndex, ) -> data_provider::Result>>; /// A state-less version of [`Self::electing_voters`]. /// /// An election-provider that only uses 1 page should use this. fn electing_voters_stateless( bounds: DataProviderBounds, ) -> data_provider::Result>> { Self::electing_voters(bounds, 0) } /// The number of targets to elect. /// /// This should be implemented as a self-weighing function. The implementor should register its /// appropriate weight at the end of execution with the system pezpallet directly. /// /// A sensible implementation should use the minimum between this value and /// [`Self::targets().len()`], since desiring a winner set larger than candidates is not /// feasible. /// /// This is documented further in issue: fn desired_targets() -> data_provider::Result; /// Provide a best effort prediction about when the next election is about to happen. /// /// In essence, the implementor should predict with this function when it will trigger the /// [`ElectionProvider::elect`]. /// /// This is only useful for stateful election providers. fn next_election_prediction(now: Self::BlockNumber) -> Self::BlockNumber; /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, /// else a noop. #[cfg(any(feature = "runtime-benchmarks", test))] fn put_snapshot( _voters: Vec>, _targets: Vec, _target_stake: Option, ) { } /// Instruct the data provider to fetch a page of the solution. /// /// This can be useful to measure the export process in benchmarking. #[cfg(any(feature = "runtime-benchmarks", test))] fn fetch_page(_page: PageIndex) {} /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, /// else a noop. /// /// Same as `put_snapshot`, but can add a single voter one by one. #[cfg(any(feature = "runtime-benchmarks", test))] fn add_voter( _voter: Self::AccountId, _weight: VoteWeight, _targets: BoundedVec, ) { } /// Utility function only to be used in benchmarking scenarios, to be implemented optionally, /// else a noop. /// /// Same as `put_snapshot`, but can add a single voter one by one. #[cfg(any(feature = "runtime-benchmarks", test))] fn add_target(_target: Self::AccountId) {} /// Clear all voters and targets. #[cfg(any(feature = "runtime-benchmarks", test))] fn clear() {} /// Force set the desired targets in the snapshot. #[cfg(any(feature = "runtime-benchmarks", test))] fn set_desired_targets(_count: u32) {} } /// Something that can compute the result of an election and pass it back to the caller in a paged /// way. pub trait ElectionProvider { /// The account ID identifier; type AccountId; /// The block number type. type BlockNumber; /// The error type returned by the provider; type Error: Debug + PartialEq; /// The maximum number of winners per page in results returned by this election provider. /// /// A winner is an `AccountId` that is part of the final election result. type MaxWinnersPerPage: Get; /// The maximum number of backers that a single page may have in results returned by this /// election provider. /// /// A backer is an `AccountId` that "backs" one or more winners. For example, in the context of /// nominated proof of stake, a backer is a voter that nominates a winner validator in the /// election result. type MaxBackersPerWinner: Get; /// Same as [`Self::MaxBackersPerWinner`], but across all pages. /// /// If [`Self::Pages`] is set to 0, a reasonable value is [`Self::MaxBackersPerWinner`]. For /// multi-page elections, a reasonable value is the range of [`Self::MaxBackersPerWinner`] to /// [`Self::Pages`] * [`Self::MaxBackersPerWinner`]. type MaxBackersPerWinnerFinal: Get; /// The number of pages that this election provider supports. type Pages: Get; /// The data provider of the election. type DataProvider: ElectionDataProvider< AccountId = Self::AccountId, BlockNumber = Self::BlockNumber, >; /// Elect a new set of winners. /// /// A complete election may require multiple calls to [`ElectionProvider::elect`] if /// [`ElectionProvider::Pages`] is higher than one. /// /// The result is returned in a target major format, namely as vector of supports. fn elect(page: PageIndex) -> Result, Self::Error>; /// The index of the *most* significant page that this election provider supports. fn msp() -> PageIndex { Self::Pages::get().saturating_sub(1) } /// The index of the *least* significant page that this election provider supports. fn lsp() -> PageIndex { Zero::zero() } /// checked call to `Self::DataProvider::desired_targets()` ensuring the value never exceeds /// [`Self::MaxWinnersPerPage`]. fn desired_targets_checked() -> data_provider::Result { Self::DataProvider::desired_targets().and_then(|desired_targets| { if desired_targets <= Self::MaxWinnersPerPage::get() { Ok(desired_targets) } else { Err("desired_targets must not be greater than MaxWinners.") } }) } /// Return the duration of your election. /// /// This excludes the duration of the export. For that, use [`Self::duration_with_export`]. fn duration() -> Self::BlockNumber; /// Return the duration of your election, including the export. fn duration_with_export() -> Self::BlockNumber where Self::BlockNumber: From + core::ops::Add, { let export: Self::BlockNumber = Self::Pages::get().into(); Self::duration() + export } /// Signal that the election should start fn start() -> Result<(), Self::Error>; /// Indicate whether this election provider is currently ongoing an asynchronous election. /// /// `Err(())` should signal that we are not doing anything, and `elect` should def. not be /// called. `Ok(false)` means we are doing something, but work is still ongoing. `elect` should /// not be called. `Ok(true)` means we are done and ready for a call to `elect`. fn status() -> Result; /// Signal the election provider that we are about to call `elect` asap, and it should prepare /// itself. #[cfg(any(feature = "runtime-benchmarks", feature = "std"))] fn asap() {} } /// A (almost) marker trait that signifies an election provider as working synchronously. i.e. being /// *instant*. /// /// This must still use the same data provider as with [`ElectionProvider::DataProvider`]. /// However, it can optionally overwrite the amount of voters and targets that are fetched from the /// data provider at runtime via `forced_input_voters_bound` and `forced_input_target_bound`. pub trait InstantElectionProvider: ElectionProvider { fn instant_elect( voters: Vec>, targets: Vec, desired_targets: u32, ) -> Result, Self::Error>; // Sine many instant election provider, like [`NoElection`] are meant to do nothing, this is a // hint for the caller to call before, and if `false` is returned, not bother with passing all // the info to `instant_elect`. fn bother() -> bool; } /// An election provider that does nothing whatsoever. pub struct NoElection(core::marker::PhantomData); impl ElectionProvider for NoElection<(AccountId, BlockNumber, DataProvider, MaxWinnersPerPage, MaxBackersPerWinner)> where DataProvider: ElectionDataProvider, MaxWinnersPerPage: Get, MaxBackersPerWinner: Get, BlockNumber: Zero, { type AccountId = AccountId; type BlockNumber = BlockNumber; type Error = &'static str; type Pages = ConstU32<1>; type DataProvider = DataProvider; type MaxWinnersPerPage = MaxWinnersPerPage; type MaxBackersPerWinner = MaxBackersPerWinner; type MaxBackersPerWinnerFinal = MaxBackersPerWinner; fn elect(_page: PageIndex) -> Result, Self::Error> { Err("`NoElection` cannot do anything.") } fn start() -> Result<(), Self::Error> { Err("`NoElection` cannot do anything.") } fn duration() -> Self::BlockNumber { Zero::zero() } fn status() -> Result { Err(()) } } impl InstantElectionProvider for NoElection<(AccountId, BlockNumber, DataProvider, MaxWinnersPerPage, MaxBackersPerWinner)> where DataProvider: ElectionDataProvider, MaxWinnersPerPage: Get, MaxBackersPerWinner: Get, BlockNumber: Zero, { fn instant_elect( _: Vec>, _: Vec, _: u32, ) -> Result, Self::Error> { Err("`NoElection` cannot do anything.") } fn bother() -> bool { false } } /// 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 /// entity. /// /// The scores (see [`Self::Score`]) are ascending, the higher, the better. /// /// Something that implements this trait will do a best-effort sort over ids, and thus can be /// used on the implementing side of [`ElectionDataProvider`]. pub trait SortedListProvider { /// The list's error type. type Error: core::fmt::Debug; /// The type used by the list to compare nodes for ordering. type Score: Bounded + Saturating + Zero + Default; /// A typical range for this list. /// /// By default, this would be implemented as `Bounded` impl of `Self::Score`. /// /// If this is implemented by a bags-list instance, it will be the smallest and largest bags. /// /// This is useful to help another pezpallet that consumes this trait generate an even /// distribution of nodes for testing/genesis. fn range() -> (Self::Score, Self::Score) { (Self::Score::min_value(), Self::Score::max_value()) } /// An iterator over the list, which can have `take` called on it. fn iter() -> Box>; /// Lock the list. /// /// This will prevent subsequent calls to /// - [`Self::on_insert`] /// - [`Self::on_update`] /// - [`Self::on_decrease`] /// - [`Self::on_increase`] /// - [`Self::on_remove`] fn lock(); /// Unlock the list. This will nullify the effects of [`Self::lock`]. fn unlock(); /// Returns an iterator over the list, starting right after from the given voter. /// /// May return an error if `start` is invalid. fn iter_from(start: &AccountId) -> Result>, Self::Error>; /// The current count of ids in the list. fn count() -> u32; /// Return true if the list already contains `id`. fn contains(id: &AccountId) -> bool; /// Hook for inserting a new id. /// /// Implementation should return an error if duplicate item is being inserted. fn on_insert(id: AccountId, score: Self::Score) -> Result<(), Self::Error>; /// Hook for updating a single id. /// /// The `new` score is given. /// /// Returns `Ok(())` iff it successfully updates an item, an `Err(_)` otherwise. fn on_update(id: &AccountId, score: Self::Score) -> Result<(), Self::Error>; /// Get the score of `id`. fn get_score(id: &AccountId) -> Result; /// Same as `on_update`, but incorporate some increased score. fn on_increase(id: &AccountId, additional: Self::Score) -> Result<(), Self::Error> { let old_score = Self::get_score(id)?; let new_score = old_score.saturating_add(additional); Self::on_update(id, new_score) } /// Same as `on_update`, but incorporate some decreased score. /// /// If the new score of the item is `Zero`, it is removed. fn on_decrease(id: &AccountId, decreased: Self::Score) -> Result<(), Self::Error> { let old_score = Self::get_score(id)?; let new_score = old_score.saturating_sub(decreased); if new_score.is_zero() { Self::on_remove(id) } else { Self::on_update(id, new_score) } } /// Hook for removing am id from the list. /// /// Returns `Ok(())` iff it successfully removes an item, an `Err(_)` otherwise. fn on_remove(id: &AccountId) -> Result<(), Self::Error>; /// Regenerate this list from scratch. Returns the count of items inserted. /// /// This should typically only be used at a runtime upgrade. /// /// ## WARNING /// /// This function should be called with care, regenerate will remove the current list write the /// new list, which can lead to too many storage accesses, exhausting the block weight. fn unsafe_regenerate( all: impl IntoIterator, score_of: Box Option>, ) -> u32; /// Remove all items from the list. /// /// ## WARNING /// /// This function should never be called in production settings because it can lead to an /// unbounded amount of storage accesses. fn unsafe_clear(); /// Check internal state of the list. Only meant for debugging. #[cfg(feature = "try-runtime")] fn try_state() -> Result<(), TryRuntimeError>; /// If `who` changes by the returned amount they are guaranteed to have a worst case change /// in their list position. #[cfg(feature = "runtime-benchmarks")] fn score_update_worst_case(_who: &AccountId, _is_increase: bool) -> Self::Score; } /// Something that can provide the `Score` of an account. Similar to [`ElectionProvider`] and /// [`ElectionDataProvider`], this should typically be implementing by whoever is supposed to *use* /// `SortedListProvider`. pub trait ScoreProvider { type Score; /// Get the current `Score` of `who`, `None` if `who` is not present. /// /// `None` can be interpreted as a signal that the voter should be removed from the list. fn score(who: &AccountId) -> Option; /// For tests, benchmarks and fuzzing, set the `score`. #[cfg(any(feature = "runtime-benchmarks", feature = "fuzz", feature = "std"))] fn set_score_of(_: &AccountId, _: Self::Score) {} } /// Something that can compute the result to an NPoS solution. pub trait NposSolver { /// The account identifier type of this solver. type AccountId: pezsp_npos_elections::IdentifierT; /// The accuracy of this solver. This will affect the accuracy of the output. type Accuracy: PerThing128; /// The error type of this implementation. type Error: core::fmt::Debug + core::cmp::PartialEq; /// Solve an NPoS solution with the given `voters`, `targets`, and select `to_elect` count /// of `targets`. fn solve( to_elect: usize, targets: Vec, voters: Vec<( Self::AccountId, VoteWeight, impl Clone + IntoIterator, )>, ) -> Result, Self::Error>; /// Measure the weight used in the calculation of the solver. /// - `voters` is the number of voters. /// - `targets` is the number of targets. /// - `vote_degree` is the degree ie the maximum numbers of votes per voter. fn weight(voters: u32, targets: u32, vote_degree: u32) -> Weight; } /// A quick and dirty solver, that produces a valid but probably worthless election result, but is /// fast. /// /// It choses a random number of winners without any consideration. /// /// Then it iterates over the voters and assigns them to the winners. /// /// It is only meant to be used in benchmarking. pub struct QuickDirtySolver(core::marker::PhantomData<(AccountId, Accuracy)>); impl NposSolver for QuickDirtySolver { type AccountId = AccountId; type Accuracy = Accuracy; type Error = &'static str; fn solve( to_elect: usize, targets: Vec, voters: Vec<( Self::AccountId, VoteWeight, impl Clone + IntoIterator, )>, ) -> Result, Self::Error> { use pezsp_std::collections::btree_map::BTreeMap; if to_elect > targets.len() { return Err("to_elect is greater than the number of targets."); } let winners = targets.into_iter().take(to_elect).collect::>(); let mut assignments = Vec::with_capacity(voters.len()); let mut final_winners = BTreeMap::::new(); for (voter, weight, votes) in voters { let our_winners = winners .iter() .filter(|w| votes.clone().into_iter().any(|v| v == **w)) .collect::>(); let our_winners_len = our_winners.len(); let distribution = our_winners .into_iter() .map(|w| { *final_winners.entry(w.clone()).or_default() += weight as u128; (w.clone(), Self::Accuracy::from_rational(1, our_winners_len as u128)) }) .collect::>(); let mut assignment = Assignment { who: voter, distribution }; assignment.try_normalize().unwrap(); assignments.push(assignment); } let winners = final_winners.into_iter().collect::>(); Ok(ElectionResult { winners, assignments }) } fn weight(_: u32, _: u32, _: u32) -> Weight { Default::default() } } /// A wrapper for [`pezsp_npos_elections::seq_phragmen`] that implements [`NposSolver`]. See the /// documentation of [`pezsp_npos_elections::seq_phragmen`] for more info. pub struct SequentialPhragmen( core::marker::PhantomData<(AccountId, Accuracy, Balancing)>, ); impl>> NposSolver for SequentialPhragmen { type AccountId = AccountId; type Accuracy = Accuracy; type Error = pezsp_npos_elections::Error; fn solve( winners: usize, targets: Vec, voters: Vec<( Self::AccountId, VoteWeight, impl Clone + IntoIterator, )>, ) -> Result, Self::Error> { pezsp_npos_elections::seq_phragmen(winners, targets, voters, Balancing::get()) } fn weight(voters: u32, targets: u32, vote_degree: u32) -> Weight { T::phragmen(voters, targets, vote_degree) } } /// A wrapper for [`pezsp_npos_elections::phragmms()`] that implements [`NposSolver`]. See the /// documentation of [`pezsp_npos_elections::phragmms()`] for more info. pub struct PhragMMS( core::marker::PhantomData<(AccountId, Accuracy, Balancing)>, ); impl>> NposSolver for PhragMMS { type AccountId = AccountId; type Accuracy = Accuracy; type Error = pezsp_npos_elections::Error; fn solve( winners: usize, targets: Vec, voters: Vec<( Self::AccountId, VoteWeight, impl Clone + IntoIterator, )>, ) -> Result, Self::Error> { pezsp_npos_elections::phragmms(winners, targets, voters, Balancing::get()) } fn weight(voters: u32, targets: u32, vote_degree: u32) -> Weight { T::phragmms(voters, targets, vote_degree) } } /// A voter, at the level of abstraction of this crate. pub type Voter = (AccountId, VoteWeight, BoundedVec); /// Same as [`Voter`], but parameterized by an [`ElectionDataProvider`]. pub type VoterOf = Voter<::AccountId, ::MaxVotesPerVoter>; /// A bounded vector of supports. Bounded equivalent to [`pezsp_npos_elections::Supports`]. #[derive( Default, Debug, Encode, Decode, DecodeWithMemTracking, scale_info::TypeInfo, MaxEncodedLen, )] #[codec(mel_bound(AccountId: MaxEncodedLen, Bound: Get))] #[scale_info(skip_type_params(Bound))] pub struct BoundedSupport> { /// Total support. pub total: ExtendedBalance, /// Support from voters. pub voters: BoundedVec<(AccountId, ExtendedBalance), Bound>, } impl> pezsp_npos_elections::Backings for &BoundedSupport { fn total(&self) -> ExtendedBalance { self.total } } impl> PartialEq for BoundedSupport { fn eq(&self, other: &Self) -> bool { self.total == other.total && self.voters == other.voters } } impl> From> for Support { fn from(b: BoundedSupport) -> Self { Support { total: b.total, voters: b.voters.into_inner() } } } impl> Clone for BoundedSupport { fn clone(&self) -> Self { Self { voters: self.voters.clone(), total: self.total } } } impl> TryFrom> for BoundedSupport { type Error = &'static str; fn try_from(s: pezsp_npos_elections::Support) -> Result { let voters = s.voters.try_into().map_err(|_| "voters bound not respected")?; Ok(Self { voters, total: s.total }) } } impl> BoundedSupport { /// Try and construct a `BoundedSupport` from an unbounded version, and reside to sorting and /// truncating if needed. /// /// Returns the number of backers removed. pub fn sorted_truncate_from( mut support: pezsp_npos_elections::Support, ) -> (Self, u32) { // If bounds meet, then short circuit. if let Ok(bounded) = support.clone().try_into() { return (bounded, 0); } let pre_len = support.voters.len(); // sort support based on stake of each backer, low to high. // Note: we don't sort high to low and truncate because we would have to track `total` // updates, so we need one iteration anyhow. support.voters.sort_by(|a, b| a.1.cmp(&b.1)); // then do the truncation. let mut bounded = Self { voters: Default::default(), total: 0 }; while let Some((voter, weight)) = support.voters.pop() { if let Err(_) = bounded.voters.try_push((voter, weight)) { break; } bounded.total += weight; } let post_len = bounded.voters.len(); (bounded, (pre_len - post_len) as u32) } } /// A bounded vector of [`BoundedSupport`]. /// /// A [`BoundedSupports`] is a set of [`pezsp_npos_elections::Supports`] which are bounded in two /// dimensions. `BInner` corresponds to the bound of the maximum backers per voter and `BOuter` /// corresponds to the bound of the maximum winners that the bounded supports may contain. /// /// With the bounds, we control the maximum size of a bounded supports instance. #[derive(Encode, Decode, DecodeWithMemTracking, TypeInfo, DefaultNoBound, MaxEncodedLen)] #[codec(mel_bound(AccountId: MaxEncodedLen, BOuter: Get, BInner: Get))] #[scale_info(skip_type_params(BOuter, BInner))] pub struct BoundedSupports, BInner: Get>( pub BoundedVec<(AccountId, BoundedSupport), BOuter>, ); /// Try and build yourself from another `BoundedSupports` with a different set of types. pub trait TryFromOtherBounds, BOtherInner: Get> { fn try_from_other_bounds( other: BoundedSupports, ) -> Result where Self: Sized; } impl< AccountId, BOuter: Get, BInner: Get, BOtherOuter: Get, BOuterInner: Get, > TryFromOtherBounds for BoundedSupports { fn try_from_other_bounds( other: BoundedSupports, ) -> Result { // NOTE: we might as well do this with unsafe rust and do it faster. if BOtherOuter::get() <= BOuter::get() && BOuterInner::get() <= BInner::get() { // Both ouf our bounds are larger than the input's bound, can convert. let supports = other .into_iter() .map(|(acc, b_support)| { b_support .try_into() .defensive_map_err(|_| Error::BoundsExceeded) .map(|b_support| (acc, b_support)) }) .collect::, _>>() .defensive()?; supports.try_into() } else { Err(crate::Error::BoundsExceeded) } } } impl, BInner: Get> BoundedSupports { /// Try and construct a `BoundedSupports` from an unbounded version, and reside to sorting and /// truncating if need ne. /// /// Two u32s returned are number of winners and backers removed respectively. pub fn sorted_truncate_from(supports: Supports) -> (Self, u32, u32) { // if bounds, meet, short circuit if let Ok(bounded) = supports.clone().try_into() { return (bounded, 0, 0); } let pre_winners = supports.len(); let mut backers_removed = 0; // first, convert all inner supports. let mut inner_supports = supports .into_iter() .map(|(account, support)| { let (bounded, removed) = BoundedSupport::::sorted_truncate_from(support); backers_removed += removed; (account, bounded) }) .collect::>(); // then sort outer supports based on total stake, high to low inner_supports.sort_by(|a, b| b.1.total.cmp(&a.1.total)); // then take the first slice that can fit. let bounded = BoundedSupports(BoundedVec::< (AccountId, BoundedSupport), BOuter, >::truncate_from(inner_supports)); let post_winners = bounded.len(); (bounded, (pre_winners - post_winners) as u32, backers_removed) } } /// Helper trait for conversion of a vector of unbounded supports into a vector of bounded ones. pub trait TryFromUnboundedPagedSupports, BInner: Get> { fn try_from_unbounded_paged( self, ) -> Result>, crate::Error> where Self: Sized; } impl, BInner: Get> TryFromUnboundedPagedSupports for Vec> { fn try_from_unbounded_paged( self, ) -> Result>, crate::Error> { self.into_iter() .map(|s| s.try_into().map_err(|_| crate::Error::BoundsExceeded)) .collect::, _>>() } } impl, BInner: Get> pezsp_npos_elections::EvaluateSupport for BoundedSupports { fn evaluate(&self) -> pezsp_npos_elections::ElectionScore { pezsp_npos_elections::evaluate_support(self.iter().map(|(_, s)| s)) } } impl, BInner: Get> pezsp_std::ops::DerefMut for BoundedSupports { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl, BInner: Get> Debug for BoundedSupports { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { for s in self.0.iter() { write!(f, "({:?}, {:?}, {:?}) ", s.0, s.1.total, s.1.voters)?; } Ok(()) } } impl, BInner: Get> PartialEq for BoundedSupports { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl, BInner: Get> Into> for BoundedSupports { fn into(self) -> Supports { // NOTE: can be done faster with unsafe code. self.0.into_iter().map(|(acc, b_support)| (acc, b_support.into())).collect() } } impl, BInner: Get> From), BOuter>> for BoundedSupports { fn from(t: BoundedVec<(AccountId, BoundedSupport), BOuter>) -> Self { Self(t) } } impl, BInner: Get> Clone for BoundedSupports { fn clone(&self) -> Self { Self(self.0.clone()) } } impl, BInner: Get> pezsp_std::ops::Deref for BoundedSupports { type Target = BoundedVec<(AccountId, BoundedSupport), BOuter>; fn deref(&self) -> &Self::Target { &self.0 } } impl, BInner: Get> IntoIterator for BoundedSupports { type Item = (AccountId, BoundedSupport); type IntoIter = pezsp_std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl, BInner: Get> TryFrom> for BoundedSupports { type Error = crate::Error; fn try_from(supports: Supports) -> Result { // optimization note: pre-allocate outer bounded vec. let mut outer_bounded_supports = BoundedVec::< (AccountId, BoundedSupport), BOuter, >::with_bounded_capacity( supports.len().min(BOuter::get() as usize) ); // optimization note: avoid intermediate allocations. supports .into_iter() .map(|(account, support)| (account, support.try_into().map_err(|_| ()))) .try_for_each(|(account, maybe_bounded_supports)| { outer_bounded_supports .try_push((account, maybe_bounded_supports?)) .map_err(|_| ()) }) .map_err(|_| crate::Error::BoundsExceeded)?; Ok(outer_bounded_supports.into()) } } /// Same as `BoundedSupports` but parameterized by an `ElectionProvider`. pub type BoundedSupportsOf = BoundedSupports< ::AccountId, ::MaxWinnersPerPage, ::MaxBackersPerWinner, >; pezsp_core::generate_feature_enabled_macro!( runtime_benchmarks_enabled, feature = "runtime-benchmarks", $ ); pezsp_core::generate_feature_enabled_macro!( runtime_benchmarks_or_std_enabled, any(feature = "runtime-benchmarks", feature = "std"), $ );