Updates to elections-phragmen and some runtime docs. (#3940)

* minor changes

* Refactors for phragmen-election

* Bump.

* Fix genesis stuff

* Fix rest of the errors
This commit is contained in:
Kian Paimani
2019-10-28 15:29:53 +01:00
committed by Gavin Wood
parent fbe9cfad46
commit 9b1dd268bf
9 changed files with 300 additions and 196 deletions
+192 -121
View File
@@ -86,6 +86,7 @@ use srml_support::{
ChangeMembers, OnUnbalanced, WithdrawReason
}
};
use phragmen::ExtendedBalance;
use system::{self, ensure_signed, ensure_root};
const MODULE_ID: LockIdentifier = *b"phrelect";
@@ -127,25 +128,26 @@ pub trait Trait: system::Trait {
/// Handler for the unbalanced reduction when a member has been kicked.
type KickedMember: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Number of members to elect.
type DesiredMembers: Get<u32>;
/// Number of runners_up to keep.
type DesiredRunnersUp: Get<u32>;
/// How long each seat is kept. This defines the next block number at which an election
/// round will happen. If set to zero, no elections are ever triggered and the module will
/// be in passive mode.
type TermDuration: Get<Self::BlockNumber>;
}
decl_storage! {
trait Store for Module<T: Trait> as PhragmenElection {
// ---- parameters
/// Number of members to elect.
pub DesiredMembers get(fn desired_members) config(): u32;
/// Number of runners_up to keep.
pub DesiredRunnersUp get(fn desired_runners_up) config(): u32;
/// How long each seat is kept. This defines the next block number at which an election
/// round will happen. If set to zero, no elections are ever triggered and the module will
/// be in passive mode. In that case only a member set defined in at genesis can exist.
pub TermDuration get(fn term_duration) config(): T::BlockNumber;
// ---- State
/// The current elected membership. Sorted based on account id.
pub Members get(fn members) config(): Vec<T::AccountId>;
pub Members get(fn members): Vec<(T::AccountId, BalanceOf<T>)>;
/// The current runners_up. Sorted based on low to high merit (worse to best runner).
pub RunnersUp get(fn runners_up): Vec<T::AccountId>;
pub RunnersUp get(fn runners_up): Vec<(T::AccountId, BalanceOf<T>)>;
/// The total number of vote rounds that have happened, excluding the upcoming one.
pub ElectionRounds get(fn election_rounds): u32 = Zero::zero();
@@ -166,6 +168,9 @@ decl_module! {
const CandidacyBond: BalanceOf<T> = T::CandidacyBond::get();
const VotingBond: BalanceOf<T> = T::VotingBond::get();
const DesiredMembers: u32 = T::DesiredMembers::get();
const DesiredRunnersUp: u32 = T::DesiredRunnersUp::get();
const TermDuration: T::BlockNumber = T::TermDuration::get();
/// Vote for a set of candidates for the upcoming round of election.
///
@@ -194,8 +199,8 @@ decl_module! {
ensure!(!allowed_votes.is_zero(), "cannot vote when no candidates or members exist");
ensure!(votes.len() <= allowed_votes, "cannot vote more than candidates");
ensure!(votes.len() <= MAXIMUM_VOTE, "cannot vote more than maximum allowed");
ensure!(!votes.is_empty(), "must vote for at least one candidate.");
ensure!(
value > T::Currency::minimum_balance(),
"cannot vote with stake less than minimum balance"
@@ -310,19 +315,6 @@ decl_module! {
<Candidates<T>>::mutate(|c| c.insert(index, who));
}
/// Set the desired member count. Changes will be effective at the beginning of next round.
///
/// # <weight>
/// #### State
/// Reads: O(1)
/// Writes: O(1)
/// # </weight>
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
fn set_desired_member_count(origin, #[compact] count: u32) {
ensure_root(origin)?;
DesiredMembers::put(count);
}
/// Remove a particular member from the set. This is effective immediately.
///
/// If a runner-up is available, then the best runner-up will be removed and replaces the
@@ -340,49 +332,45 @@ decl_module! {
ensure_root(origin)?;
let who = T::Lookup::lookup(who)?;
let mut members = Self::members();
if let Ok(index) = members.binary_search(&who) {
let mut members_with_stake = Self::members();
if let Ok(index) = members_with_stake.binary_search_by(|(ref m, ref _s)| m.cmp(&who)) {
// remove, slash, emit event.
members.remove(index);
members_with_stake.remove(index);
let (imbalance, _) = T::Currency::slash_reserved(&who, T::CandidacyBond::get());
T::KickedMember::on_unbalanced(imbalance);
Self::deposit_event(RawEvent::MemberKicked(who.clone()));
let mut runners_up = Self::runners_up();
if let Some(replacement) = runners_up.pop() {
if let Some((replacement, stake)) = runners_up.pop() {
// replace the outgoing with the best runner up.
if let Err(index) = members.binary_search(&replacement) {
members.insert(index, replacement.clone());
if let Err(index) = members_with_stake
.binary_search_by(|(ref m, ref _s)| m.cmp(&replacement))
{
members_with_stake.insert(index, (replacement.clone(), stake));
ElectionRounds::mutate(|v| *v += 1);
T::ChangeMembers::change_members_sorted(&[replacement], &[who], &members);
T::ChangeMembers::change_members_sorted(
&[replacement],
&[who],
&members_with_stake
.iter()
.map(|(m, _)| m.clone())
.collect::<Vec<T::AccountId>>(),
);
}
// else it would mean that the runner up was already a member. This cannot
// happen. If it does, not much that we can do about it.
<Members<T>>::put(members);
<Members<T>>::put(members_with_stake);
<RunnersUp<T>>::put(runners_up);
} else {
// update `Members` storage -- `do_phragmen` adds this to the candidate list.
<Members<T>>::put(members);
<Members<T>>::put(members_with_stake);
// trigger a new phragmen. grab a cup of coffee. This might take a while.
Self::do_phragmen();
}
}
}
/// Set the duration of each term. This will affect the next election's block number.
///
/// # <weight>
/// #### State
/// Reads: O(1)
/// Writes: O(1)
/// # </weight>
#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
fn set_term_duration(origin, #[compact] count: T::BlockNumber) {
ensure_root(origin)?;
<TermDuration<T>>::put(count);
}
/// What to do at the end of each block. Checks if an election needs to happen or not.
fn on_initialize(n: T::BlockNumber) {
if let Err(e) = Self::end_block(n) {
@@ -394,10 +382,13 @@ decl_module! {
}
decl_event!(
pub enum Event<T> where <T as system::Trait>::AccountId {
pub enum Event<T> where
Balance = BalanceOf<T>,
<T as system::Trait>::AccountId,
{
/// A new term with new members. This indicates that enough candidates existed, not that
/// enough have has been elected. The inner value must be examined for this purpose.
NewTerm(Vec<AccountId>),
NewTerm(Vec<(AccountId, Balance)>),
/// No (or not enough) candidates existed for this round.
EmptyTerm,
/// A member has been removed. This should always be followed by either `NewTerm` ot
@@ -429,7 +420,32 @@ impl<T: Trait> Module<T> {
///
/// Limited number of members. Binary search. Constant time factor. O(1)
fn is_member(who: &T::AccountId) -> bool {
Self::members().binary_search(who).is_ok()
Self::members_ids().binary_search(who).is_ok()
}
/// Returns number of desired members.
fn desired_members() -> u32 {
T::DesiredMembers::get()
}
/// Returns number of desired runners up.
fn desired_runners_up() -> u32 {
T::DesiredRunnersUp::get()
}
/// Returns the term duration
fn term_duration() -> T::BlockNumber {
T::TermDuration::get()
}
/// Get the members' account ids.
fn members_ids() -> Vec<T::AccountId> {
Self::members().into_iter().map(|(m, _)| m).collect::<Vec<T::AccountId>>()
}
/// The the runners' up account ids.
fn runners_up_ids() -> Vec<T::AccountId> {
Self::runners_up().into_iter().map(|(r, _)| r).collect::<Vec<T::AccountId>>()
}
/// Check if `who` is a defunct voter.
@@ -500,11 +516,11 @@ impl<T: Trait> Module<T> {
let mut exposed_candidates = candidates.clone();
// current members are always a candidate for the next round as well.
// this is guaranteed to not create any duplicates.
candidates.append(&mut Self::members());
candidates.append(&mut Self::members_ids());
// previous runners_up are also always candidates for the next round.
candidates.append(&mut Self::runners_up());
candidates.append(&mut Self::runners_up_ids());
// and exposed to being an outgoing in case they are no longer elected.
exposed_candidates.append(&mut Self::runners_up());
exposed_candidates.append(&mut Self::runners_up_ids());
let voters_and_votes = <VotesOf<T>>::enumerate()
.map(|(v, i)| (v, i))
@@ -532,14 +548,35 @@ impl<T: Trait> Module<T> {
.filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } )
.collect::<Vec<T::AccountId>>();
let support_map = phragmen::build_support_map::<_, _, _, T::CurrencyToVote>(
&new_set,
&phragmen_result.assignments,
Self::locked_stake_of,
false,
);
let to_balance = |e: ExtendedBalance|
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e);
let new_set_with_stake = new_set
.into_iter()
.map(|ref m| {
let support = support_map.get(m)
.expect(
"entire new_set was given to build_support_map; en entry must be \
created for each item; qed"
);
(m.clone(), to_balance(support.total))
})
.collect::<Vec<(T::AccountId, BalanceOf<T>)>>();
// split new set into winners and runner ups.
let split_point = desired_seats.min(new_set.len());
let mut new_members = (&new_set[..split_point]).to_vec();
let runners_up = &new_set[split_point..]
let split_point = desired_seats.min(new_set_with_stake.len());
let mut new_members = (&new_set_with_stake[..split_point]).to_vec();
let runners_up = &new_set_with_stake[split_point..]
.into_iter()
.cloned()
.rev()
.collect::<Vec<T::AccountId>>();
.collect::<Vec<(T::AccountId, BalanceOf<T>)>>();
// sort and save the members.
new_members.sort();
@@ -550,13 +587,13 @@ impl<T: Trait> Module<T> {
// report member changes. We compute diff because we need the outgoing list.
let (incoming, outgoing) = T::ChangeMembers::compute_members_diff(
&new_members,
&old_members
&Self::members_ids(),
&old_members.into_iter().map(|(m, _)| m).collect::<Vec<T::AccountId>>(),
);
T::ChangeMembers::change_members_sorted(
&incoming,
&outgoing.clone(),
&new_members
&Self::members_ids(),
);
// unlike exposed_candidates, these are members who were in the list and no longer
@@ -567,7 +604,8 @@ impl<T: Trait> Module<T> {
// runner up list is not sorted. O(K*N) given K runner ups. Overall: O(NLogM + N*K)
exposed_candidates.into_iter().for_each(|c| {
// any candidate who is not a member and not a runner up.
if new_members.binary_search(&c).is_err() && !runners_up.contains(&c)
if new_members.binary_search_by_key(&c, |(m, _)| m.clone()).is_err()
&& !Self::runners_up_ids().contains(&c)
{
let (imbalance, _) = T::Currency::slash_reserved(&c, T::CandidacyBond::get());
T::LoserCandidate::on_unbalanced(imbalance);
@@ -651,7 +689,9 @@ mod tests {
thread_local! {
static VOTING_BOND: RefCell<u64> = RefCell::new(2);
static MEMBERS: RefCell<Vec<u64>> = RefCell::new(vec![]);
static DESIRED_MEMBERS: RefCell<u32> = RefCell::new(2);
static DESIRED_RUNNERS_UP: RefCell<u32> = RefCell::new(2);
static TERM_DURATION: RefCell<u64> = RefCell::new(5);
}
pub struct VotingBond;
@@ -659,6 +699,21 @@ mod tests {
fn get() -> u64 { VOTING_BOND.with(|v| *v.borrow()) }
}
pub struct DesiredMembers;
impl Get<u32> for DesiredMembers {
fn get() -> u32 { DESIRED_MEMBERS.with(|v| *v.borrow()) }
}
pub struct DesiredRunnersUp;
impl Get<u32> for DesiredRunnersUp {
fn get() -> u32 { DESIRED_RUNNERS_UP.with(|v| *v.borrow()) }
}
pub struct TermDuration;
impl Get<u64> for TermDuration {
fn get() -> u64 { TERM_DURATION.with(|v| *v.borrow()) }
}
pub struct TestChangeMembers;
impl ChangeMembers<u64> for TestChangeMembers {
fn change_members_sorted(_: &[u64], _: &[u64], _: &[u64]) {}
@@ -682,6 +737,9 @@ mod tests {
type ChangeMembers = TestChangeMembers;
type CandidacyBond = CandidacyBond;
type VotingBond = VotingBond;
type TermDuration = TermDuration;
type DesiredMembers = DesiredMembers;
type DesiredRunnersUp = DesiredRunnersUp;
type LoserCandidate = ();
type KickedMember = ();
type BadReport = ();
@@ -698,7 +756,7 @@ mod tests {
{
System: system::{Module, Call, Event},
Balances: balances::{Module, Call, Event<T>, Config<T>},
Elections: elections::{Module, Call, Event<T>, Config<T>},
Elections: elections::{Module, Call, Event<T>},
}
);
@@ -707,7 +765,6 @@ mod tests {
voter_bond: u64,
term_duration: u64,
desired_runners_up: u32,
members: Vec<u64>,
}
impl Default for ExtBuilder {
@@ -717,7 +774,6 @@ mod tests {
voter_bond: 2,
desired_runners_up: 0,
term_duration: 5,
members: vec![],
}
}
}
@@ -735,12 +791,10 @@ mod tests {
self.term_duration = duration;
self
}
pub fn members(mut self, members: Vec<u64>) -> Self {
self.members = members;
self
}
pub fn build(self) -> runtime_io::TestExternalities {
VOTING_BOND.with(|v| *v.borrow_mut() = self.voter_bond);
TERM_DURATION.with(|v| *v.borrow_mut() = self.term_duration);
DESIRED_RUNNERS_UP.with(|v| *v.borrow_mut() = self.desired_runners_up);
GenesisConfig {
balances: Some(balances::GenesisConfig::<Test>{
balances: vec![
@@ -753,12 +807,6 @@ mod tests {
],
vesting: vec![],
}),
elections: Some(elections::GenesisConfig::<Test>{
members: self.members,
desired_members: 2,
desired_runners_up: self.desired_runners_up,
term_duration: self.term_duration,
}),
}.build_storage().unwrap().into()
}
}
@@ -798,10 +846,9 @@ mod tests {
}
#[test]
fn passive_module_should_work() {
fn term_duration_zero_is_passive() {
ExtBuilder::default()
.term_duration(0)
.members(vec![1, 2, 3])
.build()
.execute_with(||
{
@@ -810,17 +857,16 @@ mod tests {
assert_eq!(Elections::desired_members(), 2);
assert_eq!(Elections::election_rounds(), 0);
assert_eq!(Elections::members(), vec![1, 2, 3]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(Elections::runners_up(), vec![]);
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(all_voters(), vec![]);
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![1, 2, 3]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(Elections::runners_up(), vec![]);
assert_eq!(Elections::candidates(), vec![]);
});
}
@@ -863,7 +909,7 @@ mod tests {
assert!(Elections::is_candidate(&2).is_ok());
assert_eq!(Elections::candidates(), vec![1, 2]);
assert_eq!(Elections::members(), vec![]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(Elections::runners_up(), vec![]);
System::set_block_number(5);
@@ -873,7 +919,7 @@ mod tests {
assert!(Elections::is_candidate(&2).is_err());
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(Elections::members(), vec![]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(Elections::runners_up(), vec![]);
});
}
@@ -901,7 +947,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![5]);
assert_eq!(Elections::members_ids(), vec![5]);
assert_eq!(Elections::runners_up(), vec![]);
assert_eq!(Elections::candidates(), vec![]);
@@ -994,7 +1040,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::candidates(), vec![]);
assert_ok!(Elections::vote(Origin::signed(3), vec![4, 5], 10));
@@ -1016,7 +1062,7 @@ mod tests {
#[test]
fn cannot_vote_for_less_than_ed() {
ExtBuilder::default().voter_bond(8).build().execute_with(|| {
ExtBuilder::default().build().execute_with(|| {
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
@@ -1101,7 +1147,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![3, 5]);
assert_eq!(Elections::members_ids(), vec![3, 5]);
});
}
@@ -1130,7 +1176,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::candidates(), vec![]);
// all of them have a member that they voted for.
@@ -1165,7 +1211,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(balances(&3), (28, 2));
@@ -1194,7 +1240,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(balances(&4), (35, 5));
@@ -1237,7 +1283,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![3, 5]);
assert_eq!(Elections::members(), vec![(3, 30), (5, 20)]);
assert_eq!(Elections::runners_up(), vec![]);
assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
assert_eq!(Elections::candidates(), vec![]);
@@ -1259,7 +1305,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![5]);
assert_eq!(Elections::members(), vec![(5, 50)]);
assert_eq!(Elections::election_rounds(), 1);
// but now it has a valid target.
@@ -1269,7 +1315,7 @@ mod tests {
assert_ok!(Elections::end_block(System::block_number()));
// candidate 4 is affected by an old vote.
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members(), vec![(4, 30), (5, 50)]);
assert_eq!(Elections::election_rounds(), 2);
assert_eq_uvec!(all_voters(), vec![3, 5]);
});
@@ -1292,7 +1338,7 @@ mod tests {
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::election_rounds(), 1);
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
});
}
@@ -1307,7 +1353,7 @@ mod tests {
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(Elections::election_rounds(), 1);
assert_eq!(Elections::members(), vec![]);
assert_eq!(Elections::members_ids(), vec![]);
});
}
@@ -1327,9 +1373,9 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
// sorted based on account id.
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
// sorted based on merit (least -> most)
assert_eq!(Elections::runners_up(), vec![3, 2]);
assert_eq!(Elections::runners_up_ids(), vec![3, 2]);
// runner ups are still locked.
assert_eq!(balances(&4), (35, 5));
@@ -1353,15 +1399,15 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::runners_up(), vec![2, 3]);
assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]);
assert_eq!(Elections::runners_up(), vec![(2, 20), (3, 30)]);
assert_ok!(Elections::vote(Origin::signed(5), vec![5], 15));
System::set_block_number(10);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![3, 4]);
assert_eq!(Elections::runners_up(), vec![5, 2]);
assert_eq!(Elections::members(), vec![(3, 30), (4, 40)]);
assert_eq!(Elections::runners_up(), vec![(5, 15), (2, 20)]);
});
}
@@ -1378,9 +1424,9 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::runners_up(), vec![2]);
assert_eq!(Elections::runners_up_ids(), vec![2]);
assert_eq!(balances(&2), (15, 5));
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
@@ -1388,9 +1434,9 @@ mod tests {
System::set_block_number(10);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::runners_up(), vec![3]);
assert_eq!(Elections::runners_up_ids(), vec![3]);
assert_eq!(balances(&3), (25, 5));
assert_eq!(balances(&2), (15, 2));
});
@@ -1408,7 +1454,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::election_rounds(), 1);
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
@@ -1426,7 +1472,7 @@ mod tests {
assert_ok!(Elections::end_block(System::block_number()));
// 4 removed; 5 and 3 are the new best.
assert_eq!(Elections::members(), vec![3, 5]);
assert_eq!(Elections::members_ids(), vec![3, 5]);
});
}
@@ -1449,8 +1495,8 @@ mod tests {
System::set_block_number(b.into());
assert_ok!(Elections::end_block(System::block_number()));
// we keep re-electing the same folks.
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::runners_up(), vec![2, 3]);
assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]);
assert_eq!(Elections::runners_up(), vec![(2, 20), (3, 30)]);
// no new candidates but old members and runners-up are always added.
assert_eq!(Elections::candidates(), vec![]);
assert_eq!(Elections::election_rounds(), b / 5);
@@ -1476,7 +1522,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_eq!(Elections::election_rounds(), 1);
// a new candidate
@@ -1487,7 +1533,7 @@ mod tests {
assert_eq!(balances(&4), (35, 2)); // slashed
assert_eq!(Elections::election_rounds(), 2); // new election round
assert_eq!(Elections::members(), vec![3, 5]); // new members
assert_eq!(Elections::members_ids(), vec![3, 5]); // new members
});
}
@@ -1509,7 +1555,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![3, 5]);
assert_eq!(Elections::members_ids(), vec![3, 5]);
assert_eq!(Elections::election_rounds(), 1);
assert_ok!(Elections::remove_voter(Origin::signed(2)));
@@ -1520,7 +1566,7 @@ mod tests {
// meanwhile, no one cares to become a candidate again.
System::set_block_number(10);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(Elections::election_rounds(), 2);
});
}
@@ -1538,14 +1584,14 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![5]);
assert_eq!(Elections::members_ids(), vec![5]);
assert_ok!(Elections::remove_voter(Origin::signed(5)));
assert_eq!(balances(&5), (47, 3));
System::set_block_number(10);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![]);
assert_eq!(Elections::members_ids(), vec![]);
assert_eq!(balances(&5), (50, 0));
});
@@ -1565,7 +1611,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![5]);
assert_eq!(Elections::members_ids(), vec![5]);
// winner
assert_eq!(balances(&5), (47, 3));
@@ -1585,7 +1631,7 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq!(Elections::members(), vec![4, 5]);
assert_eq!(Elections::members_ids(), vec![4, 5]);
assert_ok!(Elections::submit_candidacy(Origin::signed(1)));
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
@@ -1605,7 +1651,7 @@ mod tests {
assert_ok!(Elections::end_block(System::block_number()));
// 3, 4 are new members, must still be bonded, nothing slashed.
assert_eq!(Elections::members(), vec![3, 4]);
assert_eq!(Elections::members(), vec![(3, 30), (4, 48)]);
assert_eq!(balances(&3), (25, 5));
assert_eq!(balances(&4), (35, 5));
@@ -1615,7 +1661,10 @@ mod tests {
// 5 is an outgoing loser, it will get their bond back.
assert_eq!(balances(&5), (48, 2));
assert_eq!(System::events()[0].event, Event::elections(RawEvent::NewTerm(vec![4, 5])));
assert_eq!(
System::events()[0].event,
Event::elections(RawEvent::NewTerm(vec![(4, 40), (5, 50)])),
);
})
}
@@ -1632,8 +1681,30 @@ mod tests {
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
assert_eq_uvec!(Elections::members(), vec![3, 4]);
assert_eq_uvec!(Elections::members_ids(), vec![3, 4]);
assert_eq!(Elections::election_rounds(), 1);
});
}
#[test]
fn members_are_sorted_based_on_id_runners_on_merit() {
ExtBuilder::default().desired_runners_up(2).build().execute_with(|| {
assert_ok!(Elections::submit_candidacy(Origin::signed(5)));
assert_ok!(Elections::submit_candidacy(Origin::signed(4)));
assert_ok!(Elections::submit_candidacy(Origin::signed(3)));
assert_ok!(Elections::submit_candidacy(Origin::signed(2)));
assert_ok!(Elections::vote(Origin::signed(2), vec![3], 20));
assert_ok!(Elections::vote(Origin::signed(3), vec![2], 30));
assert_ok!(Elections::vote(Origin::signed(4), vec![5], 40));
assert_ok!(Elections::vote(Origin::signed(5), vec![4], 50));
System::set_block_number(5);
assert_ok!(Elections::end_block(System::block_number()));
// id: low -> high.
assert_eq!(Elections::members(), vec![(4, 50), (5, 40)]);
// merit: low -> high.
assert_eq!(Elections::runners_up(), vec![(3, 20), (2, 30)]);
});
}
}