1#![cfg_attr(not(feature = "std"), no_std)]
36
37use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, convert::TryFrom};
38use sp_arithmetic::{
39 PerThing, Rational128,
40 helpers_128bit::multiply_by_rational,
41 traits::{Zero, Saturating, Bounded, SaturatedConversion},
42};
43
44#[cfg(test)]
45mod mock;
46#[cfg(test)]
47mod tests;
48#[cfg(feature = "std")]
49use serde::{Serialize, Deserialize};
50#[cfg(feature = "std")]
51use codec::{Encode, Decode};
52
53mod node;
54mod reduce;
55mod helpers;
56
57pub use reduce::reduce;
59
60pub use helpers::*;
62
63#[doc(hidden)]
65pub use codec;
66#[doc(hidden)]
67pub use sp_arithmetic;
68
69pub use sp_phragmen_compact::generate_compact_solution_type;
71
72pub trait VotingLimit {
74 const LIMIT: usize;
75}
76
77pub trait IdentifierT: Clone + Eq + Default + Ord + Debug + codec::Codec {}
80
81impl<T: Clone + Eq + Default + Ord + Debug + codec::Codec> IdentifierT for T {}
82
83#[derive(Debug, Eq, PartialEq)]
85pub enum Error {
86 CompactStakeOverflow,
89 CompactTargetOverflow,
91 CompactInvalidIndex,
93}
94
95pub type VoteWeight = u64;
98
99pub type ExtendedBalance = u128;
101
102pub type PhragmenScore = [ExtendedBalance; 3];
104
105pub type WithApprovalOf<A> = (A, ExtendedBalance);
107
108const DEN: u128 = u128::max_value();
112
113#[derive(Clone, Default, Debug)]
115struct Candidate<AccountId> {
116 who: AccountId,
118 score: Rational128,
120 approval_stake: ExtendedBalance,
122 elected: bool,
124}
125
126#[derive(Clone, Default, Debug)]
128struct Voter<AccountId> {
129 who: AccountId,
131 edges: Vec<Edge<AccountId>>,
133 budget: ExtendedBalance,
135 load: Rational128,
137}
138
139#[derive(Clone, Default, Debug)]
141struct Edge<AccountId> {
142 who: AccountId,
144 load: Rational128,
146 candidate_index: usize,
148}
149
150#[derive(Debug)]
152pub struct PhragmenResult<AccountId, T: PerThing> {
153 pub winners: Vec<WithApprovalOf<AccountId>>,
156 pub assignments: Vec<Assignment<AccountId, T>>,
159}
160
161#[derive(Debug, Clone, Default)]
163#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))]
164pub struct Assignment<AccountId, T: PerThing> {
165 pub who: AccountId,
167 pub distribution: Vec<(AccountId, T)>,
169}
170
171impl<AccountId, T: PerThing> Assignment<AccountId, T>
172where
173 ExtendedBalance: From<<T as PerThing>::Inner>,
174{
175 pub fn into_staked(self, stake: ExtendedBalance, fill: bool) -> StakedAssignment<AccountId>
185 where
186 T: sp_std::ops::Mul<ExtendedBalance, Output = ExtendedBalance>,
187 {
188 let mut sum: ExtendedBalance = Bounded::min_value();
189 let mut distribution = self
190 .distribution
191 .into_iter()
192 .filter_map(|(target, p)| {
193 if p == Bounded::min_value() {
195 None
196 } else {
197 let distribution_stake = p * stake;
200 sum = sum.saturating_add(distribution_stake);
202 Some((target, distribution_stake))
203 }
204 })
205 .collect::<Vec<(AccountId, ExtendedBalance)>>();
206
207 if fill {
208 if let Some(leftover) = stake.checked_sub(sum) {
211 if let Some(last) = distribution.last_mut() {
212 last.1 = last.1.saturating_add(leftover);
213 }
214 } else if let Some(excess) = sum.checked_sub(stake) {
215 if let Some(last) = distribution.last_mut() {
216 last.1 = last.1.saturating_sub(excess);
217 }
218 }
219 }
220
221 StakedAssignment {
222 who: self.who,
223 distribution,
224 }
225 }
226}
227
228#[derive(Debug, Clone, Default)]
231#[cfg_attr(feature = "std", derive(PartialEq, Eq, Encode, Decode))]
232pub struct StakedAssignment<AccountId> {
233 pub who: AccountId,
235 pub distribution: Vec<(AccountId, ExtendedBalance)>,
237}
238
239impl<AccountId> StakedAssignment<AccountId> {
240 pub fn into_assignment<T: PerThing>(self, fill: bool) -> Assignment<AccountId, T>
253 where
254 ExtendedBalance: From<<T as PerThing>::Inner>,
255 {
256 let accuracy: u128 = T::ACCURACY.saturated_into();
257 let mut sum: u128 = Zero::zero();
258 let stake = self.distribution.iter().map(|x| x.1).sum();
259 let mut distribution = self
260 .distribution
261 .into_iter()
262 .filter_map(|(target, w)| {
263 let per_thing = T::from_rational_approximation(w, stake);
264 if per_thing == Bounded::min_value() {
265 None
266 } else {
267 sum += per_thing.clone().deconstruct().saturated_into();
268 Some((target, per_thing))
269 }
270 })
271 .collect::<Vec<(AccountId, T)>>();
272
273 if fill {
274 if let Some(leftover) = accuracy.checked_sub(sum) {
275 if let Some(last) = distribution.last_mut() {
276 last.1 = last.1.saturating_add(
277 T::from_parts(leftover.saturated_into())
278 );
279 }
280 } else if let Some(excess) = sum.checked_sub(accuracy) {
281 if let Some(last) = distribution.last_mut() {
282 last.1 = last.1.saturating_sub(
283 T::from_parts(excess.saturated_into())
284 );
285 }
286 }
287 }
288
289 Assignment {
290 who: self.who,
291 distribution,
292 }
293 }
294
295 pub fn total(&self) -> ExtendedBalance {
297 self.distribution.iter().fold(Zero::zero(), |a, b| a.saturating_add(b.1))
298 }
299}
300
301#[derive(Default, Debug)]
309#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Eq, PartialEq))]
310pub struct Support<AccountId> {
311 pub total: ExtendedBalance,
313 pub voters: Vec<(AccountId, ExtendedBalance)>,
315}
316
317pub type SupportMap<A> = BTreeMap<A, Support<A>>;
319
320pub fn elect<AccountId, R>(
336 candidate_count: usize,
337 minimum_candidate_count: usize,
338 initial_candidates: Vec<AccountId>,
339 initial_voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
340) -> Option<PhragmenResult<AccountId, R>> where
341 AccountId: Default + Ord + Clone,
342 R: PerThing,
343{
344 let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>;
346 let mut assigned: Vec<Assignment<AccountId, R>>;
347
348 let mut c_idx_cache = BTreeMap::<AccountId, usize>::new();
350
351 let num_voters = initial_candidates.len() + initial_voters.len();
353 let mut voters: Vec<Voter<AccountId>> = Vec::with_capacity(num_voters);
354
355 let mut candidates = initial_candidates
358 .into_iter()
359 .enumerate()
360 .map(|(idx, who)| {
361 c_idx_cache.insert(who.clone(), idx);
362 Candidate { who, ..Default::default() }
363 })
364 .collect::<Vec<Candidate<AccountId>>>();
365
366 if candidates.len() < minimum_candidate_count { return None; }
368
369 voters.extend(initial_voters.into_iter().map(|(who, voter_stake, votes)| {
372 let mut edges: Vec<Edge<AccountId>> = Vec::with_capacity(votes.len());
373 for v in votes {
374 if let Some(idx) = c_idx_cache.get(&v) {
375 candidates[*idx].approval_stake = candidates[*idx].approval_stake
377 .saturating_add(voter_stake.into());
378 edges.push(Edge { who: v.clone(), candidate_index: *idx, ..Default::default() });
379 } }
381 Voter {
382 who,
383 edges: edges,
384 budget: voter_stake.into(),
385 load: Rational128::zero(),
386 }
387 }));
388
389
390 let to_elect = candidate_count.min(candidates.len());
393 elected_candidates = Vec::with_capacity(candidate_count);
394 assigned = Vec::with_capacity(candidate_count);
395
396 for _round in 0..to_elect {
398 for c in &mut candidates {
400 if !c.elected {
401 if c.approval_stake.is_zero() {
404 c.score = Rational128::from_unchecked(DEN, 0);
405 } else {
406 c.score = Rational128::from(DEN / c.approval_stake, DEN);
407 }
408 }
409 }
410
411 for n in &voters {
413 for e in &n.edges {
414 let c = &mut candidates[e.candidate_index];
415 if !c.elected && !c.approval_stake.is_zero() {
416 let temp_n = multiply_by_rational(
417 n.load.n(),
418 n.budget,
419 c.approval_stake,
420 ).unwrap_or(Bounded::max_value());
421 let temp_d = n.load.d();
422 let temp = Rational128::from(temp_n, temp_d);
423 c.score = c.score.lazy_saturating_add(temp);
424 }
425 }
426 }
427
428 if let Some(winner) = candidates
430 .iter_mut()
431 .filter(|c| !c.elected)
432 .min_by_key(|c| c.score)
433 {
434 winner.elected = true;
436 for n in &mut voters {
437 for e in &mut n.edges {
438 if e.who == winner.who {
439 e.load = winner.score.lazy_saturating_sub(n.load);
440 n.load = winner.score;
441 }
442 }
443 }
444
445 elected_candidates.push((winner.who.clone(), winner.approval_stake));
446 } else {
447 break
448 }
449 } for n in &mut voters {
453 let mut assignment = Assignment {
454 who: n.who.clone(),
455 ..Default::default()
456 };
457 for e in &mut n.edges {
458 if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() {
459 let per_bill_parts: R::Inner =
460 {
461 if n.load == e.load {
462 R::ACCURACY
464 } else {
465 if e.load.d() == n.load.d() {
466 let desired_scale: u128 = R::ACCURACY.saturated_into();
468 let parts = multiply_by_rational(
469 desired_scale,
470 e.load.n(),
471 n.load.n(),
472 )
473 .unwrap_or(Bounded::max_value());
475
476 TryFrom::try_from(parts)
477 .unwrap_or(Bounded::max_value())
482 } else {
483 Zero::zero()
486 }
487 }
488 };
489 let per_thing = R::from_parts(per_bill_parts);
490 assignment.distribution.push((e.who.clone(), per_thing));
491 }
492 }
493
494 let len = assignment.distribution.len();
495 if len > 0 {
496 let vote_count: R::Inner = len.saturated_into();
499 let accuracy = R::ACCURACY;
500 let mut sum: R::Inner = Zero::zero();
501 assignment.distribution.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct()));
502
503 let diff = accuracy.saturating_sub(sum);
504 let diff_per_vote = (diff / vote_count).min(accuracy);
505
506 if !diff_per_vote.is_zero() {
507 for i in 0..len {
508 let current_ratio = assignment.distribution[i % len].1;
509 let next_ratio = current_ratio
510 .saturating_add(R::from_parts(diff_per_vote));
511 assignment.distribution[i % len].1 = next_ratio;
512 }
513 }
514
515 let remainder = diff - diff_per_vote * vote_count;
518 for i in 0..remainder.saturated_into::<usize>() {
519 let current_ratio = assignment.distribution[i % len].1;
520 let next_ratio = current_ratio.saturating_add(R::from_parts(1u8.into()));
521 assignment.distribution[i % len].1 = next_ratio;
522 }
523 assigned.push(assignment);
524 }
525 }
526
527 Some(PhragmenResult {
528 winners: elected_candidates,
529 assignments: assigned,
530 })
531}
532
533pub fn build_support_map<AccountId>(
565 winners: &[AccountId],
566 assignments: &[StakedAssignment<AccountId>],
567) -> (SupportMap<AccountId>, u32) where
568 AccountId: Default + Ord + Clone,
569{
570 let mut errors = 0;
571 let mut supports = <SupportMap<AccountId>>::new();
573 winners
574 .iter()
575 .for_each(|e| { supports.insert(e.clone(), Default::default()); });
576
577 for StakedAssignment { who, distribution } in assignments.iter() {
579 for (c, weight_extended) in distribution.iter() {
580 if let Some(support) = supports.get_mut(c) {
581 support.total = support.total.saturating_add(*weight_extended);
582 support.voters.push((who.clone(), *weight_extended));
583 } else {
584 errors = errors.saturating_add(1);
585 }
586 }
587 }
588 (supports, errors)
589}
590
591pub fn evaluate_support<AccountId>(
599 support: &SupportMap<AccountId>,
600) -> PhragmenScore {
601 let mut min_support = ExtendedBalance::max_value();
602 let mut sum: ExtendedBalance = Zero::zero();
603 let mut sum_squared: ExtendedBalance = Zero::zero();
606 for (_, support) in support.iter() {
607 sum = sum.saturating_add(support.total);
608 let squared = support.total.saturating_mul(support.total);
609 sum_squared = sum_squared.saturating_add(squared);
610 if support.total < min_support {
611 min_support = support.total;
612 }
613 }
614 [min_support, sum, sum_squared]
615}
616
617pub fn is_score_better(this: PhragmenScore, that: PhragmenScore) -> bool {
624 match that
625 .iter()
626 .enumerate()
627 .map(|(i, e)| e.cmp(&this[i]))
628 .collect::<Vec<Ordering>>()
629 .as_slice()
630 {
631 [Ordering::Greater, _, _] => true,
632 [Ordering::Equal, Ordering::Greater, _] => true,
633 [Ordering::Equal, Ordering::Equal, Ordering::Less] => true,
634 _ => false,
635 }
636}
637
638pub fn equalize<AccountId>(
649 assignments: &mut Vec<StakedAssignment<AccountId>>,
650 supports: &mut SupportMap<AccountId>,
651 tolerance: ExtendedBalance,
652 iterations: usize,
653) -> usize where AccountId: Ord + Clone {
654 if iterations == 0 { return 0; }
655
656 let mut i = 0 ;
657 loop {
658 let mut max_diff = 0;
659 for assignment in assignments.iter_mut() {
660 let voter_budget = assignment.total();
661 let StakedAssignment { who, distribution } = assignment;
662 let diff = do_equalize(
663 who,
664 voter_budget,
665 distribution,
666 supports,
667 tolerance,
668 );
669 if diff > max_diff { max_diff = diff; }
670 }
671
672 i += 1;
673 if max_diff <= tolerance || i >= iterations {
674 break i;
675 }
676 }
677}
678
679fn do_equalize<AccountId>(
682 voter: &AccountId,
683 budget: ExtendedBalance,
684 elected_edges: &mut Vec<(AccountId, ExtendedBalance)>,
685 support_map: &mut SupportMap<AccountId>,
686 tolerance: ExtendedBalance
687) -> ExtendedBalance where AccountId: Ord + Clone {
688 if elected_edges.is_empty() || elected_edges.len() == 1 { return 0; }
691
692 let stake_used = elected_edges
693 .iter()
694 .fold(0 as ExtendedBalance, |s, e| s.saturating_add(e.1));
695
696 let backed_stakes_iter = elected_edges
697 .iter()
698 .filter_map(|e| support_map.get(&e.0))
699 .map(|e| e.total);
700
701 let backing_backed_stake = elected_edges
702 .iter()
703 .filter(|e| e.1 > 0)
704 .filter_map(|e| support_map.get(&e.0))
705 .map(|e| e.total)
706 .collect::<Vec<ExtendedBalance>>();
707
708 let mut difference;
709 if backing_backed_stake.len() > 0 {
710 let max_stake = backing_backed_stake
711 .iter()
712 .max()
713 .expect("vector with positive length will have a max; qed");
714 let min_stake = backed_stakes_iter
715 .min()
716 .expect("iterator with positive length will have a min; qed");
717
718 difference = max_stake.saturating_sub(min_stake);
719 difference = difference.saturating_add(budget.saturating_sub(stake_used));
720 if difference < tolerance {
721 return difference;
722 }
723 } else {
724 difference = budget;
725 }
726
727 elected_edges.iter_mut().for_each(|e| {
729 if let Some(support) = support_map.get_mut(&e.0) {
730 support.total = support.total.saturating_sub(e.1);
731 support.voters.retain(|i_support| i_support.0 != *voter);
732 }
733 e.1 = 0;
734 });
735
736 elected_edges.sort_unstable_by_key(|e|
737 if let Some(e) = support_map.get(&e.0) { e.total } else { Zero::zero() }
738 );
739
740 let mut cumulative_stake: ExtendedBalance = 0;
741 let mut last_index = elected_edges.len() - 1;
742 let mut idx = 0usize;
743 for e in &mut elected_edges[..] {
744 if let Some(support) = support_map.get_mut(&e.0) {
745 let stake = support.total;
746 let stake_mul = stake.saturating_mul(idx as ExtendedBalance);
747 let stake_sub = stake_mul.saturating_sub(cumulative_stake);
748 if stake_sub > budget {
749 last_index = idx.checked_sub(1).unwrap_or(0);
750 break;
751 }
752 cumulative_stake = cumulative_stake.saturating_add(stake);
753 }
754 idx += 1;
755 }
756
757 let last_stake = elected_edges[last_index].1;
758 let split_ways = last_index + 1;
759 let excess = budget
760 .saturating_add(cumulative_stake)
761 .saturating_sub(last_stake.saturating_mul(split_ways as ExtendedBalance));
762 elected_edges.iter_mut().take(split_ways).for_each(|e| {
763 if let Some(support) = support_map.get_mut(&e.0) {
764 e.1 = (excess / split_ways as ExtendedBalance)
765 .saturating_add(last_stake)
766 .saturating_sub(support.total);
767 support.total = support.total.saturating_add(e.1);
768 support.voters.push((voter.clone(), e.1));
769 }
770 });
771
772 difference
773}