Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
399cf4b
Revamp npos-elections and implement phragmms
kianenigma Jul 19, 2020
6a7354c
Update primitives/npos-elections/src/phragmms.rs
kianenigma Jul 20, 2020
2254e21
Master.into()
kianenigma Jul 27, 2020
bd4c4ca
Merge branch 'kiz-impl-phragmms' of github.com:paritytech/substrate i…
kianenigma Jul 27, 2020
2dcfade
Fix build
kianenigma Jul 27, 2020
b29ff31
Some review grumbles
kianenigma Jul 27, 2020
51e98ff
Master.into()
kianenigma Aug 4, 2020
37b57d8
Add some stuff for remote testing
kianenigma Aug 4, 2020
01e5473
fix some of the grumbles.
kianenigma Aug 4, 2020
3ed7024
Add remote testing stuff.
kianenigma Aug 4, 2020
47640bb
Cleanup
kianenigma Aug 6, 2020
1c765c2
Master.into()
kianenigma Aug 11, 2020
dca91e6
fix docs
kianenigma Aug 11, 2020
2fa2a96
Update primitives/arithmetic/src/rational.rs
kianenigma Aug 12, 2020
3892d26
Master.into()
kianenigma Aug 12, 2020
20e3de0
Small config change
kianenigma Aug 12, 2020
c28858d
Better handling of approval_stake == 0
kianenigma Aug 13, 2020
073325d
Final touhces.
kianenigma Aug 13, 2020
decc17f
Clean fuzzer a bit
kianenigma Aug 13, 2020
c675a20
Clean fuzzer a bit
kianenigma Aug 13, 2020
8f5087b
Merge branch 'master' of github.com:paritytech/substrate into kiz-imp…
kianenigma Aug 13, 2020
b27700d
Master.into()
kianenigma Aug 14, 2020
427ee99
Master.into()
kianenigma Aug 21, 2020
9ec941b
Update primitives/npos-elections/src/balancing.rs
kianenigma Aug 24, 2020
b4a91dd
Master.into()
kianenigma Aug 24, 2020
67d2be6
Fix fuzzer.
kianenigma Sep 11, 2020
432db79
Master.into()
kianenigma Sep 11, 2020
8f75cd9
Better api for normalize
kianenigma Sep 14, 2020
4e10ab9
Master.into()
kianenigma Sep 14, 2020
7a3aa8b
Add noramlize_up
kianenigma Sep 15, 2020
379e4c9
A large number of small fixes.
kianenigma Sep 16, 2020
15f452e
Master.into()
kianenigma Sep 22, 2020
4b26106
make it merge ready
kianenigma Sep 22, 2020
629f39a
Fix warns
kianenigma Sep 23, 2020
06edce5
bump
kianenigma Sep 23, 2020
8fd1959
Fix fuzzers a bit.
kianenigma Sep 23, 2020
c14b64a
Fix warns as well.
kianenigma Sep 23, 2020
9f97731
Fix more tests.
kianenigma Sep 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 259,
impl_version: 0,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
};
Expand Down
4 changes: 2 additions & 2 deletions client/consensus/babe/src/authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,10 @@ mod tests {
#[test]
fn claim_secondary_plain_slot_works() {
let keystore = sc_keystore::Store::new_in_memory();
let valid_public_key = dbg!(keystore.write().sr25519_generate_new(
let valid_public_key = keystore.write().sr25519_generate_new(
AuthorityId::ID,
Some(sp_core::crypto::DEV_PHRASE),
).unwrap());
).unwrap();

let authorities = vec![
(AuthorityId::from(Pair::generate().0.public()), 5),
Expand Down
193 changes: 112 additions & 81 deletions frame/elections-phragmen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ use frame_support::{
ContainsLengthBound,
}
};
use sp_npos_elections::{build_support_map, ExtendedBalance, VoteWeight, ElectionResult};
use sp_npos_elections::{ExtendedBalance, VoteWeight, ElectionResult};
use frame_system::{ensure_signed, ensure_root};

mod benchmarking;
Expand Down Expand Up @@ -209,7 +209,7 @@ decl_storage! {
// ---- State
/// The current elected membership. Sorted based on account id.
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).
/// The current runners_up. Sorted based on low to high merit (worse to best).
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();
Expand Down Expand Up @@ -689,7 +689,9 @@ decl_event!(
/// No (or not enough) candidates existed for this round. This is different from
/// `NewTerm(\[\])`. See the description of `NewTerm`.
EmptyTerm,
/// A \[member\] has been removed. This should always be followed by either `NewTerm` ot
/// Internal error happened while trying to perform election.
ElectionError,
/// A \[member\] has been removed. This should always be followed by either `NewTerm` or
/// `EmptyTerm`.
MemberKicked(AccountId),
/// A \[member\] has renounced their candidacy.
Expand Down Expand Up @@ -827,11 +829,6 @@ impl<T: Trait> Module<T> {
}
}

/// The locked stake of a voter.
fn locked_stake_of(who: &T::AccountId) -> BalanceOf<T> {
Voting::<T>::get(who).0
}

/// Check there's nothing to do this block.
///
/// Runs phragmen election and cleans all the previous candidate state. The voter state is NOT
Expand All @@ -846,7 +843,8 @@ impl<T: Trait> Module<T> {
0
}

/// Run the phragmen election with all required side processes and state updates.
/// Run the phragmen election with all required side processes and state updates, if election
/// succeeds. Else, it will emit an `ElectionError` event.
///
/// Calls the appropriate [`ChangeMembers`] function variant internally.
///
Expand All @@ -867,16 +865,18 @@ impl<T: Trait> Module<T> {
// previous runners_up are also always candidates for the next round.
candidates.append(&mut Self::runners_up_ids());

if candidates.len().is_zero() {
Self::deposit_event(RawEvent::EmptyTerm);
return;
}

// helper closures to deal with balance/stake.
let to_votes = |b: BalanceOf<T>| -> VoteWeight {
<T::CurrencyToVote as Convert<BalanceOf<T>, VoteWeight>>::convert(b)
};
let to_balance = |e: ExtendedBalance| -> BalanceOf<T> {
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e)
};
let stake_of = |who: &T::AccountId| -> VoteWeight {
to_votes(Self::locked_stake_of(who))
};

// used for prime election.
let voters_and_stakes = Voting::<T>::iter()
Expand All @@ -887,56 +887,31 @@ impl<T: Trait> Module<T> {
.cloned()
.map(|(voter, stake, votes)| { (voter, to_votes(stake), votes)} )
.collect::<Vec<_>>();
let maybe_phragmen_result = sp_npos_elections::seq_phragmen::<T::AccountId, Perbill>(

let _ = sp_npos_elections::seq_phragmen::<T::AccountId, Perbill>(
num_to_elect,
0,
candidates,
voters_and_votes,
);

if let Some(ElectionResult { winners, assignments }) = maybe_phragmen_result {
voters_and_votes.clone(),
None,
).map(|ElectionResult { winners, assignments: _ }| {
let old_members_ids = <Members<T>>::take().into_iter()
.map(|(m, _)| m)
.collect::<Vec<T::AccountId>>();
let old_runners_up_ids = <RunnersUp<T>>::take().into_iter()
.map(|(r, _)| r)
.collect::<Vec<T::AccountId>>();

// filter out those who had literally no votes at all.
// NOTE: the need to do this is because all candidates, even those who have no
// vote are still considered by phragmen and when good candidates are scarce, then these
// cheap ones might get elected. We might actually want to remove the filter and allow
// zero-voted candidates to also make it to the membership set.
let new_set_with_approval = winners;
let new_set = new_set_with_approval
// filter out those who end up with no backing stake.
let new_set_with_stake = winners
.into_iter()
.filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } )
.collect::<Vec<T::AccountId>>();
.filter_map(|(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) })
.collect::<Vec<(T::AccountId, BalanceOf<T>)>>();

// OPTIMISATION NOTE: we could bail out here if `new_set.len() == 0`. There isn't much
// left to do. Yet, re-arranging the code would require duplicating the slashing of
// exposed candidates, cleaning any previous members, and so on. For now, in favour of
// readability and veracity, we keep it simple.

let staked_assignments = sp_npos_elections::assignment_ratio_to_staked(
assignments,
stake_of,
);

let (support_map, _) = build_support_map::<T::AccountId>(&new_set, &staked_assignments);

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 runners up.
let split_point = desired_seats.min(new_set_with_stake.len());
let mut new_members = (&new_set_with_stake[..split_point]).to_vec();
Expand Down Expand Up @@ -1031,14 +1006,15 @@ impl<T: Trait> Module<T> {
<RunnersUp<T>>::put(new_runners_up);

Self::deposit_event(RawEvent::NewTerm(new_members.clone().to_vec()));
} else {
Self::deposit_event(RawEvent::EmptyTerm);
}

// clean candidates.
<Candidates<T>>::kill();
// clean candidates.
<Candidates<T>>::kill();

ElectionRounds::mutate(|v| *v += 1);
ElectionRounds::mutate(|v| *v += 1);
}).map_err(|e| {
frame_support::debug::error!("elections-phragmen: failed to run election [{:?}].", e);
Self::deposit_event(RawEvent::ElectionError);
});
}
}

