From 0cd8d2c2cf3c05be517f16ac9323d1e78f0fe8eb Mon Sep 17 00:00:00 2001
From: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Date: Mon, 13 Jan 2020 16:48:00 +0100
Subject: [PATCH] Remove wrong assertion from phragmen (#4515)

* remove assertion
---
 substrate/frame/nicks/src/lib.rs           |  2 +-
 substrate/frame/staking/src/lib.rs         | 24 ++++--
 substrate/primitives/phragmen/src/lib.rs   | 30 ++-----
 substrate/primitives/phragmen/src/mock.rs  |  2 +-
 substrate/primitives/phragmen/src/tests.rs | 91 +++++++++++++++++++++-
 5 files changed, 117 insertions(+), 32 deletions(-)

diff --git a/substrate/frame/nicks/src/lib.rs b/substrate/frame/nicks/src/lib.rs
index de39736a5a0..1d33d12ae46 100644
--- a/substrate/frame/nicks/src/lib.rs
+++ b/substrate/frame/nicks/src/lib.rs
@@ -145,7 +145,7 @@ decl_module! {
 		fn set_name(origin, name: Vec<u8>) {
 			let sender = ensure_signed(origin)?;
 
-			ensure!(name.len() >= T::MinLength::get(), Error::<T>::TooShort,);
+			ensure!(name.len() >= T::MinLength::get(), Error::<T>::TooShort);
 			ensure!(name.len() <= T::MaxLength::get(), Error::<T>::TooLong);
 
 			let deposit = if let Some((_, deposit)) = <NameOf<T>>::get(&sender) {
diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs
index f2f3fdfb70d..a59aa5d0c8e 100644
--- a/substrate/frame/staking/src/lib.rs
+++ b/substrate/frame/staking/src/lib.rs
@@ -1528,18 +1528,30 @@ impl<T: Trait> Module<T> {
 			let mut slot_stake = BalanceOf::<T>::max_value();
 			for (c, s) in supports.into_iter() {
 				// build `struct exposure` from `support`
+				let mut others = Vec::new();
+				let mut own: BalanceOf<T> = Zero::zero();
+				let mut total: BalanceOf<T> = Zero::zero();
+				s.voters
+					.into_iter()
+					.map(|(who, value)| (who, to_balance(value)))
+					.for_each(|(who, value)| {
+						if who == c {
+							own = own.saturating_add(value);
+						} else {
+							others.push(IndividualExposure { who, value });
+						}
+						total = total.saturating_add(value);
+					});
 				let exposure = Exposure {
-					own: to_balance(s.own),
+					own,
+					others,
 					// This might reasonably saturate and we cannot do much about it. The sum of
 					// someone's stake might exceed the balance type if they have the maximum amount
 					// of balance and receive some support. This is super unlikely to happen, yet
 					// we simulate it in some tests.
-					total: to_balance(s.total),
-					others: s.others
-						.into_iter()
-						.map(|(who, value)| IndividualExposure { who, value: to_balance(value) })
-						.collect::<Vec<IndividualExposure<_, _>>>(),
+					total,
 				};
+
 				if exposure.total < slot_stake {
 					slot_stake = exposure.total;
 				}
diff --git a/substrate/primitives/phragmen/src/lib.rs b/substrate/primitives/phragmen/src/lib.rs
index d9712d6ffbd..c13654543da 100644
--- a/substrate/primitives/phragmen/src/lib.rs
+++ b/substrate/primitives/phragmen/src/lib.rs
@@ -118,14 +118,12 @@ pub struct PhragmenResult<AccountId> {
 /// This, at the current version, resembles the `Exposure` defined in the staking SRML module, yet
 /// they do not necessarily have to be the same.
 #[derive(Default, RuntimeDebug)]
-#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, Eq, PartialEq))]
 pub struct Support<AccountId> {
-	/// The amount of support as the effect of self-vote.
-	pub own: ExtendedBalance,
 	/// Total support.
 	pub total: ExtendedBalance,
 	/// Support from voters.
-	pub others: Vec<PhragmenStakedAssignment<AccountId>>,
+	pub voters: Vec<PhragmenStakedAssignment<AccountId>>,
 }
 
 /// A linkage from a candidate and its [`Support`].
@@ -368,20 +366,8 @@ pub fn build_support_map<Balance, AccountId, FS, C>(
 			// per-things to be sound.
 			let other_stake = *per_thing * nominator_stake;
 			if let Some(support) = supports.get_mut(c) {
-				if c == n {
-					// This is a nomination from `n` to themselves. This will increase both the
-					// `own` and `total` field.
-					debug_assert!(*per_thing == Perbill::one()); // TODO: deal with this: do we want it?
-					support.own = support.own.saturating_add(other_stake);
-					support.total = support.total.saturating_add(other_stake);
-				} else {
-					// This is a nomination from `n` to someone else. Increase `total` and add an entry
-					// inside `others`.
-					// For an astronomically rich validator with more astronomically rich
-					// set of nominators, this might saturate.
-					support.total = support.total.saturating_add(other_stake);
-					support.others.push((n.clone(), other_stake));
-				}
+				support.voters.push((n.clone(), other_stake));
+				support.total = support.total.saturating_add(other_stake);
 			}
 		}
 	}
@@ -450,8 +436,8 @@ fn do_equalize<Balance, AccountId, C>(
 	let budget = to_votes(budget_balance);
 
 	// Nothing to do. This voter had nothing useful.
-	// Defensive only. Assignment list should always be populated.
-	if elected_edges.is_empty() { return 0; }
+	// Defensive only. Assignment list should always be populated. 1 might happen for self vote.
+	if elected_edges.is_empty() || elected_edges.len() == 1 { return 0; }
 
 	let stake_used = elected_edges
 		.iter()
@@ -492,7 +478,7 @@ fn do_equalize<Balance, AccountId, C>(
 	elected_edges.iter_mut().for_each(|e| {
 		if let Some(support) = support_map.get_mut(&e.0) {
 			support.total = support.total.saturating_sub(e.1);
-			support.others.retain(|i_support| i_support.0 != *voter);
+			support.voters.retain(|i_support| i_support.0 != *voter);
 		}
 		e.1 = 0;
 	});
@@ -529,7 +515,7 @@ fn do_equalize<Balance, AccountId, C>(
 				.saturating_add(last_stake)
 				.saturating_sub(support.total);
 			support.total = support.total.saturating_add(e.1);
-			support.others.push((voter.clone(), e.1));
+			support.voters.push((voter.clone(), e.1));
 		}
 	});
 
diff --git a/substrate/primitives/phragmen/src/mock.rs b/substrate/primitives/phragmen/src/mock.rs
index 32de65f0e60..59b8d4eb5c6 100644
--- a/substrate/primitives/phragmen/src/mock.rs
+++ b/substrate/primitives/phragmen/src/mock.rs
@@ -375,7 +375,7 @@ pub(crate) fn run_and_compare(
 	check_assignments(assignments);
 }
 
-pub(crate) fn build_support_map<FS>(
+pub(crate) fn build_support_map_float<FS>(
 	result: &mut _PhragmenResult<AccountId>,
 	stake_of: FS,
 ) -> _SupportMap<AccountId>
diff --git a/substrate/primitives/phragmen/src/tests.rs b/substrate/primitives/phragmen/src/tests.rs
index f3974d07d3b..b182cfca05a 100644
--- a/substrate/primitives/phragmen/src/tests.rs
+++ b/substrate/primitives/phragmen/src/tests.rs
@@ -19,7 +19,7 @@
 #![cfg(test)]
 
 use crate::mock::*;
-use crate::{elect, PhragmenResult};
+use crate::{elect, PhragmenResult, PhragmenStakedAssignment, build_support_map, Support, equalize};
 use substrate_test_utils::assert_eq_uvec;
 use sp_runtime::Perbill;
 
@@ -46,7 +46,7 @@ fn float_phragmen_poc_works() {
 		]
 	);
 
-	let mut support_map = build_support_map(&mut phragmen_result, &stake_of);
+	let mut support_map = build_support_map_float(&mut phragmen_result, &stake_of);
 
 	assert_eq!(
 		support_map.get(&2).unwrap(),
@@ -405,3 +405,90 @@ fn minimum_to_elect_is_respected() {
 
 	assert!(maybe_result.is_none());
 }
+
+#[test]
+fn self_votes_should_be_kept() {
+	let candidates = vec![5, 10, 20, 30];
+	let voters = vec![
+		(5, vec![5]),
+		(10, vec![10]),
+		(20, vec![20]),
+		(1, vec![10, 20])
+	];
+	let stake_of = create_stake_of(&[
+		(5, 5),
+		(10, 10),
+		(20, 20),
+		(1, 8),
+	]);
+
+	let result = elect::<_, _, _, TestCurrencyToVote>(
+		2,
+		2,
+		candidates,
+		voters,
+		&stake_of,
+	).unwrap();
+
+	assert_eq!(result.winners, vec![(20, 28), (10, 18)]);
+	assert_eq!(
+		result.assignments,
+		vec![
+			(10, vec![(10, Perbill::from_percent(100))]),
+			(20, vec![(20, Perbill::from_percent(100))]),
+			(1, vec![
+					(10, Perbill::from_percent(50)),
+					(20, Perbill::from_percent(50))
+				]
+			)
+		],
+	);
+
+	let mut supports = build_support_map::<
+		Balance,
+		AccountId,
+		_,
+		TestCurrencyToVote
+		>(
+			&result.winners.into_iter().map(|(who, _)| who).collect(),
+			&result.assignments,
+			&stake_of
+		);
+
+	assert_eq!(supports.get(&5u64), None);
+	assert_eq!(
+		supports.get(&10u64).unwrap(),
+		&Support { total: 14u128, voters: vec![(10u64, 10u128), (1u64, 4u128)] },
+	);
+	assert_eq!(
+		supports.get(&20u64).unwrap(),
+		&Support { total: 24u128, voters: vec![(20u64, 20u128), (1u64, 4u128)] },
+	);
+
+	let assignments = result.assignments;
+	let mut staked_assignments
+		: Vec<(AccountId, Vec<PhragmenStakedAssignment<AccountId>>)>
+		= Vec::with_capacity(assignments.len());
+	for (n, assignment) in assignments.iter() {
+		let mut staked_assignment
+			: Vec<PhragmenStakedAssignment<AccountId>>
+			= Vec::with_capacity(assignment.len());
+		let stake = stake_of(&n);
+		for (c, per_thing) in assignment.iter() {
+			let vote_stake = *per_thing * stake;
+			staked_assignment.push((c.clone(), vote_stake));
+		}
+		staked_assignments.push((n.clone(), staked_assignment));
+	}
+
+	equalize::<Balance, AccountId, TestCurrencyToVote, _>(staked_assignments, &mut supports, 0, 2usize, &stake_of);
+
+	assert_eq!(
+		supports.get(&10u64).unwrap(),
+		&Support { total: 18u128, voters: vec![(10u64, 10u128), (1u64, 8u128)] },
+	);
+	assert_eq!(
+		supports.get(&20u64).unwrap(),
+		&Support { total: 20u128, voters: vec![(20u64, 20u128)] },
+	);
+}
-- 
GitLab