sp_npos_elections/
balancing.rs1use crate::{BalancingConfig, Edge, ExtendedBalance, IdentifierT, Voter};
30use alloc::vec::Vec;
31use sp_arithmetic::traits::Zero;
32
33pub fn balance<AccountId: IdentifierT>(
59 voters: &mut Vec<Voter<AccountId>>,
60 config: &BalancingConfig,
61) -> usize {
62 if config.iterations == 0 {
63 return 0
64 }
65
66 let mut iter = 0;
67 loop {
68 let mut max_diff = 0;
69 for voter in voters.iter_mut() {
70 let diff = balance_voter(voter, config.tolerance);
71 if diff > max_diff {
72 max_diff = diff;
73 }
74 }
75
76 iter += 1;
77 if max_diff <= config.tolerance || iter >= config.iterations {
78 break iter
79 }
80 }
81}
82
83pub(crate) fn balance_voter<AccountId: IdentifierT>(
85 voter: &mut Voter<AccountId>,
86 tolerance: ExtendedBalance,
87) -> ExtendedBalance {
88 let mut elected_edges = voter
90 .edges
91 .iter_mut()
92 .filter(|e| e.candidate.borrow().elected)
93 .collect::<Vec<&mut Edge<AccountId>>>();
94
95 if elected_edges.len() <= 1 {
97 return Zero::zero()
98 }
99
100 let stake_used =
102 elected_edges.iter().fold(0, |a: ExtendedBalance, e| a.saturating_add(e.weight));
103
104 let backed_stakes = elected_edges
106 .iter()
107 .map(|e| e.candidate.borrow().backed_stake)
108 .collect::<Vec<_>>();
109
110 let backing_backed_stake = elected_edges
112 .iter()
113 .filter_map(|e| if e.weight > 0 { Some(e.candidate.borrow().backed_stake) } else { None })
114 .collect::<Vec<_>>();
115
116 let difference = if backing_backed_stake.len() > 0 {
117 let max_stake = backing_backed_stake
118 .iter()
119 .max()
120 .expect("vector with positive length will have a max; qed");
121 let min_stake = backed_stakes
122 .iter()
123 .min()
124 .expect("iterator with positive length will have a min; qed");
125 let mut difference = max_stake.saturating_sub(*min_stake);
126 difference = difference.saturating_add(voter.budget.saturating_sub(stake_used));
127 if difference < tolerance {
128 return difference
129 }
130 difference
131 } else {
132 voter.budget
133 };
134
135 for edge in elected_edges.iter_mut() {
137 let mut candidate = edge.candidate.borrow_mut();
138 candidate.backed_stake = candidate.backed_stake.saturating_sub(edge.weight);
139 edge.weight = 0;
140 }
141
142 elected_edges.sort_by_key(|e| e.candidate.borrow().backed_stake);
143
144 let mut cumulative_backed_stake = Zero::zero();
145 let mut last_index = elected_edges.len() - 1;
146
147 for (index, edge) in elected_edges.iter().enumerate() {
148 let index = index as ExtendedBalance;
149 let backed_stake = edge.candidate.borrow().backed_stake;
150 let temp = backed_stake.saturating_mul(index);
151 if temp.saturating_sub(cumulative_backed_stake) > voter.budget {
152 last_index = index.saturating_sub(1) as usize;
154 break
155 }
156 cumulative_backed_stake = cumulative_backed_stake.saturating_add(backed_stake);
157 }
158
159 let last_stake = elected_edges
160 .get(last_index)
161 .expect(
162 "length of elected_edges is greater than or equal 2; last_index index is at the \
163 minimum elected_edges.len() - 1; index is within range; qed",
164 )
165 .candidate
166 .borrow()
167 .backed_stake;
168 let ways_to_split = last_index + 1;
169 let excess = voter
170 .budget
171 .saturating_add(cumulative_backed_stake)
172 .saturating_sub(last_stake.saturating_mul(ways_to_split as ExtendedBalance));
173
174 for edge in elected_edges.into_iter().take(ways_to_split) {
176 let candidate_backed_stake = {
178 let candidate = edge.candidate.borrow();
179 candidate.backed_stake
180 };
181
182 let new_edge_weight = (excess / ways_to_split as ExtendedBalance)
183 .saturating_add(last_stake)
184 .saturating_sub(candidate_backed_stake);
185
186 edge.weight = new_edge_weight;
188
189 let mut candidate = edge.candidate.borrow_mut();
191 candidate.backed_stake = candidate.backed_stake.saturating_add(new_edge_weight);
192 }
193
194 let _ = voter.try_normalize_elected();
197
198 difference
199}