Moving NposSolution to frame (#11031)

* Move `sp-npos-elections-solution-type`
to `frame-election-provider-support`
First stab at it, will need to amend some more stuff

* Fixing tests

* Fixing tests

* Fixing cargo.toml for std configuration

* fmt

* Committing suggested changes
renaming, and re exporting macro.

* Removing unneeded imports

* Move `NposSolution` to frame

* Removing `npos_election` dependencies
Implementing _fpes better

* some feedback for moving NPoSSolution to frame

* fmt

* more formatting

* Fixed some imports and fmt

* Fixing docs

Co-authored-by: kianenigma <kian@parity.io>
This commit is contained in:
Georges
2022-03-16 21:27:19 +00:00
committed by GitHub
parent 7a54efd1f2
commit 26a8c7e6b2
19 changed files with 312 additions and 292 deletions
@@ -17,7 +17,7 @@
//! Structs and helpers for distributing a voter's stake among various winners.
use crate::{Error, ExtendedBalance, IdentifierT, PerThing128, __OrInvalidIndex};
use crate::{ExtendedBalance, IdentifierT, PerThing128};
#[cfg(feature = "std")]
use codec::{Decode, Encode};
use sp_arithmetic::{
@@ -166,44 +166,3 @@ impl<AccountId> StakedAssignment<AccountId> {
self.distribution.iter().fold(Zero::zero(), |a, b| a.saturating_add(b.1))
}
}
/// The [`IndexAssignment`] type is an intermediate between the assignments list
/// ([`&[Assignment<T>]`][Assignment]) and `SolutionOf<T>`.
///
/// The voter and target identifiers have already been replaced with appropriate indices,
/// making it fast to repeatedly encode into a `SolutionOf<T>`. 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<VoterIndex, TargetIndex, P: PerThing> {
/// 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<VoterIndex, TargetIndex, P: PerThing> IndexAssignment<VoterIndex, TargetIndex, P> {
pub fn new<AccountId: IdentifierT>(
assignment: &Assignment<AccountId, P>,
voter_index: impl Fn(&AccountId) -> Option<VoterIndex>,
target_index: impl Fn(&AccountId) -> Option<TargetIndex>,
) -> Result<Self, Error> {
Ok(Self {
who: voter_index(&assignment.who).or_invalid_index()?,
distribution: assignment
.distribution
.iter()
.map(|(target, proportion)| Some((target_index(target)?, proportion.clone())))
.collect::<Option<Vec<_>>>()
.or_invalid_index()?,
})
}
}
/// A type alias for [`IndexAssignment`] made from [`crate::NposSolution`].
pub type IndexAssignmentOf<C> = IndexAssignment<
<C as crate::NposSolution>::VoterIndex,
<C as crate::NposSolution>::TargetIndex,
<C as crate::NposSolution>::Accuracy,
>;
+3 -13
View File
@@ -100,26 +100,16 @@ pub mod pjr;
pub mod reduce;
pub mod traits;
pub use assignments::{Assignment, IndexAssignment, IndexAssignmentOf, StakedAssignment};
pub use assignments::{Assignment, StakedAssignment};
pub use balancing::*;
pub use helpers::*;
pub use phragmen::*;
pub use phragmms::*;
pub use pjr::*;
pub use reduce::reduce;
pub use traits::{IdentifierT, NposSolution, PerThing128, __OrInvalidIndex};
pub use traits::{IdentifierT, PerThing128};
// re-export for the solution macro, with the dependencies of the macro.
#[doc(hidden)]
pub use codec;
#[doc(hidden)]
pub use scale_info;
#[doc(hidden)]
pub use sp_arithmetic;
#[doc(hidden)]
pub use sp_std;
/// The errors that might occur in the this crate and solution-type.
/// The errors that might occur in this crate and `frame-election-provider-solution-type`.
#[derive(Eq, PartialEq, RuntimeDebug)]
pub enum Error {
/// While going from solution indices to ratio, the weight of all the edges has gone above the
@@ -17,22 +17,9 @@
//! Traits for the npos-election operations.
use crate::{
Assignment, ElectionScore, Error, EvaluateSupport, ExtendedBalance, IndexAssignmentOf,
VoteWeight,
};
use codec::Encode;
use scale_info::TypeInfo;
use sp_arithmetic::{
traits::{Bounded, UniqueSaturatedInto},
PerThing,
};
use sp_std::{
convert::{TryFrom, TryInto},
fmt::Debug,
ops::Mul,
prelude::*,
};
use crate::ExtendedBalance;
use sp_arithmetic::PerThing;
use sp_std::{fmt::Debug, ops::Mul, prelude::*};
/// an aggregator trait for a generic type of a voter/target identifier. This usually maps to
/// substrate's account id.
@@ -42,116 +29,3 @@ impl<T: Clone + Eq + Ord + Debug + codec::Codec> IdentifierT for T {}
/// Aggregator trait for a PerThing that can be multiplied by u128 (ExtendedBalance).
pub trait PerThing128: PerThing + Mul<ExtendedBalance, Output = ExtendedBalance> {}
impl<T: PerThing + Mul<ExtendedBalance, Output = ExtendedBalance>> PerThing128 for T {}
/// 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.
#[doc(hidden)]
pub trait __OrInvalidIndex<T> {
fn or_invalid_index(self) -> Result<T, Error>;
}
impl<T> __OrInvalidIndex<T> for Option<T> {
fn or_invalid_index(self) -> Result<T, Error> {
self.ok_or(Error::SolutionInvalidIndex)
}
}
/// An opaque index-based, NPoS solution type.
pub trait NposSolution
where
Self: Sized + for<'a> sp_std::convert::TryFrom<&'a [IndexAssignmentOf<Self>], Error = Error>,
{
/// The maximum number of votes that are allowed.
const LIMIT: usize;
/// The voter type. Needs to be an index (convert to usize).
type VoterIndex: UniqueSaturatedInto<usize>
+ TryInto<usize>
+ TryFrom<usize>
+ Debug
+ Copy
+ Clone
+ Bounded
+ Encode
+ TypeInfo;
/// The target type. Needs to be an index (convert to usize).
type TargetIndex: UniqueSaturatedInto<usize>
+ TryInto<usize>
+ TryFrom<usize>
+ Debug
+ Copy
+ Clone
+ Bounded
+ Encode
+ TypeInfo;
/// The weight/accuracy type of each vote.
type Accuracy: PerThing128;
/// Get the length of all the voters that this type is encoding.
///
/// This is basically the same as the number of assignments, or number of active voters.
fn voter_count(&self) -> usize;
/// Get the total count of edges.
///
/// This is effectively in the range of {[`Self::voter_count`], [`Self::voter_count`] *
/// [`Self::LIMIT`]}.
fn edge_count(&self) -> usize;
/// Get the number of unique targets in the whole struct.
///
/// Once presented with a list of winners, this set and the set of winners must be
/// equal.
fn unique_targets(&self) -> Vec<Self::TargetIndex>;
/// Get the average edge count.
fn average_edge_count(&self) -> usize {
self.edge_count().checked_div(self.voter_count()).unwrap_or(0)
}
/// Compute the score of this solution type.
fn score<A, FS>(
self,
stake_of: FS,
voter_at: impl Fn(Self::VoterIndex) -> Option<A>,
target_at: impl Fn(Self::TargetIndex) -> Option<A>,
) -> Result<ElectionScore, Error>
where
for<'r> FS: Fn(&'r A) -> VoteWeight,
A: IdentifierT,
{
let ratio = self.into_assignment(voter_at, target_at)?;
let staked = crate::helpers::assignment_ratio_to_staked_normalized(ratio, stake_of)?;
let supports = crate::to_supports(&staked);
Ok(supports.evaluate())
}
/// Remove a certain voter.
///
/// This will only search until the first instance of `to_remove`, and return true. If
/// no instance is found (no-op), then it returns false.
///
/// In other words, if this return true, exactly **one** element must have been removed self.
fn remove_voter(&mut self, to_remove: Self::VoterIndex) -> bool;
/// Build self from a list of assignments.
fn from_assignment<FV, FT, A>(
assignments: &[Assignment<A, Self::Accuracy>],
voter_index: FV,
target_index: FT,
) -> Result<Self, Error>
where
A: IdentifierT,
for<'r> FV: Fn(&'r A) -> Option<Self::VoterIndex>,
for<'r> FT: Fn(&'r A) -> Option<Self::TargetIndex>;
/// Convert self into a `Vec<Assignment<A, Self::Accuracy>>`
fn into_assignment<A: IdentifierT>(
self,
voter_at: impl Fn(Self::VoterIndex) -> Option<A>,
target_at: impl Fn(Self::TargetIndex) -> Option<A>,
) -> Result<Vec<Assignment<A, Self::Accuracy>>, Error>;
}