Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Better prime election. #6939

Merged
merged 6 commits into from
Aug 26, 2020
Merged
Changes from 2 commits
Commits
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
28 changes: 21 additions & 7 deletions frame/elections-phragmen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ use codec::{Encode, Decode};
use sp_std::prelude::*;
use sp_runtime::{
DispatchError, RuntimeDebug, Perbill,
traits::{Zero, StaticLookup, Convert},
traits::{Zero, StaticLookup, Convert, Saturating},
};
use frame_support::{
decl_storage, decl_event, ensure, decl_module, decl_error,
Expand Down Expand Up @@ -904,14 +904,20 @@ impl<T: Trait> Module<T> {
to_votes(Self::locked_stake_of(who))
};

let voters_and_votes = Voting::<T>::iter()
.map(|(voter, (stake, targets))| { (voter, to_votes(stake), targets) })
// used for prime election.
let voters_and_stakes = Voting::<T>::iter()
.map(|(voter, (stake, targets))| { (voter, stake, targets) })
.collect::<Vec<_>>();
// used for phragmen.
let voters_and_votes = voters_and_stakes.iter()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-iterating could be heavy. Would imply we need to re-do weights. Also, could we not just re-use voters_and_stakes perhaps convertingn the stake item back to its original type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-iterating could be heavy. Would imply we need to re-do weights

Weight of this is already maximum of the block, can't get any worse.

Also, could we not just re-use voters_and_stakes perhaps converting the stake item back to its original type?

This is practically what we are doing. second iter is in-memory and won't go into storage again. .cloned() is inevitable because we need to pass voters_and_votes by value to seq_phragmen.

.cloned()
.map(|(voter, stake, targets)| { (voter, to_votes(stake), targets)} )
.collect::<Vec<_>>();
let maybe_phragmen_result = sp_npos_elections::seq_phragmen::<T::AccountId, Perbill>(
num_to_elect,
0,
candidates,
voters_and_votes.clone(),
voters_and_votes,
);

if let Some(ElectionResult { winners, assignments }) = maybe_phragmen_result {
Expand Down Expand Up @@ -965,17 +971,25 @@ impl<T: Trait> Module<T> {
// save the members, sorted based on account id.
new_members.sort_by(|i, j| i.0.cmp(&j.0));

let mut prime_votes: Vec<_> = new_members.iter().map(|c| (&c.0, VoteWeight::zero())).collect();
for (_, stake, targets) in voters_and_votes.into_iter() {
// Now we select a prime member by weighing everyone's vote for that new member by a
// multiplier based on the order of the votes. i.e. the first person a voter votes for
// gets a 16x multiplier, the next person gets a 15x multiplier, an so on...
let mut prime_votes: Vec<_> = new_members.iter().map(|c| (&c.0, BalanceOf::<T>::zero())).collect();
for (_, stake, targets) in voters_and_stakes.into_iter() {
for (votes, who) in targets.iter()
.enumerate()
.map(|(votes, who)| ((MAXIMUM_VOTE - votes) as u32, who))
{
if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) {
prime_votes[i].1 += stake * votes as VoteWeight;
prime_votes[i].1 = prime_votes[i].1.saturating_add(
stake.saturating_mul(votes.into())
);
}
}
}
// We then select the new member with the highest weighted stake. In the case of
// a tie, the last person in the list with the tied score is selected. This is
// the person with the "highest" account id based on the sort above.
let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone());

// new_members_ids is sorted by account id.
Expand Down