mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 04:41:02 +00:00
PhragMMS election. (#6685)
* Revamp npos-elections and implement phragmms * Update primitives/npos-elections/src/phragmms.rs * Fix build * Some review grumbles * Add some stuff for remote testing * fix some of the grumbles. * Add remote testing stuff. * Cleanup * fix docs * Update primitives/arithmetic/src/rational.rs Co-authored-by: Dan Forbes <dan@danforbes.dev> * Small config change * Better handling of approval_stake == 0 * Final touhces. * Clean fuzzer a bit * Clean fuzzer a bit * Update primitives/npos-elections/src/balancing.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix fuzzer. * Better api for normalize * Add noramlize_up * A large number of small fixes. * make it merge ready * Fix warns * bump * Fix fuzzers a bit. * Fix warns as well. * Fix more tests. Co-authored-by: Dan Forbes <dan@danforbes.dev> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -304,7 +304,7 @@ use frame_support::{
|
||||
};
|
||||
use pallet_session::historical;
|
||||
use sp_runtime::{
|
||||
Percent, Perbill, PerU16, PerThing, RuntimeDebug, DispatchError,
|
||||
Percent, Perbill, PerU16, PerThing, InnerOf, RuntimeDebug, DispatchError,
|
||||
curve::PiecewiseLinear,
|
||||
traits::{
|
||||
Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion,
|
||||
@@ -707,18 +707,18 @@ pub struct ElectionSize {
|
||||
|
||||
|
||||
impl<BlockNumber: PartialEq> ElectionStatus<BlockNumber> {
|
||||
fn is_open_at(&self, n: BlockNumber) -> bool {
|
||||
pub fn is_open_at(&self, n: BlockNumber) -> bool {
|
||||
*self == Self::Open(n)
|
||||
}
|
||||
|
||||
fn is_closed(&self) -> bool {
|
||||
pub fn is_closed(&self) -> bool {
|
||||
match self {
|
||||
Self::Closed => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_open(&self) -> bool {
|
||||
pub fn is_open(&self) -> bool {
|
||||
!self.is_closed()
|
||||
}
|
||||
}
|
||||
@@ -1372,6 +1372,25 @@ decl_module! {
|
||||
T::BondingDuration::get(),
|
||||
)
|
||||
);
|
||||
|
||||
use sp_runtime::UpperOf;
|
||||
// see the documentation of `Assignment::try_normalize`. Now we can ensure that this
|
||||
// will always return `Ok`.
|
||||
// 1. Maximum sum of Vec<ChainAccuracy> must fit into `UpperOf<ChainAccuracy>`.
|
||||
assert!(
|
||||
<usize as TryInto<UpperOf<ChainAccuracy>>>::try_into(MAX_NOMINATIONS)
|
||||
.unwrap()
|
||||
.checked_mul(<ChainAccuracy>::one().deconstruct().try_into().unwrap())
|
||||
.is_some()
|
||||
);
|
||||
|
||||
// 2. Maximum sum of Vec<OffchainAccuracy> must fit into `UpperOf<OffchainAccuracy>`.
|
||||
assert!(
|
||||
<usize as TryInto<UpperOf<OffchainAccuracy>>>::try_into(MAX_NOMINATIONS)
|
||||
.unwrap()
|
||||
.checked_mul(<OffchainAccuracy>::one().deconstruct().try_into().unwrap())
|
||||
.is_some()
|
||||
);
|
||||
}
|
||||
|
||||
/// Take the origin account as a stash and lock up `value` of its balance. `controller` will
|
||||
@@ -2165,7 +2184,7 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// internal impl of [`slashable_balance_of`] that returns [`VoteWeight`].
|
||||
fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight {
|
||||
pub fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight {
|
||||
<T::CurrencyToVote as Convert<BalanceOf<T>, VoteWeight>>::convert(
|
||||
Self::slashable_balance_of(stash)
|
||||
)
|
||||
@@ -2577,14 +2596,10 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
|
||||
// build the support map thereof in order to evaluate.
|
||||
// OPTIMIZATION: loop to create the staked assignments but it would bloat the code. Okay for
|
||||
// now as it does not add to the complexity order.
|
||||
let (supports, num_error) = build_support_map::<T::AccountId>(
|
||||
let supports = build_support_map::<T::AccountId>(
|
||||
&winners,
|
||||
&staked_assignments,
|
||||
);
|
||||
// This technically checks that all targets in all nominators were among the winners.
|
||||
ensure!(num_error == 0, Error::<T>::OffchainElectionBogusEdge);
|
||||
).map_err(|_| Error::<T>::OffchainElectionBogusEdge)?;
|
||||
|
||||
// Check if the score is the same as the claimed one.
|
||||
let submitted_score = evaluate_support(&supports);
|
||||
@@ -2811,7 +2826,7 @@ impl<T: Trait> Module<T> {
|
||||
fn try_do_election() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> {
|
||||
// an election result from either a stored submission or locally executed one.
|
||||
let next_result = <QueuedElected<T>>::take().or_else(||
|
||||
Self::do_phragmen_with_post_processing::<ChainAccuracy>(ElectionCompute::OnChain)
|
||||
Self::do_on_chain_phragmen()
|
||||
);
|
||||
|
||||
// either way, kill this. We remove it here to make sure it always has the exact same
|
||||
@@ -2828,13 +2843,8 @@ impl<T: Trait> Module<T> {
|
||||
/// `PrimitiveElectionResult` into `ElectionResult`.
|
||||
///
|
||||
/// No storage item is updated.
|
||||
fn do_phragmen_with_post_processing<Accuracy: PerThing>(compute: ElectionCompute)
|
||||
-> Option<ElectionResult<T::AccountId, BalanceOf<T>>>
|
||||
where
|
||||
Accuracy: sp_std::ops::Mul<ExtendedBalance, Output=ExtendedBalance>,
|
||||
ExtendedBalance: From<<Accuracy as PerThing>::Inner>,
|
||||
{
|
||||
if let Some(phragmen_result) = Self::do_phragmen::<Accuracy>() {
|
||||
fn do_on_chain_phragmen() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> {
|
||||
if let Some(phragmen_result) = Self::do_phragmen::<ChainAccuracy>(0) {
|
||||
let elected_stashes = phragmen_result.winners.iter()
|
||||
.map(|(s, _)| s.clone())
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
@@ -2845,10 +2855,17 @@ impl<T: Trait> Module<T> {
|
||||
Self::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
let (supports, _) = build_support_map::<T::AccountId>(
|
||||
let supports = build_support_map::<T::AccountId>(
|
||||
&elected_stashes,
|
||||
&staked_assignments,
|
||||
);
|
||||
)
|
||||
.map_err(|_|
|
||||
log!(
|
||||
error,
|
||||
"💸 on-chain phragmen is failing due to a problem in the result. This must be a bug."
|
||||
)
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
// collect exposures
|
||||
let exposures = Self::collect_exposure(supports);
|
||||
@@ -2860,7 +2877,7 @@ impl<T: Trait> Module<T> {
|
||||
Some(ElectionResult::<T::AccountId, BalanceOf<T>> {
|
||||
elected_stashes,
|
||||
exposures,
|
||||
compute,
|
||||
compute: ElectionCompute::OnChain,
|
||||
})
|
||||
} else {
|
||||
// There were not enough candidates for even our minimal level of functionality. This is
|
||||
@@ -2874,10 +2891,14 @@ impl<T: Trait> Module<T> {
|
||||
/// Execute phragmen election and return the new results. No post-processing is applied and the
|
||||
/// raw edge weights are returned.
|
||||
///
|
||||
/// Self votes are added and nominations before the most recent slashing span are reaped.
|
||||
/// Self votes are added and nominations before the most recent slashing span are ignored.
|
||||
///
|
||||
/// No storage item is updated.
|
||||
fn do_phragmen<Accuracy: PerThing>() -> Option<PrimitiveElectionResult<T::AccountId, Accuracy>> {
|
||||
pub fn do_phragmen<Accuracy: PerThing>(
|
||||
iterations: usize,
|
||||
) -> Option<PrimitiveElectionResult<T::AccountId, Accuracy>>
|
||||
where ExtendedBalance: From<InnerOf<Accuracy>>
|
||||
{
|
||||
let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec<T::AccountId>)> = Vec::new();
|
||||
let mut all_validators = Vec::new();
|
||||
for (validator, _) in <Validators<T>>::iter() {
|
||||
@@ -2906,16 +2927,26 @@ impl<T: Trait> Module<T> {
|
||||
(n, s, ns)
|
||||
}));
|
||||
|
||||
seq_phragmen::<_, Accuracy>(
|
||||
Self::validator_count() as usize,
|
||||
Self::minimum_validator_count().max(1) as usize,
|
||||
all_validators,
|
||||
all_nominators,
|
||||
)
|
||||
if all_validators.len() < Self::minimum_validator_count().max(1) as usize {
|
||||
// If we don't have enough candidates, nothing to do.
|
||||
log!(error, "💸 Chain does not have enough staking candidates to operate. Era {:?}.", Self::current_era());
|
||||
None
|
||||
} else {
|
||||
seq_phragmen::<_, Accuracy>(
|
||||
Self::validator_count() as usize,
|
||||
all_validators,
|
||||
all_nominators,
|
||||
Some((iterations, 0)), // exactly run `iterations` rounds.
|
||||
)
|
||||
.map_err(|err| log!(error, "Call to seq-phragmen failed due to {}", err))
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a [`Exposure`]
|
||||
fn collect_exposure(supports: SupportMap<T::AccountId>) -> Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)> {
|
||||
fn collect_exposure(
|
||||
supports: SupportMap<T::AccountId>,
|
||||
) -> Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)> {
|
||||
let to_balance = |e: ExtendedBalance|
|
||||
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user