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
@@ -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))
}