From 5b6c41639ee93c6f073561661aff01a065c47b1f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 22 Aug 2020 13:20:54 +0200 Subject: [PATCH 1/4] Better prime election. --- frame/elections-phragmen/src/lib.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 50c5de9bc0de4..34b7e38cf2d41 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -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, @@ -904,14 +904,20 @@ impl Module { to_votes(Self::locked_stake_of(who)) }; - let voters_and_votes = Voting::::iter() - .map(|(voter, (stake, targets))| { (voter, to_votes(stake), targets) }) + // used for prime election. + let voters_and_stakes = Voting::::iter() + .map(|(voter, (stake, targets))| { (voter, stake, targets) }) + .collect::>(); + // used for phragmen. + let voters_and_votes = voters_and_stakes.iter() + .cloned() + .map(|(voter, stake, targets)| { (voter, to_votes(stake), targets)} ) .collect::>(); let maybe_phragmen_result = sp_npos_elections::seq_phragmen::( num_to_elect, 0, candidates, - voters_and_votes.clone(), + voters_and_votes, ); if let Some(ElectionResult { winners, assignments }) = maybe_phragmen_result { @@ -965,14 +971,16 @@ impl Module { // 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() { + let mut prime_votes: Vec<_> = new_members.iter().map(|c| (&c.0, BalanceOf::::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()) + ); } } } From ec001777cd32d4d758a4ae98a37c86d8189c1414 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sat, 22 Aug 2020 15:27:04 +0200 Subject: [PATCH 2/4] improve docs --- frame/elections-phragmen/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 34b7e38cf2d41..6b20819944f09 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -971,6 +971,9 @@ impl Module { // save the members, sorted based on account id. new_members.sort_by(|i, j| i.0.cmp(&j.0)); + // 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::::zero())).collect(); for (_, stake, targets) in voters_and_stakes.into_iter() { for (votes, who) in targets.iter() @@ -984,6 +987,9 @@ impl Module { } } } + // 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. From 0a64a85c4ec238b224c743db9312d309f9ad1d53 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sat, 22 Aug 2020 15:32:08 +0200 Subject: [PATCH 3/4] more sensible variable names --- frame/elections-phragmen/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 6b20819944f09..96f0e4699f88b 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -976,13 +976,13 @@ impl Module { // 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::::zero())).collect(); for (_, stake, targets) in voters_and_stakes.into_iter() { - for (votes, who) in targets.iter() + for (vote_multiplier, who) in targets.iter() .enumerate() - .map(|(votes, who)| ((MAXIMUM_VOTE - votes) as u32, who)) + .map(|(vote_position, who)| ((MAXIMUM_VOTE - vote_position) as u32, who)) { if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) { prime_votes[i].1 = prime_votes[i].1.saturating_add( - stake.saturating_mul(votes.into()) + stake.saturating_mul(vote_multiplier.into()) ); } } From 576c1c9e593f6baef1dde9ad397c81dcfa759779 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi Date: Sat, 22 Aug 2020 16:06:50 +0200 Subject: [PATCH 4/4] link to Borda count wiki --- frame/elections-phragmen/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 96f0e4699f88b..9d1922576ad42 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -971,9 +971,10 @@ impl Module { // save the members, sorted based on account id. new_members.sort_by(|i, j| i.0.cmp(&j.0)); - // 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... + // Now we select a prime member using a [Borda count](https://en.wikipedia.org/wiki/Borda_count). + // We weigh 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... (assuming `MAXIMUM_VOTE` = 16) let mut prime_votes: Vec<_> = new_members.iter().map(|c| (&c.0, BalanceOf::::zero())).collect(); for (_, stake, targets) in voters_and_stakes.into_iter() { for (vote_multiplier, who) in targets.iter()