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:
Kian Paimani
2020-09-23 10:16:10 +02:00
committed by GitHub
parent ecdc94420e
commit 313f86ec23
32 changed files with 2074 additions and 914 deletions
+62 -31
View File
@@ -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);
+20 -27
View File
@@ -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))
}
+7 -3
View File
@@ -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()
}
+24 -25
View File
@@ -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!(