mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +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);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ use frame_support::{
|
||||
use sp_io;
|
||||
use sp_npos_elections::{
|
||||
build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore,
|
||||
VoteWeight,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
@@ -198,6 +197,7 @@ parameter_types! {
|
||||
pub const MaximumBlockWeight: Weight = 1024;
|
||||
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||
pub const MaxLocks: u32 = 1024;
|
||||
}
|
||||
impl frame_system::Trait for Test {
|
||||
type BaseCallFilter = ();
|
||||
@@ -227,7 +227,7 @@ impl frame_system::Trait for Test {
|
||||
type SystemWeightInfo = ();
|
||||
}
|
||||
impl pallet_balances::Trait for Test {
|
||||
type MaxLocks = ();
|
||||
type MaxLocks = MaxLocks;
|
||||
type Balance = Balance;
|
||||
type Event = MetaEvent;
|
||||
type DustRemoval = ();
|
||||
@@ -857,9 +857,9 @@ pub(crate) fn horrible_npos_solution(
|
||||
// Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used
|
||||
// for testing.
|
||||
let score = {
|
||||
let (_, _, better_score) = prepare_submission_with(true, 0, |_| {});
|
||||
let (_, _, better_score) = prepare_submission_with(true, true, 0, |_| {});
|
||||
|
||||
let support = build_support_map::<AccountId>(&winners, &staked_assignment).0;
|
||||
let support = build_support_map::<AccountId>(&winners, &staked_assignment).unwrap();
|
||||
let score = evaluate_support(&support);
|
||||
|
||||
assert!(sp_npos_elections::is_score_better::<Perbill>(
|
||||
@@ -898,9 +898,13 @@ pub(crate) fn horrible_npos_solution(
|
||||
(compact, winners, score)
|
||||
}
|
||||
|
||||
// Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we
|
||||
// cannot do it since we want to have `tweak` injected into the process.
|
||||
/// Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we
|
||||
/// cannot do it since we want to have `tweak` injected into the process.
|
||||
///
|
||||
/// If the input is being tweaked in a way that the score cannot be compute accurately,
|
||||
/// `compute_real_score` can be set to true. In this case a `Default` score is returned.
|
||||
pub(crate) fn prepare_submission_with(
|
||||
compute_real_score: bool,
|
||||
do_reduce: bool,
|
||||
iterations: usize,
|
||||
tweak: impl FnOnce(&mut Vec<StakedAssignment<AccountId>>),
|
||||
@@ -909,26 +913,13 @@ pub(crate) fn prepare_submission_with(
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = Staking::do_phragmen::<OffchainAccuracy>().unwrap();
|
||||
} = Staking::do_phragmen::<OffchainAccuracy>(iterations).unwrap();
|
||||
let winners = sp_npos_elections::to_without_backing(winners);
|
||||
|
||||
let stake_of = |who: &AccountId| -> VoteWeight {
|
||||
<CurrencyToVoteHandler as Convert<Balance, VoteWeight>>::convert(
|
||||
Staking::slashable_balance_of(&who)
|
||||
)
|
||||
};
|
||||
|
||||
let mut staked = sp_npos_elections::assignment_ratio_to_staked(assignments, stake_of);
|
||||
let (mut support_map, _) = build_support_map::<AccountId>(&winners, &staked);
|
||||
|
||||
if iterations > 0 {
|
||||
sp_npos_elections::balance_solution(
|
||||
&mut staked,
|
||||
&mut support_map,
|
||||
Zero::zero(),
|
||||
iterations,
|
||||
);
|
||||
}
|
||||
let mut staked = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments,
|
||||
Staking::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
// apply custom tweaks. awesome for testing.
|
||||
tweak(&mut staked);
|
||||
@@ -962,17 +953,19 @@ pub(crate) fn prepare_submission_with(
|
||||
let assignments_reduced = sp_npos_elections::assignment_staked_to_ratio(staked);
|
||||
|
||||
// re-compute score by converting, yet again, into staked type
|
||||
let score = {
|
||||
let score = if compute_real_score {
|
||||
let staked = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments_reduced.clone(),
|
||||
Staking::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
let (support_map, _) = build_support_map::<AccountId>(
|
||||
let support_map = build_support_map::<AccountId>(
|
||||
winners.as_slice(),
|
||||
staked.as_slice(),
|
||||
);
|
||||
).unwrap();
|
||||
evaluate_support::<AccountId>(&support_map)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let compact =
|
||||
|
||||
@@ -25,10 +25,10 @@ use crate::{
|
||||
use frame_system::offchain::SubmitTransaction;
|
||||
use sp_npos_elections::{
|
||||
build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, ElectionResult,
|
||||
ElectionScore, balance_solution,
|
||||
ElectionScore,
|
||||
};
|
||||
use sp_runtime::offchain::storage::StorageValueRef;
|
||||
use sp_runtime::{PerThing, RuntimeDebug, traits::{TrailingZeroInput, Zero}};
|
||||
use sp_runtime::{PerThing, RuntimeDebug, traits::TrailingZeroInput};
|
||||
use frame_support::traits::Get;
|
||||
use sp_std::{convert::TryInto, prelude::*};
|
||||
|
||||
@@ -106,16 +106,24 @@ pub(crate) fn set_check_offchain_execution_status<T: Trait>(
|
||||
/// compacts and reduces the solution, computes the score and submits it back to the chain as an
|
||||
/// unsigned transaction, without any signature.
|
||||
pub(crate) fn compute_offchain_election<T: Trait>() -> Result<(), OffchainElectionError> {
|
||||
let iters = get_balancing_iters::<T>();
|
||||
// compute raw solution. Note that we use `OffchainAccuracy`.
|
||||
let ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>()
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>(iters)
|
||||
.ok_or(OffchainElectionError::ElectionFailed)?;
|
||||
|
||||
// process and prepare it for submission.
|
||||
let (winners, compact, score, size) = prepare_submission::<T>(assignments, winners, true)?;
|
||||
|
||||
crate::log!(
|
||||
info,
|
||||
"prepared a seq-phragmen solution with {} balancing iterations and score {:?}",
|
||||
iters,
|
||||
score,
|
||||
);
|
||||
|
||||
// defensive-only: current era can never be none except genesis.
|
||||
let current_era = <Module<T>>::current_era().unwrap_or_default();
|
||||
|
||||
@@ -132,6 +140,20 @@ pub(crate) fn compute_offchain_election<T: Trait>() -> Result<(), OffchainElecti
|
||||
.map_err(|_| OffchainElectionError::PoolSubmissionFailed)
|
||||
}
|
||||
|
||||
/// Get a random number of iterations to run the balancing.
|
||||
///
|
||||
/// Uses the offchain seed to generate a random number.
|
||||
pub fn get_balancing_iters<T: Trait>() -> usize {
|
||||
match T::MaxIterations::get() {
|
||||
0 => 0,
|
||||
max @ _ => {
|
||||
let seed = sp_io::offchain::random_seed();
|
||||
let random = <u32>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed") % max.saturating_add(1);
|
||||
random as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes an election result and spits out some data that can be submitted to the chain.
|
||||
///
|
||||
@@ -177,26 +199,6 @@ pub fn prepare_submission<T: Trait>(
|
||||
<Module<T>>::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
let (mut support_map, _) = build_support_map::<T::AccountId>(&winners, &staked);
|
||||
// balance a random number of times.
|
||||
let iterations_executed = match T::MaxIterations::get() {
|
||||
0 => {
|
||||
// Don't run balance_solution at all
|
||||
0
|
||||
}
|
||||
iterations @ _ => {
|
||||
let seed = sp_io::offchain::random_seed();
|
||||
let iterations = <u32>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed") % iterations.saturating_add(1);
|
||||
balance_solution(
|
||||
&mut staked,
|
||||
&mut support_map,
|
||||
Zero::zero(),
|
||||
iterations as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// reduce
|
||||
if do_reduce {
|
||||
reduce(&mut staked);
|
||||
@@ -220,7 +222,8 @@ pub fn prepare_submission<T: Trait>(
|
||||
<Module<T>>::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
let (support_map, _) = build_support_map::<T::AccountId>(&winners, &staked);
|
||||
let support_map = build_support_map::<T::AccountId>(&winners, &staked)
|
||||
.map_err(|_| OffchainElectionError::ElectionFailed)?;
|
||||
evaluate_support::<T::AccountId>(&support_map)
|
||||
};
|
||||
|
||||
@@ -250,12 +253,5 @@ pub fn prepare_submission<T: Trait>(
|
||||
nominators: snapshot_nominators.len() as NominatorIndex,
|
||||
};
|
||||
|
||||
crate::log!(
|
||||
info,
|
||||
"prepared solution after {} equalization iterations with score {:?}",
|
||||
iterations_executed,
|
||||
score,
|
||||
);
|
||||
|
||||
Ok((winners_indexed, compact, score, size))
|
||||
}
|
||||
|
||||
@@ -237,8 +237,10 @@ pub fn get_weak_solution<T: Trait>(
|
||||
stake_of
|
||||
);
|
||||
|
||||
let (support_map, _) =
|
||||
build_support_map::<T::AccountId>(winners.as_slice(), staked.as_slice());
|
||||
let support_map = build_support_map::<T::AccountId>(
|
||||
winners.as_slice(),
|
||||
staked.as_slice(),
|
||||
).unwrap();
|
||||
evaluate_support::<T::AccountId>(&support_map)
|
||||
};
|
||||
|
||||
@@ -276,10 +278,12 @@ pub fn get_weak_solution<T: Trait>(
|
||||
pub fn get_seq_phragmen_solution<T: Trait>(
|
||||
do_reduce: bool,
|
||||
) -> (Vec<ValidatorIndex>, CompactAssignments, ElectionScore, ElectionSize) {
|
||||
let iters = offchain_election::get_balancing_iters::<T>();
|
||||
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>().unwrap();
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>(iters).unwrap();
|
||||
|
||||
offchain_election::prepare_submission::<T>(assignments, winners, do_reduce).unwrap()
|
||||
}
|
||||
|
||||
@@ -1749,11 +1749,10 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() {
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), vec![21, 31]));
|
||||
|
||||
// winners should be 21 and 31. Otherwise this election is taking duplicates into account.
|
||||
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = Staking::do_phragmen::<Perbill>().unwrap();
|
||||
} = Staking::do_phragmen::<Perbill>(0).unwrap();
|
||||
let winners = sp_npos_elections::to_without_backing(winners);
|
||||
|
||||
assert_eq!(winners, vec![31, 21]);
|
||||
@@ -1801,7 +1800,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election_elected() {
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = Staking::do_phragmen::<Perbill>().unwrap();
|
||||
} = Staking::do_phragmen::<Perbill>(0).unwrap();
|
||||
|
||||
let winners = sp_npos_elections::to_without_backing(winners);
|
||||
assert_eq!(winners, vec![21, 11]);
|
||||
@@ -3157,7 +3156,7 @@ mod offchain_election {
|
||||
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
|
||||
assert!(Staking::snapshot_validators().is_some());
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
assert_ok!(submit_solution(
|
||||
Origin::signed(10),
|
||||
winners,
|
||||
@@ -3214,7 +3213,7 @@ mod offchain_election {
|
||||
run_to_block(14);
|
||||
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
assert_ok!(submit_solution(Origin::signed(10), winners, compact, score));
|
||||
|
||||
let queued_result = Staking::queued_elected().unwrap();
|
||||
@@ -3255,7 +3254,7 @@ mod offchain_election {
|
||||
|
||||
// create all the indices just to build the solution.
|
||||
Staking::create_stakers_snapshot();
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
Staking::kill_stakers_snapshot();
|
||||
|
||||
assert_err_with_weight!(
|
||||
@@ -3286,7 +3285,7 @@ mod offchain_election {
|
||||
run_to_block(12);
|
||||
|
||||
// a good solution
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
assert_ok!(submit_solution(
|
||||
Origin::signed(10),
|
||||
winners,
|
||||
@@ -3331,7 +3330,7 @@ mod offchain_election {
|
||||
));
|
||||
|
||||
// a better solution
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
assert_ok!(submit_solution(
|
||||
Origin::signed(10),
|
||||
winners,
|
||||
@@ -3436,7 +3435,7 @@ mod offchain_election {
|
||||
ext.execute_with(|| {
|
||||
run_to_block(12);
|
||||
// put a good solution on-chain
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
assert_ok!(submit_solution(
|
||||
Origin::signed(10),
|
||||
winners,
|
||||
@@ -3481,7 +3480,7 @@ mod offchain_election {
|
||||
run_to_block(12);
|
||||
|
||||
ValidatorCount::put(3);
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
ValidatorCount::put(4);
|
||||
|
||||
assert_eq!(winners.len(), 3);
|
||||
@@ -3506,7 +3505,7 @@ mod offchain_election {
|
||||
.execute_with(|| {
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
|
||||
assert_noop!(
|
||||
Staking::submit_election_solution(
|
||||
@@ -3535,7 +3534,7 @@ mod offchain_election {
|
||||
run_to_block(12);
|
||||
|
||||
ValidatorCount::put(3);
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
ValidatorCount::put(4);
|
||||
|
||||
assert_eq!(winners.len(), 3);
|
||||
@@ -3564,7 +3563,7 @@ mod offchain_election {
|
||||
build_offchain_election_test_ext();
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
|
||||
assert_eq!(winners.len(), 4);
|
||||
|
||||
@@ -3592,7 +3591,7 @@ mod offchain_election {
|
||||
|
||||
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
|
||||
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
|
||||
let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (mut compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
|
||||
// index 9 doesn't exist.
|
||||
compact.votes1.push((9, 2));
|
||||
@@ -3624,7 +3623,7 @@ mod offchain_election {
|
||||
|
||||
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
|
||||
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
|
||||
let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (mut compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
|
||||
// index 4 doesn't exist.
|
||||
compact.votes1.iter_mut().for_each(|(_, vidx)| if *vidx == 1 { *vidx = 4 });
|
||||
@@ -3656,7 +3655,7 @@ mod offchain_election {
|
||||
|
||||
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
|
||||
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
|
||||
let (compact, _, score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, _, score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
|
||||
// index 4 doesn't exist.
|
||||
let winners = vec![0, 1, 2, 4];
|
||||
@@ -3688,7 +3687,7 @@ mod offchain_election {
|
||||
|
||||
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
|
||||
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(false, true, 2, |a| {
|
||||
// swap all 11 and 41s in the distribution with non-winners. Note that it is
|
||||
// important that the count of winners and the count of unique targets remain
|
||||
// valid.
|
||||
@@ -3729,7 +3728,7 @@ mod offchain_election {
|
||||
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
|
||||
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(false, true, 2, |a| {
|
||||
a.iter_mut()
|
||||
.find(|x| x.who == 5)
|
||||
// just add any new target.
|
||||
@@ -3765,7 +3764,7 @@ mod offchain_election {
|
||||
build_offchain_election_test_ext();
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |a| {
|
||||
// mutate a self vote to target someone else. That someone else is still among the
|
||||
// winners
|
||||
a.iter_mut().find(|x| x.who == 11).map(|x| {
|
||||
@@ -3800,7 +3799,7 @@ mod offchain_election {
|
||||
build_offchain_election_test_ext();
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(true, true, 2, |a| {
|
||||
// Remove the self vote.
|
||||
a.retain(|x| x.who != 11);
|
||||
// add is as a new double vote
|
||||
@@ -3837,7 +3836,7 @@ mod offchain_election {
|
||||
|
||||
// Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you
|
||||
// reduce.
|
||||
let (mut compact, winners, score) = prepare_submission_with(false, 0, |_| {});
|
||||
let (mut compact, winners, score) = prepare_submission_with(true, false, 0, |_| {});
|
||||
|
||||
if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) {
|
||||
// by default it should have been (0, [(2, 33%), (1, 33%)], 0)
|
||||
@@ -3878,7 +3877,7 @@ mod offchain_election {
|
||||
build_offchain_election_test_ext();
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| {
|
||||
// 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still
|
||||
// correctly 100.
|
||||
a.iter_mut()
|
||||
@@ -3939,7 +3938,7 @@ mod offchain_election {
|
||||
run_to_block(32);
|
||||
|
||||
// a solution that has been prepared after the slash.
|
||||
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| {
|
||||
// no one is allowed to vote for 10, except for itself.
|
||||
a.into_iter()
|
||||
.filter(|s| s.who != 11)
|
||||
@@ -3957,7 +3956,7 @@ mod offchain_election {
|
||||
));
|
||||
|
||||
// a wrong solution.
|
||||
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
|
||||
let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| {
|
||||
// add back the vote that has been filtered out.
|
||||
a.push(StakedAssignment {
|
||||
who: 1,
|
||||
@@ -3990,7 +3989,7 @@ mod offchain_election {
|
||||
build_offchain_election_test_ext();
|
||||
run_to_block(12);
|
||||
|
||||
let (compact, winners, mut score) = prepare_submission_with(true, 2, |_| {});
|
||||
let (compact, winners, mut score) = prepare_submission_with(true, true, 2, |_| {});
|
||||
score[0] += 1;
|
||||
|
||||
assert_noop!(
|
||||
|
||||
Reference in New Issue
Block a user