Expand Down Expand Up @@ -1366,6 +1342,10 @@ mod tests {
assert_eq!(Elections::candidates(), candidates);
}

fn locked_stake_of(who: &u64) -> u64 {
Voting::<Test>::get(who).0
}

fn ensure_members_has_approval_stake() {
// we filter members that have no approval state. This means that even we have more seats
// than candidates, we will never ever chose a member with no votes.
Expand Down Expand Up @@ -1684,13 +1664,13 @@ mod tests {

assert_eq!(balances(&2), (18, 2));
assert_eq!(has_lock(&2), 20);
assert_eq!(Elections::locked_stake_of(&2), 20);
assert_eq!(locked_stake_of(&2), 20);

// can update; different stake; different lock and reserve.
assert_ok!(vote(Origin::signed(2), vec![5, 4], 15));
assert_eq!(balances(&2), (18, 2));
assert_eq!(has_lock(&2), 15);
assert_eq!(Elections::locked_stake_of(&2), 15);
assert_eq!(locked_stake_of(&2), 15);
});
}

Expand Down Expand Up @@ -1828,7 +1808,7 @@ mod tests {

assert_ok!(vote(Origin::signed(2), vec![4, 5], 30));
// you can lie but won't get away with it.
assert_eq!(Elections::locked_stake_of(&2), 20);
assert_eq!(locked_stake_of(&2), 20);
assert_eq!(has_lock(&2), 20);
});
}
Expand All @@ -1842,16 +1822,16 @@ mod tests {
assert_ok!(vote(Origin::signed(3), vec![5], 30));

assert_eq_uvec!(all_voters(), vec![2, 3]);
assert_eq!(Elections::locked_stake_of(&2), 20);
assert_eq!(Elections::locked_stake_of(&3), 30);
assert_eq!(locked_stake_of(&2), 20);
assert_eq!(locked_stake_of(&3), 30);
assert_eq!(votes_of(&2), vec![5]);
assert_eq!(votes_of(&3), vec![5]);

assert_ok!(Elections::remove_voter(Origin::signed(2)));

assert_eq_uvec!(all_voters(), vec![3]);
assert!(votes_of(&2).is_empty());
assert_eq!(Elections::locked_stake_of(&2), 0);
assert_eq!(locked_stake_of(&2), 0);

assert_eq!(balances(&2), (20, 0));
assert_eq!(Balances::locks(&2).len(), 0);
Expand Down Expand Up @@ -2096,6 +2076,57 @@ mod tests {
});
}

