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
@@ -17,6 +17,14 @@
//! Common fuzzing utils.
// Each function will be used based on which fuzzer binary is being used.
#![allow(dead_code)]
use sp_npos_elections::{ElectionResult, VoteWeight, phragmms, seq_phragmen};
use sp_std::collections::btree_map::BTreeMap;
use sp_runtime::Perbill;
use rand::{self, Rng, RngCore};
/// converts x into the range [a, b] in a pseudo-fair way.
pub fn to_range(x: usize, a: usize, b: usize) -> usize {
// does not work correctly if b < 2 * a
@@ -28,3 +36,78 @@ pub fn to_range(x: usize, a: usize, b: usize) -> usize {
collapsed + a
}
}
pub enum ElectionType {
Phragmen(Option<(usize, u128)>),
Phragmms(Option<(usize, u128)>)
}
pub type AccountId = u64;
pub fn generate_random_npos_result(
voter_count: u64,
target_count: u64,
to_elect: usize,
mut rng: impl RngCore,
election_type: ElectionType,
) -> (
ElectionResult<AccountId, Perbill>,
Vec<AccountId>,
Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
BTreeMap<AccountId, VoteWeight>,
) {
let prefix = 100_000;
// Note, it is important that stakes are always bigger than ed.
let base_stake: u64 = 1_000_000_000_000;
let ed: u64 = base_stake;
let mut candidates = Vec::with_capacity(target_count as usize);
let mut stake_of: BTreeMap<AccountId, VoteWeight> = BTreeMap::new();
(1..=target_count).for_each(|acc| {
candidates.push(acc);
let stake_var = rng.gen_range(ed, 100 * ed);
stake_of.insert(acc, base_stake + stake_var);
});
let mut voters = Vec::with_capacity(voter_count as usize);
(prefix ..= (prefix + voter_count)).for_each(|acc| {
let edge_per_this_voter = rng.gen_range(1, candidates.len());
// all possible targets
let mut all_targets = candidates.clone();
// we remove and pop into `targets` `edge_per_this_voter` times.
let targets = (0..edge_per_this_voter).map(|_| {
let upper = all_targets.len() - 1;
let idx = rng.gen_range(0, upper);
all_targets.remove(idx)
})
.collect::<Vec<AccountId>>();
let stake_var = rng.gen_range(ed, 100 * ed) ;
let stake = base_stake + stake_var;
stake_of.insert(acc, stake);
voters.push((acc, stake, targets));
});
(
match election_type {
ElectionType::Phragmen(conf) =>
seq_phragmen::<AccountId, sp_runtime::Perbill>(
to_elect,
candidates.clone(),
voters.clone(),
conf,
).unwrap(),
ElectionType::Phragmms(conf) =>
phragmms::<AccountId, sp_runtime::Perbill>(
to_elect,
candidates.clone(),
voters.clone(),
conf,
).unwrap(),
},
candidates,
voters,
stake_of,
)
}