mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 02:21:04 +00:00
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:
@@ -25,5 +25,5 @@ parity-scale-codec = "3.0.0"
|
||||
scale-info = "2.0.1"
|
||||
sp-arithmetic = { version = "5.0.0", path = "../../../primitives/arithmetic" }
|
||||
# used by generate_solution_type:
|
||||
sp-npos-elections = { version = "4.0.0-dev", path = "../../../primitives/npos-elections" }
|
||||
frame-election-provider-support = { version = "4.0.0-dev", path = ".." }
|
||||
trybuild = "1.0.53"
|
||||
|
||||
@@ -20,6 +20,7 @@ rand = { version = "0.8", features = ["std", "small_rng"] }
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }
|
||||
frame-election-provider-solution-type = { version = "4.0.0-dev", path = ".." }
|
||||
frame-election-provider-support = { version = "4.0.0-dev", path = "../.." }
|
||||
sp-arithmetic = { version = "5.0.0", path = "../../../../primitives/arithmetic" }
|
||||
sp-runtime = { version = "6.0.0", path = "../../../../primitives/runtime" }
|
||||
# used by generate_solution_type:
|
||||
|
||||
@@ -51,14 +51,14 @@ fn decode_impl(
|
||||
quote! {
|
||||
let #name =
|
||||
<
|
||||
_npos::sp_std::prelude::Vec<(_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#target_type>)>
|
||||
_feps::sp_std::prelude::Vec<(_feps::codec::Compact<#voter_type>, _feps::codec::Compact<#target_type>)>
|
||||
as
|
||||
_npos::codec::Decode
|
||||
_feps::codec::Decode
|
||||
>::decode(value)?;
|
||||
let #name = #name
|
||||
.into_iter()
|
||||
.map(|(v, t)| (v.0, t.0))
|
||||
.collect::<_npos::sp_std::prelude::Vec<_>>();
|
||||
.collect::<_feps::sp_std::prelude::Vec<_>>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,12 +73,12 @@ fn decode_impl(
|
||||
quote! {
|
||||
let #name =
|
||||
<
|
||||
_npos::sp_std::prelude::Vec<(
|
||||
_npos::codec::Compact<#voter_type>,
|
||||
[(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>); #c-1],
|
||||
_npos::codec::Compact<#target_type>,
|
||||
_feps::sp_std::prelude::Vec<(
|
||||
_feps::codec::Compact<#voter_type>,
|
||||
[(_feps::codec::Compact<#target_type>, _feps::codec::Compact<#weight_type>); #c-1],
|
||||
_feps::codec::Compact<#target_type>,
|
||||
)>
|
||||
as _npos::codec::Decode
|
||||
as _feps::codec::Decode
|
||||
>::decode(value)?;
|
||||
let #name = #name
|
||||
.into_iter()
|
||||
@@ -87,7 +87,7 @@ fn decode_impl(
|
||||
[ #inner_impl ],
|
||||
t_last.0,
|
||||
))
|
||||
.collect::<_npos::sp_std::prelude::Vec<_>>();
|
||||
.collect::<_feps::sp_std::prelude::Vec<_>>();
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream2>();
|
||||
@@ -100,8 +100,8 @@ fn decode_impl(
|
||||
.collect::<TokenStream2>();
|
||||
|
||||
quote!(
|
||||
impl _npos::codec::Decode for #ident {
|
||||
fn decode<I: _npos::codec::Input>(value: &mut I) -> Result<Self, _npos::codec::Error> {
|
||||
impl _feps::codec::Decode for #ident {
|
||||
fn decode<I: _feps::codec::Input>(value: &mut I) -> Result<Self, _feps::codec::Error> {
|
||||
#decode_impl_single
|
||||
#decode_impl_rest
|
||||
|
||||
@@ -123,10 +123,10 @@ fn encode_impl(ident: &syn::Ident, count: usize) -> TokenStream2 {
|
||||
let #name = self.#name
|
||||
.iter()
|
||||
.map(|(v, t)| (
|
||||
_npos::codec::Compact(v.clone()),
|
||||
_npos::codec::Compact(t.clone()),
|
||||
_feps::codec::Compact(v.clone()),
|
||||
_feps::codec::Compact(t.clone()),
|
||||
))
|
||||
.collect::<_npos::sp_std::prelude::Vec<_>>();
|
||||
.collect::<_feps::sp_std::prelude::Vec<_>>();
|
||||
#name.encode_to(&mut r);
|
||||
}
|
||||
};
|
||||
@@ -139,8 +139,8 @@ fn encode_impl(ident: &syn::Ident, count: usize) -> TokenStream2 {
|
||||
let inners_solution_array = (0..c - 1)
|
||||
.map(|i| {
|
||||
quote! {(
|
||||
_npos::codec::Compact(inner[#i].0.clone()),
|
||||
_npos::codec::Compact(inner[#i].1.clone()),
|
||||
_feps::codec::Compact(inner[#i].0.clone()),
|
||||
_feps::codec::Compact(inner[#i].1.clone()),
|
||||
),}
|
||||
})
|
||||
.collect::<TokenStream2>();
|
||||
@@ -149,19 +149,19 @@ fn encode_impl(ident: &syn::Ident, count: usize) -> TokenStream2 {
|
||||
let #name = self.#name
|
||||
.iter()
|
||||
.map(|(v, inner, t_last)| (
|
||||
_npos::codec::Compact(v.clone()),
|
||||
_feps::codec::Compact(v.clone()),
|
||||
[ #inners_solution_array ],
|
||||
_npos::codec::Compact(t_last.clone()),
|
||||
_feps::codec::Compact(t_last.clone()),
|
||||
))
|
||||
.collect::<_npos::sp_std::prelude::Vec<_>>();
|
||||
.collect::<_feps::sp_std::prelude::Vec<_>>();
|
||||
#name.encode_to(&mut r);
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream2>();
|
||||
|
||||
quote!(
|
||||
impl _npos::codec::Encode for #ident {
|
||||
fn encode(&self) -> _npos::sp_std::prelude::Vec<u8> {
|
||||
impl _feps::codec::Encode for #ident {
|
||||
fn encode(&self) -> _feps::sp_std::prelude::Vec<u8> {
|
||||
let mut r = vec![];
|
||||
#encode_impl_single
|
||||
#encode_impl_rest
|
||||
@@ -182,8 +182,8 @@ fn scale_info_impl(
|
||||
let name = format!("{}", vote_field(1));
|
||||
quote! {
|
||||
.field(|f|
|
||||
f.ty::<_npos::sp_std::prelude::Vec<
|
||||
(_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#target_type>)
|
||||
f.ty::<_feps::sp_std::prelude::Vec<
|
||||
(_feps::codec::Compact<#voter_type>, _feps::codec::Compact<#target_type>)
|
||||
>>()
|
||||
.name(#name)
|
||||
)
|
||||
@@ -194,10 +194,10 @@ fn scale_info_impl(
|
||||
let name = format!("{}", vote_field(2));
|
||||
quote! {
|
||||
.field(|f|
|
||||
f.ty::<_npos::sp_std::prelude::Vec<(
|
||||
_npos::codec::Compact<#voter_type>,
|
||||
(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>),
|
||||
_npos::codec::Compact<#target_type>
|
||||
f.ty::<_feps::sp_std::prelude::Vec<(
|
||||
_feps::codec::Compact<#voter_type>,
|
||||
(_feps::codec::Compact<#target_type>, _feps::codec::Compact<#weight_type>),
|
||||
_feps::codec::Compact<#target_type>
|
||||
)>>()
|
||||
.name(#name)
|
||||
)
|
||||
@@ -209,13 +209,13 @@ fn scale_info_impl(
|
||||
let name = format!("{}", vote_field(c));
|
||||
quote! {
|
||||
.field(|f|
|
||||
f.ty::<_npos::sp_std::prelude::Vec<(
|
||||
_npos::codec::Compact<#voter_type>,
|
||||
f.ty::<_feps::sp_std::prelude::Vec<(
|
||||
_feps::codec::Compact<#voter_type>,
|
||||
[
|
||||
(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>);
|
||||
(_feps::codec::Compact<#target_type>, _feps::codec::Compact<#weight_type>);
|
||||
#c - 1
|
||||
],
|
||||
_npos::codec::Compact<#target_type>
|
||||
_feps::codec::Compact<#target_type>
|
||||
)>>()
|
||||
.name(#name)
|
||||
)
|
||||
@@ -224,14 +224,14 @@ fn scale_info_impl(
|
||||
.collect::<TokenStream2>();
|
||||
|
||||
quote!(
|
||||
impl _npos::scale_info::TypeInfo for #ident {
|
||||
impl _feps::scale_info::TypeInfo for #ident {
|
||||
type Identity = Self;
|
||||
|
||||
fn type_info() -> _npos::scale_info::Type<_npos::scale_info::form::MetaForm> {
|
||||
_npos::scale_info::Type::builder()
|
||||
.path(_npos::scale_info::Path::new(stringify!(#ident), module_path!()))
|
||||
fn type_info() -> _feps::scale_info::Type<_feps::scale_info::form::MetaForm> {
|
||||
_feps::scale_info::Type::builder()
|
||||
.path(_feps::scale_info::Path::new(stringify!(#ident), module_path!()))
|
||||
.composite(
|
||||
_npos::scale_info::build::Fields::named()
|
||||
_feps::scale_info::build::Fields::named()
|
||||
#scale_info_impl_single
|
||||
#scale_info_impl_double
|
||||
#scale_info_impl_rest
|
||||
|
||||
@@ -88,7 +88,7 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
|
||||
/// ```
|
||||
///
|
||||
/// The given struct provides function to convert from/to `Assignment` as part of
|
||||
/// `sp_npos_elections::Solution` trait:
|
||||
/// `frame_election_provider_support::NposSolution` trait:
|
||||
///
|
||||
/// - `fn from_assignment<..>(..)`
|
||||
/// - `fn into_assignment<..>(..)`
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn syn_err(message: &'static str) -> syn::Error {
|
||||
///
|
||||
/// ```
|
||||
/// # use frame_election_provider_solution_type::generate_solution_type;
|
||||
/// # use sp_npos_elections::NposSolution;
|
||||
/// # use frame_election_provider_support::NposSolution;
|
||||
/// # use sp_arithmetic::per_things::Perbill;
|
||||
/// generate_solution_type!(
|
||||
/// #[compact]
|
||||
@@ -226,11 +226,11 @@ where
|
||||
}
|
||||
|
||||
fn imports() -> Result<TokenStream2> {
|
||||
match crate_name("sp-npos-elections") {
|
||||
Ok(FoundCrate::Itself) => Ok(quote! { use crate as _npos; }),
|
||||
Ok(FoundCrate::Name(sp_npos_elections)) => {
|
||||
let ident = syn::Ident::new(&sp_npos_elections, Span::call_site());
|
||||
Ok(quote!( extern crate #ident as _npos; ))
|
||||
match crate_name("frame-election-provider-support") {
|
||||
Ok(FoundCrate::Itself) => Ok(quote! { use crate as _feps; }),
|
||||
Ok(FoundCrate::Name(frame_election_provider_support)) => {
|
||||
let ident = syn::Ident::new(&frame_election_provider_support, Span::call_site());
|
||||
Ok(quote!( extern crate #ident as _feps; ))
|
||||
},
|
||||
Err(e) => Err(syn::Error::new(Span::call_site(), e)),
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
let name = vote_field(1);
|
||||
// NOTE: we use the visibility of the struct for the fields as well.. could be made better.
|
||||
quote!(
|
||||
#vis #name: _npos::sp_std::prelude::Vec<(#voter_type, #target_type)>,
|
||||
#vis #name: _feps::sp_std::prelude::Vec<(#voter_type, #target_type)>,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
let field_name = vote_field(c);
|
||||
let array_len = c - 1;
|
||||
quote!(
|
||||
#vis #field_name: _npos::sp_std::prelude::Vec<(
|
||||
#vis #field_name: _feps::sp_std::prelude::Vec<(
|
||||
#voter_type,
|
||||
[(#target_type, #weight_type); #array_len],
|
||||
#target_type
|
||||
@@ -83,9 +83,9 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
Eq,
|
||||
Clone,
|
||||
Debug,
|
||||
_npos::codec::Encode,
|
||||
_npos::codec::Decode,
|
||||
_npos::scale_info::TypeInfo,
|
||||
_feps::codec::Encode,
|
||||
_feps::codec::Decode,
|
||||
_feps::scale_info::TypeInfo,
|
||||
)])
|
||||
};
|
||||
|
||||
@@ -101,8 +101,8 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
#derives_and_maybe_compact_encoding
|
||||
#vis struct #ident { #single #rest }
|
||||
|
||||
use _npos::__OrInvalidIndex;
|
||||
impl _npos::NposSolution for #ident {
|
||||
use _feps::__OrInvalidIndex;
|
||||
impl _feps::NposSolution for #ident {
|
||||
const LIMIT: usize = #count;
|
||||
type VoterIndex = #voter_type;
|
||||
type TargetIndex = #target_type;
|
||||
@@ -114,34 +114,34 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
}
|
||||
|
||||
fn from_assignment<FV, FT, A>(
|
||||
assignments: &[_npos::Assignment<A, #weight_type>],
|
||||
assignments: &[_feps::Assignment<A, #weight_type>],
|
||||
voter_index: FV,
|
||||
target_index: FT,
|
||||
) -> Result<Self, _npos::Error>
|
||||
) -> Result<Self, _feps::Error>
|
||||
where
|
||||
A: _npos::IdentifierT,
|
||||
A: _feps::IdentifierT,
|
||||
for<'r> FV: Fn(&'r A) -> Option<Self::VoterIndex>,
|
||||
for<'r> FT: Fn(&'r A) -> Option<Self::TargetIndex>,
|
||||
{
|
||||
let mut #struct_name: #ident = Default::default();
|
||||
for _npos::Assignment { who, distribution } in assignments {
|
||||
for _feps::Assignment { who, distribution } in assignments {
|
||||
match distribution.len() {
|
||||
0 => continue,
|
||||
#from_impl
|
||||
_ => {
|
||||
return Err(_npos::Error::SolutionTargetOverflow);
|
||||
return Err(_feps::Error::SolutionTargetOverflow);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(#struct_name)
|
||||
}
|
||||
|
||||
fn into_assignment<A: _npos::IdentifierT>(
|
||||
fn into_assignment<A: _feps::IdentifierT>(
|
||||
self,
|
||||
voter_at: impl Fn(Self::VoterIndex) -> Option<A>,
|
||||
target_at: impl Fn(Self::TargetIndex) -> Option<A>,
|
||||
) -> Result<_npos::sp_std::prelude::Vec<_npos::Assignment<A, #weight_type>>, _npos::Error> {
|
||||
let mut #assignment_name: _npos::sp_std::prelude::Vec<_npos::Assignment<A, #weight_type>> = Default::default();
|
||||
) -> Result<_feps::sp_std::prelude::Vec<_feps::Assignment<A, #weight_type>>, _feps::Error> {
|
||||
let mut #assignment_name: _feps::sp_std::prelude::Vec<_feps::Assignment<A, #weight_type>> = Default::default();
|
||||
#into_impl
|
||||
Ok(#assignment_name)
|
||||
}
|
||||
@@ -158,10 +158,10 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
all_edges
|
||||
}
|
||||
|
||||
fn unique_targets(&self) -> _npos::sp_std::prelude::Vec<Self::TargetIndex> {
|
||||
fn unique_targets(&self) -> _feps::sp_std::prelude::Vec<Self::TargetIndex> {
|
||||
// NOTE: this implementation returns the targets sorted, but we don't use it yet per
|
||||
// se, nor is the API enforcing it.
|
||||
use _npos::sp_std::collections::btree_set::BTreeSet;
|
||||
use _feps::sp_std::collections::btree_set::BTreeSet;
|
||||
let mut all_targets: BTreeSet<Self::TargetIndex> = BTreeSet::new();
|
||||
let mut maybe_insert_target = |t: Self::TargetIndex| {
|
||||
all_targets.insert(t);
|
||||
@@ -173,22 +173,22 @@ pub(crate) fn generate(def: crate::SolutionDef) -> Result<TokenStream2> {
|
||||
}
|
||||
}
|
||||
|
||||
type __IndexAssignment = _npos::IndexAssignment<
|
||||
<#ident as _npos::NposSolution>::VoterIndex,
|
||||
<#ident as _npos::NposSolution>::TargetIndex,
|
||||
<#ident as _npos::NposSolution>::Accuracy,
|
||||
type __IndexAssignment = _feps::IndexAssignment<
|
||||
<#ident as _feps::NposSolution>::VoterIndex,
|
||||
<#ident as _feps::NposSolution>::TargetIndex,
|
||||
<#ident as _feps::NposSolution>::Accuracy,
|
||||
>;
|
||||
impl<'a> _npos::sp_std::convert::TryFrom<&'a [__IndexAssignment]> for #ident {
|
||||
type Error = _npos::Error;
|
||||
impl<'a> _feps::sp_std::convert::TryFrom<&'a [__IndexAssignment]> for #ident {
|
||||
type Error = _feps::Error;
|
||||
fn try_from(index_assignments: &'a [__IndexAssignment]) -> Result<Self, Self::Error> {
|
||||
let mut #struct_name = #ident::default();
|
||||
|
||||
for _npos::IndexAssignment { who, distribution } in index_assignments {
|
||||
for _feps::IndexAssignment { who, distribution } in index_assignments {
|
||||
match distribution.len() {
|
||||
0 => {}
|
||||
#from_index_impl
|
||||
_ => {
|
||||
return Err(_npos::Error::SolutionTargetOverflow);
|
||||
return Err(_feps::Error::SolutionTargetOverflow);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -310,7 +310,7 @@ pub(crate) fn into_impl(
|
||||
let name = vote_field(1);
|
||||
quote!(
|
||||
for (voter_index, target_index) in self.#name {
|
||||
#assignments.push(_npos::Assignment {
|
||||
#assignments.push(_feps::Assignment {
|
||||
who: voter_at(voter_index).or_invalid_index()?,
|
||||
distribution: vec![
|
||||
(target_at(target_index).or_invalid_index()?, #per_thing::one())
|
||||
@@ -329,25 +329,25 @@ pub(crate) fn into_impl(
|
||||
let mut inners_parsed = inners
|
||||
.iter()
|
||||
.map(|(ref t_idx, p)| {
|
||||
sum = _npos::sp_arithmetic::traits::Saturating::saturating_add(sum, *p);
|
||||
sum = _feps::sp_arithmetic::traits::Saturating::saturating_add(sum, *p);
|
||||
let target = target_at(*t_idx).or_invalid_index()?;
|
||||
Ok((target, *p))
|
||||
})
|
||||
.collect::<Result<_npos::sp_std::prelude::Vec<(A, #per_thing)>, _npos::Error>>()?;
|
||||
.collect::<Result<_feps::sp_std::prelude::Vec<(A, #per_thing)>, _feps::Error>>()?;
|
||||
|
||||
if sum >= #per_thing::one() {
|
||||
return Err(_npos::Error::SolutionWeightOverflow);
|
||||
return Err(_feps::Error::SolutionWeightOverflow);
|
||||
}
|
||||
|
||||
// defensive only. Since Percent doesn't have `Sub`.
|
||||
let p_last = _npos::sp_arithmetic::traits::Saturating::saturating_sub(
|
||||
let p_last = _feps::sp_arithmetic::traits::Saturating::saturating_sub(
|
||||
#per_thing::one(),
|
||||
sum,
|
||||
);
|
||||
|
||||
inners_parsed.push((target_at(t_last_idx).or_invalid_index()?, p_last));
|
||||
|
||||
#assignments.push(_npos::Assignment {
|
||||
#assignments.push(_feps::Assignment {
|
||||
who: voter_at(voter_index).or_invalid_index()?,
|
||||
distribution: inners_parsed,
|
||||
});
|
||||
|
||||
@@ -167,17 +167,86 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub mod onchain;
|
||||
use frame_support::{traits::Get, BoundedVec};
|
||||
pub mod traits;
|
||||
use codec::{Decode, Encode};
|
||||
use frame_support::{traits::Get, BoundedVec, RuntimeDebug};
|
||||
use sp_runtime::traits::Bounded;
|
||||
use sp_std::{fmt::Debug, prelude::*};
|
||||
|
||||
/// Re-export some type as they are used in the interface.
|
||||
/// Re-export the solution generation macro.
|
||||
pub use frame_election_provider_solution_type::generate_solution_type;
|
||||
/// Re-export some type as they are used in the interface.
|
||||
pub use sp_arithmetic::PerThing;
|
||||
pub use sp_npos_elections::{
|
||||
Assignment, ElectionResult, ExtendedBalance, IdentifierT, PerThing128, Support, Supports,
|
||||
VoteWeight,
|
||||
Assignment, ElectionResult, Error, ExtendedBalance, IdentifierT, PerThing128, Support,
|
||||
Supports, VoteWeight,
|
||||
};
|
||||
pub use traits::NposSolution;
|
||||
|
||||
// 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;
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 [`NposSolution`].
|
||||
pub type IndexAssignmentOf<C> = IndexAssignment<
|
||||
<C as NposSolution>::VoterIndex,
|
||||
<C as NposSolution>::TargetIndex,
|
||||
<C as NposSolution>::Accuracy,
|
||||
>;
|
||||
|
||||
/// Types that are used by the data provider trait.
|
||||
pub mod data_provider {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// 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
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits for the election operations.
|
||||
|
||||
use crate::{Assignment, IdentifierT, IndexAssignmentOf, PerThing128, VoteWeight};
|
||||
use codec::Encode;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_arithmetic::traits::{Bounded, UniqueSaturatedInto};
|
||||
use sp_npos_elections::{ElectionScore, Error, EvaluateSupport};
|
||||
use sp_std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt::Debug,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// 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 =
|
||||
sp_npos_elections::helpers::assignment_ratio_to_staked_normalized(ratio, stake_of)?;
|
||||
let supports = sp_npos_elections::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>;
|
||||
}
|
||||
Reference in New Issue
Block a user