#[test]
fn empty_term() {
ExtBuilder::default().build_and_execute(|| {
// no candidates, no nothing.
System::set_block_number(5);
Elections::end_block(System::block_number());

assert_eq!(
System::events().iter().last().unwrap().event,
Event::elections_phragmen(RawEvent::EmptyTerm),
)
})
}

#[test]
fn all_outgoing() {
ExtBuilder::default().build_and_execute(|| {
assert_ok!(submit_candidacy(Origin::signed(5)));
assert_ok!(submit_candidacy(Origin::signed(4)));

assert_ok!(vote(Origin::signed(5), vec![5], 50));
assert_ok!(vote(Origin::signed(4), vec![4], 40));

System::set_block_number(5);
Elections::end_block(System::block_number());

assert_eq!(
System::events().iter().last().unwrap().event,
Event::elections_phragmen(RawEvent::NewTerm(vec![(4, 40), (5, 50)])),
);

assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]);
assert_eq!(Elections::runners_up(), vec![]);

assert_ok!(Elections::remove_voter(Origin::signed(5)));
assert_ok!(Elections::remove_voter(Origin::signed(4)));

System::set_block_number(10);
Elections::end_block(System::block_number());

assert_eq!(
System::events().iter().last().unwrap().event,
Event::elections_phragmen(RawEvent::NewTerm(vec![])),
);

// outgoing have lost their bond.
assert_eq!(balances(&4), (37, 0));
assert_eq!(balances(&5), (47, 0));
});
}

#[test]
fn defunct_voter_will_be_counted() {
ExtBuilder::default().build_and_execute(|| {
Expand Down Expand Up @@ -2670,29 +2701,29 @@ mod tests {
})
}

// #[test]
// fn runner_up_replacement_works_when_out_of_order() {
// ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
// assert_ok!(submit_candidacy(Origin::signed(5)));
// assert_ok!(submit_candidacy(Origin::signed(4)));
// assert_ok!(submit_candidacy(Origin::signed(3)));
// assert_ok!(submit_candidacy(Origin::signed(2)));

// assert_ok!(vote(Origin::signed(2), vec![5], 20));
// assert_ok!(vote(Origin::signed(3), vec![3], 30));
// assert_ok!(vote(Origin::signed(4), vec![4], 40));
// assert_ok!(vote(Origin::signed(5), vec![2], 50));

// System::set_block_number(5);
// Elections::end_block(System::block_number());

// assert_eq!(Elections::members_ids(), vec![2, 4]);
// assert_eq!(ELections::runners_up_ids(), vec![3, 5]);
// assert_ok!(Elections::renounce_candidacy(Origin::signed(3), Renouncing::RunnerUp));
// assert_eq!(Elections::members_ids(), vec![2, 4]);
// assert_eq!(ELections::runners_up_ids(), vec![5]);
// });
// }
#[test]
fn runner_up_replacement_works_when_out_of_order() {
ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
assert_ok!(submit_candidacy(Origin::signed(5)));
assert_ok!(submit_candidacy(Origin::signed(4)));
assert_ok!(submit_candidacy(Origin::signed(3)));
assert_ok!(submit_candidacy(Origin::signed(2)));

assert_ok!(vote(Origin::signed(2), vec![5], 20));
assert_ok!(vote(Origin::signed(3), vec![3], 30));
assert_ok!(vote(Origin::signed(4), vec![4], 40));
assert_ok!(vote(Origin::signed(5), vec![2], 50));

System::set_block_number(5);
Elections::end_block(System::block_number());

assert_eq!(Elections::members_ids(), vec![2, 4]);
assert_eq!(Elections::runners_up_ids(), vec![5, 3]);
assert_ok!(Elections::renounce_candidacy(Origin::signed(3), Renouncing::RunnerUp));
assert_eq!(Elections::members_ids(), vec![2, 4]);
assert_eq!(Elections::runners_up_ids(), vec![5]);
});
}

#[test]
fn can_renounce_candidacy_candidate() {
Expand Down
2 changes: 1 addition & 1 deletion frame/staking/fuzzer/src/submit_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fn main() {
// stuff to submit
let (winners, compact, score, size) = match mode {
Mode::InitialSubmission => {
/* No need to setup anything */
// No need to setup anything
get_seq_phragmen_solution::<Test>(do_reduce)
},
Mode::StrongerSubmission => {
Expand Down
Loading