referrerpolicy=no-referrer-when-downgrade

pallet_elections_phragmen/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Phragmén Election Module.
19//!
20//! An election module based on sequential phragmen.
21//!
22//! ### Term and Round
23//!
24//! The election happens in _rounds_: every `N` blocks, all previous members are retired and a new
25//! set is elected (which may or may not have an intersection with the previous set). Each round
26//! lasts for some number of blocks defined by [`Config::TermDuration`]. The words _term_ and
27//! _round_ can be used interchangeably in this context.
28//!
29//! [`Config::TermDuration`] might change during a round. This can shorten or extend the length of
30//! the round. The next election round's block number is never stored but rather always checked on
31//! the fly. Based on the current block number and [`Config::TermDuration`], the condition
32//! `BlockNumber % TermDuration == 0` being satisfied will always trigger a new election round.
33//!
34//! ### Bonds and Deposits
35//!
36//! Both voting and being a candidate requires deposits to be taken, in exchange for the data that
37//! needs to be kept on-chain. The terms *bond* and *deposit* can be used interchangeably in this
38//! context.
39//!
40//! Bonds will be unreserved only upon adhering to the protocol laws. Failing to do so will cause in
41//! the bond to slashed.
42//!
43//! ### Voting
44//!
45//! Voters can vote for a limited number of the candidates by providing a list of account ids,
46//! bounded by [`Config::MaxVotesPerVoter`]. Invalid votes (voting for non-candidates) and duplicate
47//! votes are ignored during election. Yet, a voter _might_ vote for a future candidate. Voters
48//! reserve a bond as they vote. Each vote defines a `value`. This amount is locked from the account
49//! of the voter and indicates the weight of the vote. Voters can update their votes at any time by
50//! calling `vote()` again. This can update the vote targets (which might update the deposit) or
51//! update the vote's stake ([`Voter::stake`]). After a round, votes are kept and might still be
52//! valid for further rounds. A voter is responsible for calling `remove_voter` once they are done
53//! to have their bond back and remove the lock.
54//!
55//! See [`Call::vote`], [`Call::remove_voter`].
56//!
57//! ### Defunct Voter
58//!
59//! A voter is defunct once all of the candidates that they have voted for are not a valid candidate
60//! (as seen further below, members and runners-up are also always candidates). Defunct voters can
61//! be removed via a root call ([`Call::clean_defunct_voters`]). Upon being removed, their bond is
62//! returned. This is an administrative operation and can be called only by the root origin in the
63//! case of state bloat.
64//!
65//! ### Candidacy and Members
66//!
67//! Candidates also reserve a bond as they submit candidacy. A candidate can end up in one of the
68//! below situations:
69//!   - **Members**: A winner is kept as a _member_. They must still have a bond in reserve and they
70//!     are automatically counted as a candidate for the next election. The number of desired
71//!     members is set by [`Config::DesiredMembers`].
72//!   - **Runner-up**: Runners-up are the best candidates immediately after the winners. The number
73//!     of runners up to keep is set by [`Config::DesiredRunnersUp`]. Runners-up are used, in the
74//!     same order as they are elected, as replacements when a candidate is kicked by
75//!     [`Call::remove_member`], or when an active member renounces their candidacy. Runners are
76//!     automatically counted as a candidate for the next election.
77//!   - **Loser**: Any of the candidate who are not member/runner-up are left as losers. A loser
78//!     might be an _outgoing member or runner-up_, meaning that they are an active member who
79//!     failed to keep their spot. **An outgoing candidate/member/runner-up will always lose their
80//!     bond**.
81//!
82//! #### Renouncing candidacy.
83//!
84//! All candidates, elected or not, can renounce their candidacy. A call to
85//! [`Call::renounce_candidacy`] will always cause the candidacy bond to be refunded.
86//!
87//! Note that with the members being the default candidates for the next round and votes persisting
88//! in storage, the election system is entirely stable given no further input. This means that if
89//! the system has a particular set of candidates `C` and voters `V` that lead to a set of members
90//! `M` being elected, as long as `V` and `C` don't remove their candidacy and votes, `M` will keep
91//! being re-elected at the end of each round.
92//!
93//! ### Module Information
94//!
95//! - [`Config`]
96//! - [`Call`]
97//! - [`Module`]
98
99#![cfg_attr(not(feature = "std"), no_std)]
100
101extern crate alloc;
102
103use alloc::{vec, vec::Vec};
104use codec::{Decode, DecodeWithMemTracking, Encode};
105use core::cmp::Ordering;
106use frame_support::{
107	traits::{
108		defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, Get,
109		InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency,
110		SortedMembers, WithdrawReasons,
111	},
112	weights::Weight,
113};
114use log;
115use scale_info::TypeInfo;
116use sp_npos_elections::{ElectionResult, ExtendedBalance};
117use sp_runtime::{
118	traits::{Saturating, StaticLookup, Zero},
119	Debug, DispatchError, Perbill,
120};
121use sp_staking::currency_to_vote::CurrencyToVote;
122
123#[cfg(any(feature = "try-runtime", test))]
124use sp_runtime::TryRuntimeError;
125
126mod benchmarking;
127pub mod weights;
128pub use weights::WeightInfo;
129
130/// All migrations.
131pub mod migrations;
132
133const LOG_TARGET: &str = "runtime::elections-phragmen";
134
135type BalanceOf<T> =
136	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
137type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
138	<T as frame_system::Config>::AccountId,
139>>::NegativeImbalance;
140
141/// An indication that the renouncing account currently has which of the below roles.
142#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
143pub enum Renouncing {
144	/// A member is renouncing.
145	Member,
146	/// A runner-up is renouncing.
147	RunnerUp,
148	/// A candidate is renouncing, while the given total number of candidates exists.
149	Candidate(#[codec(compact)] u32),
150}
151
152/// An active voter.
153#[derive(Encode, Decode, Clone, Debug, PartialEq, TypeInfo)]
154pub struct Voter<AccountId, Balance> {
155	/// The members being backed.
156	pub votes: Vec<AccountId>,
157	/// The amount of stake placed on this vote.
158	pub stake: Balance,
159	/// The amount of deposit reserved for this vote.
160	///
161	/// To be unreserved upon removal.
162	pub deposit: Balance,
163}
164
165impl<AccountId, Balance: Default> Default for Voter<AccountId, Balance> {
166	fn default() -> Self {
167		Self { votes: vec![], stake: Default::default(), deposit: Default::default() }
168	}
169}
170
171/// A holder of a seat as either a member or a runner-up.
172#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, TypeInfo)]
173pub struct SeatHolder<AccountId, Balance> {
174	/// The holder.
175	pub who: AccountId,
176	/// The total backing stake.
177	pub stake: Balance,
178	/// The amount of deposit held on-chain.
179	///
180	/// To be unreserved upon renouncing, or slashed upon being a loser.
181	pub deposit: Balance,
182}
183
184pub use pallet::*;
185
186type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
187
188#[frame_support::pallet]
189pub mod pallet {
190	use super::*;
191	use frame_support::pallet_prelude::*;
192	use frame_system::pallet_prelude::*;
193
194	/// The in-code storage version.
195	const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
196
197	#[pallet::pallet]
198	#[pallet::storage_version(STORAGE_VERSION)]
199	#[pallet::without_storage_info]
200	pub struct Pallet<T>(_);
201
202	#[pallet::config]
203	pub trait Config: frame_system::Config {
204		#[allow(deprecated)]
205		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
206
207		/// Identifier for the elections-phragmen pallet's lock
208		#[pallet::constant]
209		type PalletId: Get<LockIdentifier>;
210
211		/// The currency that people are electing with.
212		type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>
213			+ ReservableCurrency<Self::AccountId>;
214
215		/// What to do when the members change.
216		type ChangeMembers: ChangeMembers<Self::AccountId>;
217
218		/// What to do with genesis members
219		type InitializeMembers: InitializeMembers<Self::AccountId>;
220
221		/// Convert a balance into a number used for election calculation.
222		/// This must fit into a `u64` but is allowed to be sensibly lossy.
223		type CurrencyToVote: CurrencyToVote<BalanceOf<Self>>;
224
225		/// How much should be locked up in order to submit one's candidacy.
226		#[pallet::constant]
227		type CandidacyBond: Get<BalanceOf<Self>>;
228
229		/// Base deposit associated with voting.
230		///
231		/// This should be sensibly high to economically ensure the pallet cannot be attacked by
232		/// creating a gigantic number of votes.
233		#[pallet::constant]
234		type VotingBondBase: Get<BalanceOf<Self>>;
235
236		/// The amount of bond that need to be locked for each vote (32 bytes).
237		#[pallet::constant]
238		type VotingBondFactor: Get<BalanceOf<Self>>;
239
240		/// Handler for the unbalanced reduction when a candidate has lost (and is not a runner-up)
241		type LoserCandidate: OnUnbalanced<NegativeImbalanceOf<Self>>;
242
243		/// Handler for the unbalanced reduction when a member has been kicked.
244		type KickedMember: OnUnbalanced<NegativeImbalanceOf<Self>>;
245
246		/// Number of members to elect.
247		#[pallet::constant]
248		type DesiredMembers: Get<u32>;
249
250		/// Number of runners_up to keep.
251		#[pallet::constant]
252		type DesiredRunnersUp: Get<u32>;
253
254		/// How long each seat is kept. This defines the next block number at which an election
255		/// round will happen. If set to zero, no elections are ever triggered and the module will
256		/// be in passive mode.
257		#[pallet::constant]
258		type TermDuration: Get<BlockNumberFor<Self>>;
259
260		/// The maximum number of candidates in a phragmen election.
261		///
262		/// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
263		/// consider how it will impact `T::WeightInfo::election_phragmen`.
264		///
265		/// When this limit is reached no more candidates are accepted in the election.
266		#[pallet::constant]
267		type MaxCandidates: Get<u32>;
268
269		/// The maximum number of voters to allow in a phragmen election.
270		///
271		/// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
272		/// consider how it will impact `T::WeightInfo::election_phragmen`.
273		///
274		/// When the limit is reached the new voters are ignored.
275		#[pallet::constant]
276		type MaxVoters: Get<u32>;
277
278		/// Maximum numbers of votes per voter.
279		///
280		/// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
281		/// consider how it will impact `T::WeightInfo::election_phragmen`.
282		#[pallet::constant]
283		type MaxVotesPerVoter: Get<u32>;
284
285		/// Weight information for extrinsics in this pallet.
286		type WeightInfo: WeightInfo;
287	}
288
289	#[pallet::hooks]
290	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
291		/// What to do at the end of each block.
292		///
293		/// Checks if an election needs to happen or not.
294		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
295			let term_duration = T::TermDuration::get();
296			if !term_duration.is_zero() && (n % term_duration).is_zero() {
297				Self::do_phragmen()
298			} else {
299				Weight::zero()
300			}
301		}
302
303		fn integrity_test() {
304			let block_weight = T::BlockWeights::get().max_block;
305			// mind the order.
306			let election_weight = T::WeightInfo::election_phragmen(
307				T::MaxCandidates::get(),
308				T::MaxVoters::get(),
309				T::MaxVotesPerVoter::get() * T::MaxVoters::get(),
310			);
311
312			let to_seconds = |w: &Weight| {
313				w.ref_time() as f32 /
314					frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND as f32
315			};
316
317			log::debug!(
318				target: LOG_TARGET,
319				"election weight {}s ({:?}) // chain's block weight {}s ({:?})",
320				to_seconds(&election_weight),
321				election_weight,
322				to_seconds(&block_weight),
323				block_weight,
324			);
325			assert!(
326				election_weight.all_lt(block_weight),
327				"election weight {}s ({:?}) will exceed a {}s chain's block weight ({:?}) (MaxCandidates {}, MaxVoters {}, MaxVotesPerVoter {} -- tweak these parameters)",
328				election_weight,
329				to_seconds(&election_weight),
330				to_seconds(&block_weight),
331				block_weight,
332				T::MaxCandidates::get(),
333				T::MaxVoters::get(),
334				T::MaxVotesPerVoter::get(),
335			);
336		}
337
338		#[cfg(feature = "try-runtime")]
339		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
340			Self::do_try_state()
341		}
342	}
343
344	#[pallet::call]
345	impl<T: Config> Pallet<T> {
346		/// Vote for a set of candidates for the upcoming round of election. This can be called to
347		/// set the initial votes, or update already existing votes.
348		///
349		/// Upon initial voting, `value` units of `who`'s balance is locked and a deposit amount is
350		/// reserved. The deposit is based on the number of votes and can be updated over time.
351		///
352		/// The `votes` should:
353		///   - not be empty.
354		///   - be less than the number of possible candidates. Note that all current members and
355		///     runners-up are also automatically candidates for the next round.
356		///
357		/// If `value` is more than `who`'s free balance, then the maximum of the two is used.
358		///
359		/// The dispatch origin of this call must be signed.
360		///
361		/// ### Warning
362		///
363		/// It is the responsibility of the caller to **NOT** place all of their balance into the
364		/// lock and keep some for further operations.
365		#[pallet::call_index(0)]
366		#[pallet::weight(
367			T::WeightInfo::vote_more(votes.len() as u32)
368			.max(T::WeightInfo::vote_less(votes.len() as u32))
369			.max(T::WeightInfo::vote_equal(votes.len() as u32))
370		)]
371		pub fn vote(
372			origin: OriginFor<T>,
373			votes: Vec<T::AccountId>,
374			#[pallet::compact] value: BalanceOf<T>,
375		) -> DispatchResultWithPostInfo {
376			let who = ensure_signed(origin)?;
377
378			ensure!(
379				votes.len() <= T::MaxVotesPerVoter::get() as usize,
380				Error::<T>::MaximumVotesExceeded
381			);
382			ensure!(!votes.is_empty(), Error::<T>::NoVotes);
383
384			let candidates_count = Candidates::<T>::decode_len().unwrap_or(0);
385			let members_count = Members::<T>::decode_len().unwrap_or(0);
386			let runners_up_count = RunnersUp::<T>::decode_len().unwrap_or(0);
387
388			// can never submit a vote of there are no members, and cannot submit more votes than
389			// all potential vote targets.
390			// addition is valid: candidates, members and runners-up will never overlap.
391			let allowed_votes =
392				candidates_count.saturating_add(members_count).saturating_add(runners_up_count);
393			ensure!(!allowed_votes.is_zero(), Error::<T>::UnableToVote);
394			ensure!(votes.len() <= allowed_votes, Error::<T>::TooManyVotes);
395
396			ensure!(value > T::Currency::minimum_balance(), Error::<T>::LowBalance);
397
398			// Reserve bond.
399			let new_deposit = Self::deposit_of(votes.len());
400			let Voter { deposit: old_deposit, .. } = Voting::<T>::get(&who);
401			match new_deposit.cmp(&old_deposit) {
402				Ordering::Greater => {
403					// Must reserve a bit more.
404					let to_reserve = new_deposit - old_deposit;
405					T::Currency::reserve(&who, to_reserve)
406						.map_err(|_| Error::<T>::UnableToPayBond)?;
407				},
408				Ordering::Equal => {},
409				Ordering::Less => {
410					// Must unreserve a bit.
411					let to_unreserve = old_deposit - new_deposit;
412					let _remainder = T::Currency::unreserve(&who, to_unreserve);
413					debug_assert!(_remainder.is_zero());
414				},
415			};
416
417			// Amount to be locked up.
418			let locked_stake = value.min(T::Currency::free_balance(&who));
419			T::Currency::set_lock(T::PalletId::get(), &who, locked_stake, WithdrawReasons::all());
420
421			Voting::<T>::insert(&who, Voter { votes, deposit: new_deposit, stake: locked_stake });
422			Ok(None::<Weight>.into())
423		}
424
425		/// Remove `origin` as a voter.
426		///
427		/// This removes the lock and returns the deposit.
428		///
429		/// The dispatch origin of this call must be signed and be a voter.
430		#[pallet::call_index(1)]
431		#[pallet::weight(T::WeightInfo::remove_voter())]
432		pub fn remove_voter(origin: OriginFor<T>) -> DispatchResult {
433			let who = ensure_signed(origin)?;
434			ensure!(Self::is_voter(&who), Error::<T>::MustBeVoter);
435			Self::do_remove_voter(&who);
436			Ok(())
437		}
438
439		/// Submit oneself for candidacy. A fixed amount of deposit is recorded.
440		///
441		/// All candidates are wiped at the end of the term. They either become a member/runner-up,
442		/// or leave the system while their deposit is slashed.
443		///
444		/// The dispatch origin of this call must be signed.
445		///
446		/// ### Warning
447		///
448		/// Even if a candidate ends up being a member, they must call [`Call::renounce_candidacy`]
449		/// to get their deposit back. Losing the spot in an election will always lead to a slash.
450		///
451		/// The number of current candidates must be provided as witness data.
452		/// ## Complexity
453		/// O(C + log(C)) where C is candidate_count.
454		#[pallet::call_index(2)]
455		#[pallet::weight(T::WeightInfo::submit_candidacy(*candidate_count))]
456		pub fn submit_candidacy(
457			origin: OriginFor<T>,
458			#[pallet::compact] candidate_count: u32,
459		) -> DispatchResult {
460			let who = ensure_signed(origin)?;
461
462			let actual_count = Candidates::<T>::decode_len().unwrap_or(0) as u32;
463			ensure!(actual_count <= candidate_count, Error::<T>::InvalidWitnessData);
464			ensure!(
465				actual_count < <T as Config>::MaxCandidates::get(),
466				Error::<T>::TooManyCandidates
467			);
468
469			let index = Self::is_candidate(&who).err().ok_or(Error::<T>::DuplicatedCandidate)?;
470
471			ensure!(!Self::is_member(&who), Error::<T>::MemberSubmit);
472			ensure!(!Self::is_runner_up(&who), Error::<T>::RunnerUpSubmit);
473
474			T::Currency::reserve(&who, T::CandidacyBond::get())
475				.map_err(|_| Error::<T>::InsufficientCandidateFunds)?;
476
477			Candidates::<T>::mutate(|c| c.insert(index, (who, T::CandidacyBond::get())));
478			Ok(())
479		}
480
481		/// Renounce one's intention to be a candidate for the next election round. 3 potential
482		/// outcomes exist:
483		///
484		/// - `origin` is a candidate and not elected in any set. In this case, the deposit is
485		///   unreserved, returned and origin is removed as a candidate.
486		/// - `origin` is a current runner-up. In this case, the deposit is unreserved, returned and
487		///   origin is removed as a runner-up.
488		/// - `origin` is a current member. In this case, the deposit is unreserved and origin is
489		///   removed as a member, consequently not being a candidate for the next round anymore.
490		///   Similar to [`remove_member`](Self::remove_member), if replacement runners exists, they
491		///   are immediately used. If the prime is renouncing, then no prime will exist until the
492		///   next round.
493		///
494		/// The dispatch origin of this call must be signed, and have one of the above roles.
495		/// The type of renouncing must be provided as witness data.
496		///
497		/// ## Complexity
498		///   - Renouncing::Candidate(count): O(count + log(count))
499		///   - Renouncing::Member: O(1)
500		///   - Renouncing::RunnerUp: O(1)
501		#[pallet::call_index(3)]
502		#[pallet::weight(match *renouncing {
503			Renouncing::Candidate(count) => T::WeightInfo::renounce_candidacy_candidate(count),
504			Renouncing::Member => T::WeightInfo::renounce_candidacy_members(),
505			Renouncing::RunnerUp => T::WeightInfo::renounce_candidacy_runners_up(),
506		})]
507		pub fn renounce_candidacy(origin: OriginFor<T>, renouncing: Renouncing) -> DispatchResult {
508			let who = ensure_signed(origin)?;
509			match renouncing {
510				Renouncing::Member => {
511					Self::remove_and_replace_member(&who, false)
512						.map_err(|_| Error::<T>::InvalidRenouncing)?;
513					Self::deposit_event(Event::Renounced { candidate: who });
514				},
515				Renouncing::RunnerUp => {
516					RunnersUp::<T>::try_mutate::<_, Error<T>, _>(|runners_up| {
517						let index = runners_up
518							.iter()
519							.position(|SeatHolder { who: r, .. }| r == &who)
520							.ok_or(Error::<T>::InvalidRenouncing)?;
521						// can't fail anymore.
522						let SeatHolder { deposit, .. } = runners_up.remove(index);
523						let _remainder = T::Currency::unreserve(&who, deposit);
524						debug_assert!(_remainder.is_zero());
525						Self::deposit_event(Event::Renounced { candidate: who });
526						Ok(())
527					})?;
528				},
529				Renouncing::Candidate(count) => {
530					Candidates::<T>::try_mutate::<_, Error<T>, _>(|candidates| {
531						ensure!(count >= candidates.len() as u32, Error::<T>::InvalidWitnessData);
532						let index = candidates
533							.binary_search_by(|(c, _)| c.cmp(&who))
534							.map_err(|_| Error::<T>::InvalidRenouncing)?;
535						let (_removed, deposit) = candidates.remove(index);
536						let _remainder = T::Currency::unreserve(&who, deposit);
537						debug_assert!(_remainder.is_zero());
538						Self::deposit_event(Event::Renounced { candidate: who });
539						Ok(())
540					})?;
541				},
542			};
543			Ok(())
544		}
545
546		/// Remove a particular member from the set. This is effective immediately and the bond of
547		/// the outgoing member is slashed.
548		///
549		/// If a runner-up is available, then the best runner-up will be removed and replaces the
550		/// outgoing member. Otherwise, if `rerun_election` is `true`, a new phragmen election is
551		/// started, else, nothing happens.
552		///
553		/// If `slash_bond` is set to true, the bond of the member being removed is slashed. Else,
554		/// it is returned.
555		///
556		/// The dispatch origin of this call must be root.
557		///
558		/// Note that this does not affect the designated block number of the next election.
559		///
560		/// ## Complexity
561		/// - Check details of remove_and_replace_member() and do_phragmen().
562		#[pallet::call_index(4)]
563		#[pallet::weight(if *rerun_election {
564			T::WeightInfo::remove_member_without_replacement()
565		} else {
566			T::WeightInfo::remove_member_with_replacement()
567		})]
568		pub fn remove_member(
569			origin: OriginFor<T>,
570			who: AccountIdLookupOf<T>,
571			slash_bond: bool,
572			rerun_election: bool,
573		) -> DispatchResult {
574			ensure_root(origin)?;
575			let who = T::Lookup::lookup(who)?;
576
577			Self::remove_and_replace_member(&who, slash_bond)?;
578			Self::deposit_event(Event::MemberKicked { member: who });
579
580			if rerun_election {
581				Self::do_phragmen();
582			}
583
584			// no refund needed.
585			Ok(())
586		}
587
588		/// Clean all voters who are defunct (i.e. they do not serve any purpose at all). The
589		/// deposit of the removed voters are returned.
590		///
591		/// This is an root function to be used only for cleaning the state.
592		///
593		/// The dispatch origin of this call must be root.
594		///
595		/// ## Complexity
596		/// - Check is_defunct_voter() details.
597		#[pallet::call_index(5)]
598		#[pallet::weight(T::WeightInfo::clean_defunct_voters(*num_voters, *num_defunct))]
599		pub fn clean_defunct_voters(
600			origin: OriginFor<T>,
601			num_voters: u32,
602			num_defunct: u32,
603		) -> DispatchResult {
604			ensure_root(origin)?;
605
606			Voting::<T>::iter()
607				.take(num_voters as usize)
608				.filter(|(_, x)| Self::is_defunct_voter(&x.votes))
609				.take(num_defunct as usize)
610				.for_each(|(dv, _)| Self::do_remove_voter(&dv));
611
612			Ok(())
613		}
614	}
615
616	#[pallet::event]
617	#[pallet::generate_deposit(pub(super) fn deposit_event)]
618	pub enum Event<T: Config> {
619		/// A new term with new_members. This indicates that enough candidates existed to run
620		/// the election, not that enough have been elected. The inner value must be examined
621		/// for this purpose. A `NewTerm(\[\])` indicates that some candidates got their bond
622		/// slashed and none were elected, whilst `EmptyTerm` means that no candidates existed to
623		/// begin with.
624		NewTerm { new_members: Vec<(<T as frame_system::Config>::AccountId, BalanceOf<T>)> },
625		/// No (or not enough) candidates existed for this round. This is different from
626		/// `NewTerm(\[\])`. See the description of `NewTerm`.
627		EmptyTerm,
628		/// Internal error happened while trying to perform election.
629		ElectionError,
630		/// A member has been removed. This should always be followed by either `NewTerm` or
631		/// `EmptyTerm`.
632		MemberKicked { member: <T as frame_system::Config>::AccountId },
633		/// Someone has renounced their candidacy.
634		Renounced { candidate: <T as frame_system::Config>::AccountId },
635		/// A candidate was slashed by amount due to failing to obtain a seat as member or
636		/// runner-up.
637		///
638		/// Note that old members and runners-up are also candidates.
639		CandidateSlashed { candidate: <T as frame_system::Config>::AccountId, amount: BalanceOf<T> },
640		/// A seat holder was slashed by amount by being forcefully removed from the set.
641		SeatHolderSlashed {
642			seat_holder: <T as frame_system::Config>::AccountId,
643			amount: BalanceOf<T>,
644		},
645	}
646
647	#[pallet::error]
648	pub enum Error<T> {
649		/// Cannot vote when no candidates or members exist.
650		UnableToVote,
651		/// Must vote for at least one candidate.
652		NoVotes,
653		/// Cannot vote more than candidates.
654		TooManyVotes,
655		/// Cannot vote more than maximum allowed.
656		MaximumVotesExceeded,
657		/// Cannot vote with stake less than minimum balance.
658		LowBalance,
659		/// Voter can not pay voting bond.
660		UnableToPayBond,
661		/// Must be a voter.
662		MustBeVoter,
663		/// Duplicated candidate submission.
664		DuplicatedCandidate,
665		/// Too many candidates have been created.
666		TooManyCandidates,
667		/// Member cannot re-submit candidacy.
668		MemberSubmit,
669		/// Runner cannot re-submit candidacy.
670		RunnerUpSubmit,
671		/// Candidate does not have enough funds.
672		InsufficientCandidateFunds,
673		/// Not a member.
674		NotMember,
675		/// The provided count of number of candidates is incorrect.
676		InvalidWitnessData,
677		/// The provided count of number of votes is incorrect.
678		InvalidVoteCount,
679		/// The renouncing origin presented a wrong `Renouncing` parameter.
680		InvalidRenouncing,
681		/// Prediction regarding replacement after member removal is wrong.
682		InvalidReplacement,
683	}
684
685	/// The current elected members.
686	///
687	/// Invariant: Always sorted based on account id.
688	#[pallet::storage]
689	pub type Members<T: Config> =
690		StorageValue<_, Vec<SeatHolder<T::AccountId, BalanceOf<T>>>, ValueQuery>;
691
692	/// The current reserved runners-up.
693	///
694	/// Invariant: Always sorted based on rank (worse to best). Upon removal of a member, the
695	/// last (i.e. _best_) runner-up will be replaced.
696	#[pallet::storage]
697	pub type RunnersUp<T: Config> =
698		StorageValue<_, Vec<SeatHolder<T::AccountId, BalanceOf<T>>>, ValueQuery>;
699
700	/// The present candidate list. A current member or runner-up can never enter this vector
701	/// and is always implicitly assumed to be a candidate.
702	///
703	/// Second element is the deposit.
704	///
705	/// Invariant: Always sorted based on account id.
706	#[pallet::storage]
707	pub type Candidates<T: Config> = StorageValue<_, Vec<(T::AccountId, BalanceOf<T>)>, ValueQuery>;
708
709	/// The total number of vote rounds that have happened, excluding the upcoming one.
710	#[pallet::storage]
711	pub type ElectionRounds<T: Config> = StorageValue<_, u32, ValueQuery>;
712
713	/// Votes and locked stake of a particular voter.
714	///
715	/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash.
716	#[pallet::storage]
717	pub type Voting<T: Config> =
718		StorageMap<_, Twox64Concat, T::AccountId, Voter<T::AccountId, BalanceOf<T>>, ValueQuery>;
719
720	#[pallet::genesis_config]
721	#[derive(frame_support::DefaultNoBound)]
722	pub struct GenesisConfig<T: Config> {
723		pub members: Vec<(T::AccountId, BalanceOf<T>)>,
724	}
725
726	#[pallet::genesis_build]
727	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
728		fn build(&self) {
729			assert!(
730				self.members.len() as u32 <= T::DesiredMembers::get(),
731				"Cannot accept more than DesiredMembers genesis member",
732			);
733			let members = self
734				.members
735				.iter()
736				.map(|(ref member, ref stake)| {
737					// make sure they have enough stake.
738					assert!(
739						T::Currency::free_balance(member) >= *stake,
740						"Genesis member does not have enough stake.",
741					);
742
743					// Note: all members will only vote for themselves, hence they must be given
744					// exactly their own stake as total backing. Any sane election should behave as
745					// such. Nonetheless, stakes will be updated for term 1 onwards according to the
746					// election.
747					Members::<T>::mutate(|members| {
748						match members.binary_search_by(|m| m.who.cmp(member)) {
749							Ok(_) => {
750								panic!(
751									"Duplicate member in elections-phragmen genesis: {:?}",
752									member
753								)
754							},
755							Err(pos) => members.insert(
756								pos,
757								SeatHolder {
758									who: member.clone(),
759									stake: *stake,
760									deposit: Zero::zero(),
761								},
762							),
763						}
764					});
765
766					// set self-votes to make persistent. Genesis voters don't have any bond, nor do
767					// they have any lock. NOTE: this means that we will still try to remove a lock
768					// once this genesis voter is removed, and for now it is okay because
769					// remove_lock is noop if lock is not there.
770					Voting::<T>::insert(
771						&member,
772						Voter { votes: vec![member.clone()], stake: *stake, deposit: Zero::zero() },
773					);
774
775					member.clone()
776				})
777				.collect::<Vec<T::AccountId>>();
778
779			// report genesis members to upstream, if any.
780			T::InitializeMembers::initialize_members(&members);
781		}
782	}
783}
784
785impl<T: Config> Pallet<T> {
786	/// The deposit value of `count` votes.
787	fn deposit_of(count: usize) -> BalanceOf<T> {
788		T::VotingBondBase::get()
789			.saturating_add(T::VotingBondFactor::get().saturating_mul((count as u32).into()))
790	}
791
792	/// Attempts to remove a member `who`. If a runner-up exists, it is used as the replacement.
793	///
794	/// Returns:
795	///
796	/// - `Ok(true)` if the member was removed and a replacement was found.
797	/// - `Ok(false)` if the member was removed and but no replacement was found.
798	/// - `Err(_)` if the member was no found.
799	///
800	/// Both `Members` and `RunnersUp` storage is updated accordingly. `T::ChangeMember` is called
801	/// if needed. If `slash` is true, the deposit of the potentially removed member is slashed,
802	/// else, it is unreserved.
803	///
804	/// ### Note: Prime preservation
805	///
806	/// This function attempts to preserve the prime. If the removed members is not the prime, it is
807	/// set again via [`Config::ChangeMembers`].
808	fn remove_and_replace_member(who: &T::AccountId, slash: bool) -> Result<bool, DispatchError> {
809		// closure will return:
810		// - `Ok(Option(replacement))` if member was removed and replacement was replaced.
811		// - `Ok(None)` if member was removed but no replacement was found
812		// - `Err(_)` if who is not a member.
813		let maybe_replacement = Members::<T>::try_mutate::<_, Error<T>, _>(|members| {
814			let remove_index = members
815				.binary_search_by(|m| m.who.cmp(who))
816				.map_err(|_| Error::<T>::NotMember)?;
817			// we remove the member anyhow, regardless of having a runner-up or not.
818			let removed = members.remove(remove_index);
819
820			// slash or unreserve
821			if slash {
822				let (imbalance, _remainder) = T::Currency::slash_reserved(who, removed.deposit);
823				debug_assert!(_remainder.is_zero());
824				T::LoserCandidate::on_unbalanced(imbalance);
825				Self::deposit_event(Event::SeatHolderSlashed {
826					seat_holder: who.clone(),
827					amount: removed.deposit,
828				});
829			} else {
830				T::Currency::unreserve(who, removed.deposit);
831			}
832
833			let maybe_next_best = RunnersUp::<T>::mutate(|r| r.pop()).inspect(|next_best| {
834				// defensive-only: Members and runners-up are disjoint. This will always be err and
835				// give us an index to insert.
836				if let Err(index) = members.binary_search_by(|m| m.who.cmp(&next_best.who)) {
837					members.insert(index, next_best.clone());
838				} else {
839					// overlap. This can never happen. If so, it seems like our intended replacement
840					// is already a member, so not much more to do.
841					log::error!(target: LOG_TARGET, "A member seems to also be a runner-up.");
842				}
843			});
844			Ok(maybe_next_best)
845		})?;
846
847		let remaining_member_ids_sorted =
848			Members::<T>::get().into_iter().map(|x| x.who).collect::<Vec<_>>();
849		let outgoing = &[who.clone()];
850		let maybe_current_prime = T::ChangeMembers::get_prime();
851		let return_value = match maybe_replacement {
852			// member ids are already sorted, other two elements have one item.
853			Some(incoming) => {
854				T::ChangeMembers::change_members_sorted(
855					&[incoming.who],
856					outgoing,
857					&remaining_member_ids_sorted[..],
858				);
859				true
860			},
861			None => {
862				T::ChangeMembers::change_members_sorted(
863					&[],
864					outgoing,
865					&remaining_member_ids_sorted[..],
866				);
867				false
868			},
869		};
870
871		// if there was a prime before and they are not the one being removed, then set them
872		// again.
873		if let Some(current_prime) = maybe_current_prime {
874			if &current_prime != who {
875				T::ChangeMembers::set_prime(Some(current_prime));
876			}
877		}
878
879		Ok(return_value)
880	}
881
882	/// Check if `who` is a candidate. It returns the insert index if the element does not exists as
883	/// an error.
884	fn is_candidate(who: &T::AccountId) -> Result<(), usize> {
885		Candidates::<T>::get().binary_search_by(|c| c.0.cmp(who)).map(|_| ())
886	}
887
888	/// Check if `who` is a voter. It may or may not be a _current_ one.
889	fn is_voter(who: &T::AccountId) -> bool {
890		Voting::<T>::contains_key(who)
891	}
892
893	/// Check if `who` is currently an active member.
894	fn is_member(who: &T::AccountId) -> bool {
895		Members::<T>::get().binary_search_by(|m| m.who.cmp(who)).is_ok()
896	}
897
898	/// Check if `who` is currently an active runner-up.
899	fn is_runner_up(who: &T::AccountId) -> bool {
900		RunnersUp::<T>::get().iter().any(|r| &r.who == who)
901	}
902
903	/// Get the members' account ids.
904	pub(crate) fn members_ids() -> Vec<T::AccountId> {
905		Members::<T>::get().into_iter().map(|m| m.who).collect::<Vec<T::AccountId>>()
906	}
907
908	/// Get a concatenation of previous members and runners-up and their deposits.
909	///
910	/// These accounts are essentially treated as candidates.
911	fn implicit_candidates_with_deposit() -> Vec<(T::AccountId, BalanceOf<T>)> {
912		// invariant: these two are always without duplicates.
913		Members::<T>::get()
914			.into_iter()
915			.map(|m| (m.who, m.deposit))
916			.chain(RunnersUp::<T>::get().into_iter().map(|r| (r.who, r.deposit)))
917			.collect::<Vec<_>>()
918	}
919
920	/// Check if `votes` will correspond to a defunct voter. As no origin is part of the inputs,
921	/// this function does not check the origin at all.
922	///
923	/// O(NLogM) with M candidates and `who` having voted for `N` of them.
924	/// Reads Members, RunnersUp, Candidates and Voting(who) from database.
925	fn is_defunct_voter(votes: &[T::AccountId]) -> bool {
926		votes.iter().all(|v| {
927			!Self::is_member(v) && !Self::is_runner_up(v) && Self::is_candidate(v).is_err()
928		})
929	}
930
931	/// Remove a certain someone as a voter.
932	fn do_remove_voter(who: &T::AccountId) {
933		let Voter { deposit, .. } = Voting::<T>::take(who);
934
935		// remove storage, lock and unreserve.
936		T::Currency::remove_lock(T::PalletId::get(), who);
937
938		// NOTE: we could check the deposit amount before removing and skip if zero, but it will be
939		// a noop anyhow.
940		let _remainder = T::Currency::unreserve(who, deposit);
941		debug_assert!(_remainder.is_zero());
942	}
943
944	/// Run the phragmen election with all required side processes and state updates, if election
945	/// succeeds. Else, it will emit an `ElectionError` event.
946	///
947	/// Calls the appropriate [`ChangeMembers`] function variant internally.
948	fn do_phragmen() -> Weight {
949		let desired_seats = T::DesiredMembers::get() as usize;
950		let desired_runners_up = T::DesiredRunnersUp::get() as usize;
951		let num_to_elect = desired_runners_up + desired_seats;
952
953		let mut candidates_and_deposit = Candidates::<T>::get();
954		// add all the previous members and runners-up as candidates as well.
955		candidates_and_deposit.append(&mut Self::implicit_candidates_with_deposit());
956
957		if candidates_and_deposit.len().is_zero() {
958			Self::deposit_event(Event::EmptyTerm);
959			return T::DbWeight::get().reads(3);
960		}
961
962		// All of the new winners that come out of phragmen will thus have a deposit recorded.
963		let candidate_ids =
964			candidates_and_deposit.iter().map(|(x, _)| x).cloned().collect::<Vec<_>>();
965
966		// helper closures to deal with balance/stake.
967		let total_issuance = T::Currency::total_issuance();
968		let to_votes = |b: BalanceOf<T>| T::CurrencyToVote::to_vote(b, total_issuance);
969		let to_balance = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance);
970
971		let mut num_edges: u32 = 0;
972
973		let max_voters = <T as Config>::MaxVoters::get() as usize;
974		// used for prime election.
975		let mut voters_and_stakes = Vec::new();
976		match Voting::<T>::iter().try_for_each(|(voter, Voter { stake, votes, .. })| {
977			if voters_and_stakes.len() < max_voters {
978				voters_and_stakes.push((voter, stake, votes));
979				Ok(())
980			} else {
981				Err(())
982			}
983		}) {
984			Ok(_) => (),
985			Err(_) => {
986				log::error!(
987					target: LOG_TARGET,
988					"Failed to run election. Number of voters exceeded",
989				);
990				Self::deposit_event(Event::ElectionError);
991				return T::DbWeight::get().reads(3 + max_voters as u64);
992			},
993		}
994
995		// used for phragmen.
996		let voters_and_votes = voters_and_stakes
997			.iter()
998			.cloned()
999			.map(|(voter, stake, votes)| {
1000				num_edges = num_edges.saturating_add(votes.len() as u32);
1001				(voter, to_votes(stake), votes)
1002			})
1003			.collect::<Vec<_>>();
1004
1005		let weight_candidates = candidates_and_deposit.len() as u32;
1006		let weight_voters = voters_and_votes.len() as u32;
1007		let weight_edges = num_edges;
1008		let _ =
1009			sp_npos_elections::seq_phragmen(num_to_elect, candidate_ids, voters_and_votes, None)
1010				.map(|ElectionResult::<T::AccountId, Perbill> { winners, assignments: _ }| {
1011					// this is already sorted by id.
1012					let old_members_ids_sorted = Members::<T>::take()
1013						.into_iter()
1014						.map(|m| m.who)
1015						.collect::<Vec<T::AccountId>>();
1016					// this one needs a sort by id.
1017					let mut old_runners_up_ids_sorted = RunnersUp::<T>::take()
1018						.into_iter()
1019						.map(|r| r.who)
1020						.collect::<Vec<T::AccountId>>();
1021					old_runners_up_ids_sorted.sort();
1022
1023					// filter out those who end up with no backing stake.
1024					let mut new_set_with_stake = winners
1025						.into_iter()
1026						.filter_map(
1027							|(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) },
1028						)
1029						.collect::<Vec<(T::AccountId, BalanceOf<T>)>>();
1030
1031					// OPTIMIZATION NOTE: we could bail out here if `new_set.len() == 0`. There
1032					// isn't much left to do. Yet, re-arranging the code would require duplicating
1033					// the slashing of exposed candidates, cleaning any previous members, and so on.
1034					// For now, in favor of readability and veracity, we keep it simple.
1035
1036					// split new set into winners and runners up.
1037					let split_point = desired_seats.min(new_set_with_stake.len());
1038					let mut new_members_sorted_by_id =
1039						new_set_with_stake.drain(..split_point).collect::<Vec<_>>();
1040					new_members_sorted_by_id.sort_by(|i, j| i.0.cmp(&j.0));
1041
1042					// all the rest will be runners-up
1043					new_set_with_stake.reverse();
1044					let new_runners_up_sorted_by_rank = new_set_with_stake;
1045					let mut new_runners_up_ids_sorted = new_runners_up_sorted_by_rank
1046						.iter()
1047						.map(|(r, _)| r.clone())
1048						.collect::<Vec<_>>();
1049					new_runners_up_ids_sorted.sort();
1050
1051					// Now we select a prime member using a [Borda
1052					// count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for
1053					// that new member by a multiplier based on the order of the votes. i.e. the
1054					// first person a voter votes for gets a 16x multiplier, the next person gets a
1055					// 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter` = 16)
1056					// Ballots stored before a reduction of `T::MaxVotesPerVoter` can be longer
1057					// than the current bound, so positions past it get a zero multiplier.
1058					let mut prime_votes = new_members_sorted_by_id
1059						.iter()
1060						.map(|c| (&c.0, BalanceOf::<T>::zero()))
1061						.collect::<Vec<_>>();
1062					for (_, stake, votes) in voters_and_stakes.into_iter() {
1063						for (vote_multiplier, who) in
1064							votes.iter().enumerate().map(|(vote_position, who)| {
1065								(
1066									(T::MaxVotesPerVoter::get() as usize)
1067										.saturating_sub(vote_position) as u32,
1068									who,
1069								)
1070							}) {
1071							if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) {
1072								prime_votes[i].1 = prime_votes[i]
1073									.1
1074									.saturating_add(stake.saturating_mul(vote_multiplier.into()));
1075							}
1076						}
1077					}
1078					// We then select the new member with the highest weighted stake. In the case of
1079					// a tie, the last person in the list with the tied score is selected. This is
1080					// the person with the "highest" account id based on the sort above.
1081					let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone());
1082
1083					// new_members_sorted_by_id is sorted by account id.
1084					let new_members_ids_sorted = new_members_sorted_by_id
1085						.iter()
1086						.map(|(m, _)| m.clone())
1087						.collect::<Vec<T::AccountId>>();
1088
1089					// report member changes. We compute diff because we need the outgoing list.
1090					let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted(
1091						&new_members_ids_sorted,
1092						&old_members_ids_sorted,
1093					);
1094					T::ChangeMembers::change_members_sorted(
1095						&incoming,
1096						&outgoing,
1097						&new_members_ids_sorted,
1098					);
1099					T::ChangeMembers::set_prime(prime);
1100
1101					// All candidates/members/runners-up who are no longer retaining a position as a
1102					// seat holder will lose their bond.
1103					candidates_and_deposit.iter().for_each(|(c, d)| {
1104						if new_members_ids_sorted.binary_search(c).is_err() &&
1105							new_runners_up_ids_sorted.binary_search(c).is_err()
1106						{
1107							let (imbalance, _) = T::Currency::slash_reserved(c, *d);
1108							T::LoserCandidate::on_unbalanced(imbalance);
1109							Self::deposit_event(Event::CandidateSlashed {
1110								candidate: c.clone(),
1111								amount: *d,
1112							});
1113						}
1114					});
1115
1116					// write final values to storage.
1117					let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf<T> {
1118						// defensive-only. This closure is used against the new members and new
1119						// runners-up, both of which are phragmen winners and thus must have
1120						// deposit.
1121						candidates_and_deposit
1122							.iter()
1123							.find_map(|(c, d)| if c == x { Some(*d) } else { None })
1124							.defensive_unwrap_or_default()
1125					};
1126					// fetch deposits from the one recorded one. This will make sure that a
1127					// candidate who submitted candidacy before a change to candidacy deposit will
1128					// have the correct amount recorded.
1129					Members::<T>::put(
1130						new_members_sorted_by_id
1131							.iter()
1132							.map(|(who, stake)| SeatHolder {
1133								deposit: deposit_of_candidate(who),
1134								who: who.clone(),
1135								stake: *stake,
1136							})
1137							.collect::<Vec<_>>(),
1138					);
1139					RunnersUp::<T>::put(
1140						new_runners_up_sorted_by_rank
1141							.into_iter()
1142							.map(|(who, stake)| SeatHolder {
1143								deposit: deposit_of_candidate(&who),
1144								who,
1145								stake,
1146							})
1147							.collect::<Vec<_>>(),
1148					);
1149
1150					// clean candidates.
1151					Candidates::<T>::kill();
1152
1153					Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id });
1154					ElectionRounds::<T>::mutate(|v| *v += 1);
1155				})
1156				.map_err(|e| {
1157					log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,);
1158					Self::deposit_event(Event::ElectionError);
1159				});
1160
1161		T::WeightInfo::election_phragmen(weight_candidates, weight_voters, weight_edges)
1162	}
1163}
1164
1165impl<T: Config> Contains<T::AccountId> for Pallet<T> {
1166	fn contains(who: &T::AccountId) -> bool {
1167		Self::is_member(who)
1168	}
1169}
1170
1171impl<T: Config> SortedMembers<T::AccountId> for Pallet<T> {
1172	fn contains(who: &T::AccountId) -> bool {
1173		Self::is_member(who)
1174	}
1175
1176	fn sorted_members() -> Vec<T::AccountId> {
1177		Self::members_ids()
1178	}
1179
1180	// A special function to populate members in this pallet for passing Origin
1181	// checks in runtime benchmarking.
1182	#[cfg(feature = "runtime-benchmarks")]
1183	fn add(who: &T::AccountId) {
1184		Members::<T>::mutate(|members| match members.binary_search_by(|m| m.who.cmp(who)) {
1185			Ok(_) => (),
1186			Err(pos) => {
1187				let s = SeatHolder {
1188					who: who.clone(),
1189					stake: Default::default(),
1190					deposit: Default::default(),
1191				};
1192				members.insert(pos, s)
1193			},
1194		})
1195	}
1196}
1197
1198impl<T: Config> ContainsLengthBound for Pallet<T> {
1199	fn min_len() -> usize {
1200		0
1201	}
1202
1203	/// Implementation uses a parameter type so calling is cost-free.
1204	fn max_len() -> usize {
1205		T::DesiredMembers::get() as usize
1206	}
1207}
1208
1209#[cfg(any(feature = "try-runtime", test))]
1210impl<T: Config> Pallet<T> {
1211	fn do_try_state() -> Result<(), TryRuntimeError> {
1212		Self::try_state_members()?;
1213		Self::try_state_runners_up()?;
1214		Self::try_state_candidates()?;
1215		Self::try_state_candidates_runners_up_disjoint()?;
1216		Self::try_state_members_disjoint()?;
1217		Self::try_state_members_approval_stake()
1218	}
1219
1220	/// [`Members`] state checks. Invariants:
1221	///  - Members are always sorted based on account ID.
1222	fn try_state_members() -> Result<(), TryRuntimeError> {
1223		let mut members = Members::<T>::get().clone();
1224		members.sort_by_key(|m| m.who.clone());
1225
1226		if Members::<T>::get() == members {
1227			Ok(())
1228		} else {
1229			Err("try_state checks: Members must be always sorted by account ID".into())
1230		}
1231	}
1232
1233	// [`RunnersUp`] state checks. Invariants:
1234	//  - Elements are sorted based on weight (worst to best).
1235	fn try_state_runners_up() -> Result<(), TryRuntimeError> {
1236		let mut sorted = RunnersUp::<T>::get();
1237		// worst stake first
1238		sorted.sort_by(|a, b| a.stake.cmp(&b.stake));
1239
1240		if RunnersUp::<T>::get() == sorted {
1241			Ok(())
1242		} else {
1243			Err("try_state checks: Runners Up must always be sorted by stake (worst to best)"
1244				.into())
1245		}
1246	}
1247
1248	// [`Candidates`] state checks. Invariants:
1249	//  - Always sorted based on account ID.
1250	fn try_state_candidates() -> Result<(), TryRuntimeError> {
1251		let mut candidates = Candidates::<T>::get().clone();
1252		candidates.sort_by_key(|(c, _)| c.clone());
1253
1254		if Candidates::<T>::get() == candidates {
1255			Ok(())
1256		} else {
1257			Err("try_state checks: Candidates must be always sorted by account ID".into())
1258		}
1259	}
1260	// [`Candidates`] and [`RunnersUp`] state checks. Invariants:
1261	//  - Candidates and runners-ups sets are disjoint.
1262	fn try_state_candidates_runners_up_disjoint() -> Result<(), TryRuntimeError> {
1263		match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) {
1264			true => Err("Candidates and runners up sets should always be disjoint".into()),
1265			false => Ok(()),
1266		}
1267	}
1268
1269	// [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants:
1270	//  - Members and candidates sets are disjoint;
1271	//  - Members and runners-ups sets are disjoint.
1272	fn try_state_members_disjoint() -> Result<(), TryRuntimeError> {
1273		match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) &&
1274			Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids())
1275		{
1276			true => {
1277				Err("Members set should be disjoint from candidates and runners-up sets".into())
1278			},
1279			false => Ok(()),
1280		}
1281	}
1282
1283	// [`Members`], [`RunnersUp`] and approval stake state checks. Invariants:
1284	// - Selected members should have approval stake;
1285	// - Selected RunnersUp should have approval stake.
1286	fn try_state_members_approval_stake() -> Result<(), TryRuntimeError> {
1287		match Members::<T>::get()
1288			.iter()
1289			.chain(RunnersUp::<T>::get().iter())
1290			.all(|s| s.stake != BalanceOf::<T>::zero())
1291		{
1292			true => Ok(()),
1293			false => Err("Members and RunnersUp must have approval stake".into()),
1294		}
1295	}
1296
1297	fn intersects<P: PartialEq>(a: &[P], b: &[P]) -> bool {
1298		a.iter().any(|e| b.contains(e))
1299	}
1300
1301	fn candidates_ids() -> Vec<T::AccountId> {
1302		Candidates::<T>::get().iter().map(|(x, _)| x).cloned().collect::<Vec<_>>()
1303	}
1304
1305	fn runners_up_ids() -> Vec<T::AccountId> {
1306		RunnersUp::<T>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1307	}
1308}
1309
1310#[cfg(test)]
1311mod tests {
1312	use super::*;
1313	use crate as elections_phragmen;
1314	use frame_support::{
1315		assert_noop, assert_ok, derive_impl, dispatch::DispatchResultWithPostInfo, parameter_types,
1316		traits::OnInitialize,
1317	};
1318	use frame_system::ensure_signed;
1319	use sp_runtime::{testing::Header, BuildStorage};
1320	use substrate_test_utils::assert_eq_uvec;
1321
1322	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
1323	impl frame_system::Config for Test {
1324		type Block = Block;
1325		type AccountData = pallet_balances::AccountData<u64>;
1326	}
1327
1328	#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
1329	impl pallet_balances::Config for Test {
1330		type AccountStore = frame_system::Pallet<Test>;
1331	}
1332
1333	frame_support::parameter_types! {
1334		pub static VotingBondBase: u64 = 2;
1335		pub static VotingBondFactor: u64 = 0;
1336		pub static CandidacyBond: u64 = 3;
1337		pub static DesiredMembers: u32 = 2;
1338		pub static DesiredRunnersUp: u32 = 0;
1339		pub static TermDuration: u64 = 5;
1340		pub static MaxCandidates: u32 = 100;
1341		pub static MaxVotesPerVoter: u32 = 16;
1342		pub static Members: Vec<u64> = vec![];
1343		pub static Prime: Option<u64> = None;
1344	}
1345
1346	pub struct TestChangeMembers;
1347	impl ChangeMembers<u64> for TestChangeMembers {
1348		fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) {
1349			// new, incoming, outgoing must be sorted.
1350			let mut new_sorted = new.to_vec();
1351			new_sorted.sort();
1352			assert_eq!(new, &new_sorted[..]);
1353
1354			let mut incoming_sorted = incoming.to_vec();
1355			incoming_sorted.sort();
1356			assert_eq!(incoming, &incoming_sorted[..]);
1357
1358			let mut outgoing_sorted = outgoing.to_vec();
1359			outgoing_sorted.sort();
1360			assert_eq!(outgoing, &outgoing_sorted[..]);
1361
1362			// incoming and outgoing must be disjoint
1363			for x in incoming.iter() {
1364				assert!(outgoing.binary_search(x).is_err());
1365			}
1366
1367			let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec());
1368			old_plus_incoming.extend_from_slice(incoming);
1369			old_plus_incoming.sort();
1370
1371			let mut new_plus_outgoing = new.to_vec();
1372			new_plus_outgoing.extend_from_slice(outgoing);
1373			new_plus_outgoing.sort();
1374
1375			assert_eq!(old_plus_incoming, new_plus_outgoing, "change members call is incorrect!");
1376
1377			MEMBERS.with(|m| *m.borrow_mut() = new.to_vec());
1378			PRIME.with(|p| *p.borrow_mut() = None);
1379		}
1380
1381		fn set_prime(who: Option<u64>) {
1382			PRIME.with(|p| *p.borrow_mut() = who);
1383		}
1384
1385		fn get_prime() -> Option<u64> {
1386			PRIME.with(|p| *p.borrow())
1387		}
1388	}
1389
1390	parameter_types! {
1391		pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect";
1392		pub const PhragmenMaxVoters: u32 = 1000;
1393	}
1394
1395	impl Config for Test {
1396		type PalletId = ElectionsPhragmenPalletId;
1397		type RuntimeEvent = RuntimeEvent;
1398		type Currency = Balances;
1399		type CurrencyToVote = ();
1400		type ChangeMembers = TestChangeMembers;
1401		type InitializeMembers = ();
1402		type CandidacyBond = CandidacyBond;
1403		type VotingBondBase = VotingBondBase;
1404		type VotingBondFactor = VotingBondFactor;
1405		type TermDuration = TermDuration;
1406		type DesiredMembers = DesiredMembers;
1407		type DesiredRunnersUp = DesiredRunnersUp;
1408		type LoserCandidate = ();
1409		type KickedMember = ();
1410		type WeightInfo = ();
1411		type MaxVoters = PhragmenMaxVoters;
1412		type MaxVotesPerVoter = MaxVotesPerVoter;
1413		type MaxCandidates = MaxCandidates;
1414	}
1415
1416	pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
1417	pub type UncheckedExtrinsic =
1418		sp_runtime::generic::UncheckedExtrinsic<u32, RuntimeCall, u64, ()>;
1419
1420	frame_support::construct_runtime!(
1421		pub enum Test
1422		{
1423			System: frame_system,
1424			Balances: pallet_balances,
1425			Elections: elections_phragmen,
1426		}
1427	);
1428
1429	pub struct ExtBuilder {
1430		balance_factor: u64,
1431		genesis_members: Vec<(u64, u64)>,
1432	}
1433
1434	impl Default for ExtBuilder {
1435		fn default() -> Self {
1436			Self { balance_factor: 1, genesis_members: vec![] }
1437		}
1438	}
1439
1440	impl ExtBuilder {
1441		pub fn voter_bond(self, bond: u64) -> Self {
1442			VOTING_BOND_BASE.with(|v| *v.borrow_mut() = bond);
1443			self
1444		}
1445		pub fn voter_bond_factor(self, bond: u64) -> Self {
1446			VOTING_BOND_FACTOR.with(|v| *v.borrow_mut() = bond);
1447			self
1448		}
1449		pub fn desired_runners_up(self, count: u32) -> Self {
1450			DESIRED_RUNNERS_UP.with(|v| *v.borrow_mut() = count);
1451			self
1452		}
1453		pub fn term_duration(self, duration: u64) -> Self {
1454			TERM_DURATION.with(|v| *v.borrow_mut() = duration);
1455			self
1456		}
1457		pub fn genesis_members(mut self, members: Vec<(u64, u64)>) -> Self {
1458			MEMBERS.with(|m| *m.borrow_mut() = members.iter().map(|(m, _)| *m).collect::<Vec<_>>());
1459			self.genesis_members = members;
1460			self
1461		}
1462		pub fn max_candidates(self, count: u32) -> Self {
1463			MAX_CANDIDATES.with(|m| *m.borrow_mut() = count);
1464			self
1465		}
1466		pub fn desired_members(self, count: u32) -> Self {
1467			DESIRED_MEMBERS.with(|m| *m.borrow_mut() = count);
1468			self
1469		}
1470		pub fn balance_factor(mut self, factor: u64) -> Self {
1471			self.balance_factor = factor;
1472			self
1473		}
1474		pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1475			sp_tracing::try_init_simple();
1476			MEMBERS.with(|m| {
1477				*m.borrow_mut() = self.genesis_members.iter().map(|(m, _)| *m).collect::<Vec<_>>()
1478			});
1479			let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig {
1480				system: frame_system::GenesisConfig::default(),
1481				balances: pallet_balances::GenesisConfig::<Test> {
1482					balances: vec![
1483						(1, 10 * self.balance_factor),
1484						(2, 20 * self.balance_factor),
1485						(3, 30 * self.balance_factor),
1486						(4, 40 * self.balance_factor),
1487						(5, 50 * self.balance_factor),
1488						(6, 60 * self.balance_factor),
1489					],
1490					..Default::default()
1491				},
1492				elections: elections_phragmen::GenesisConfig::<Test> {
1493					members: self.genesis_members,
1494				},
1495			}
1496			.build_storage()
1497			.unwrap()
1498			.into();
1499			ext.execute_with(pre_conditions);
1500			ext.execute_with(test);
1501
1502			#[cfg(feature = "try-runtime")]
1503			ext.execute_with(|| {
1504				assert_ok!(<Elections as frame_support::traits::Hooks<u64>>::try_state(
1505					System::block_number()
1506				));
1507			});
1508		}
1509	}
1510
1511	fn candidate_ids() -> Vec<u64> {
1512		Candidates::<Test>::get().into_iter().map(|(c, _)| c).collect::<Vec<_>>()
1513	}
1514
1515	fn candidate_deposit(who: &u64) -> u64 {
1516		Candidates::<Test>::get()
1517			.into_iter()
1518			.find_map(|(c, d)| if c == *who { Some(d) } else { None })
1519			.unwrap_or_default()
1520	}
1521
1522	fn voter_deposit(who: &u64) -> u64 {
1523		Voting::<Test>::get(who).deposit
1524	}
1525
1526	fn runners_up_ids() -> Vec<u64> {
1527		RunnersUp::<Test>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1528	}
1529
1530	fn members_ids() -> Vec<u64> {
1531		Elections::members_ids()
1532	}
1533
1534	fn members_and_stake() -> Vec<(u64, u64)> {
1535		elections_phragmen::Members::<Test>::get()
1536			.into_iter()
1537			.map(|m| (m.who, m.stake))
1538			.collect::<Vec<_>>()
1539	}
1540
1541	fn runners_up_and_stake() -> Vec<(u64, u64)> {
1542		RunnersUp::<Test>::get()
1543			.into_iter()
1544			.map(|r| (r.who, r.stake))
1545			.collect::<Vec<_>>()
1546	}
1547
1548	fn all_voters() -> Vec<u64> {
1549		Voting::<Test>::iter().map(|(v, _)| v).collect::<Vec<u64>>()
1550	}
1551
1552	fn balances(who: &u64) -> (u64, u64) {
1553		(Balances::free_balance(who), Balances::reserved_balance(who))
1554	}
1555
1556	fn has_lock(who: &u64) -> u64 {
1557		Balances::locks(who)
1558			.get(0)
1559			.cloned()
1560			.map(|lock| {
1561				assert_eq!(lock.id, ElectionsPhragmenPalletId::get());
1562				lock.amount
1563			})
1564			.unwrap_or_default()
1565	}
1566
1567	fn locked_stake_of(who: &u64) -> u64 {
1568		Voting::<Test>::get(who).stake
1569	}
1570
1571	fn pre_conditions() {
1572		System::set_block_number(1);
1573		Elections::do_try_state().unwrap();
1574	}
1575
1576	fn submit_candidacy(origin: RuntimeOrigin) -> sp_runtime::DispatchResult {
1577		Elections::submit_candidacy(origin, Candidates::<Test>::get().len() as u32)
1578	}
1579
1580	fn vote(origin: RuntimeOrigin, votes: Vec<u64>, stake: u64) -> DispatchResultWithPostInfo {
1581		// historical note: helper function was created in a period of time in which the API of vote
1582		// call was changing. Currently it is a wrapper for the original call and does not do much.
1583		// Nonetheless, totally harmless.
1584		ensure_signed(origin.clone()).expect("vote origin must be signed");
1585		Elections::vote(origin, votes, stake)
1586	}
1587
1588	fn votes_of(who: &u64) -> Vec<u64> {
1589		Voting::<Test>::get(who).votes
1590	}
1591
1592	#[test]
1593	fn params_should_work() {
1594		ExtBuilder::default().build_and_execute(|| {
1595			assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1596			assert_eq!(<Test as Config>::DesiredRunnersUp::get(), 0);
1597			assert_eq!(<Test as Config>::VotingBondBase::get(), 2);
1598			assert_eq!(<Test as Config>::VotingBondFactor::get(), 0);
1599			assert_eq!(<Test as Config>::CandidacyBond::get(), 3);
1600			assert_eq!(<Test as Config>::TermDuration::get(), 5);
1601			assert_eq!(ElectionRounds::<Test>::get(), 0);
1602
1603			assert!(elections_phragmen::Members::<Test>::get().is_empty());
1604			assert!(RunnersUp::<Test>::get().is_empty());
1605
1606			assert!(candidate_ids().is_empty());
1607			assert_eq!(Candidates::<Test>::decode_len(), None);
1608			assert!(Elections::is_candidate(&1).is_err());
1609
1610			assert!(all_voters().is_empty());
1611			assert!(votes_of(&1).is_empty());
1612		});
1613	}
1614
1615	#[test]
1616	fn genesis_members_should_work() {
1617		ExtBuilder::default()
1618			.genesis_members(vec![(1, 10), (2, 20)])
1619			.build_and_execute(|| {
1620				System::set_block_number(1);
1621				assert_eq!(
1622					elections_phragmen::Members::<Test>::get(),
1623					vec![
1624						SeatHolder { who: 1, stake: 10, deposit: 0 },
1625						SeatHolder { who: 2, stake: 20, deposit: 0 }
1626					]
1627				);
1628
1629				assert_eq!(
1630					Voting::<Test>::get(1),
1631					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1632				);
1633				assert_eq!(
1634					Voting::<Test>::get(2),
1635					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1636				);
1637
1638				// they will persist since they have self vote.
1639				System::set_block_number(5);
1640				Elections::on_initialize(System::block_number());
1641
1642				assert_eq!(members_ids(), vec![1, 2]);
1643			})
1644	}
1645
1646	#[test]
1647	fn genesis_voters_can_remove_lock() {
1648		ExtBuilder::default()
1649			.genesis_members(vec![(1, 10), (2, 20)])
1650			.build_and_execute(|| {
1651				System::set_block_number(1);
1652
1653				assert_eq!(
1654					Voting::<Test>::get(1),
1655					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1656				);
1657				assert_eq!(
1658					Voting::<Test>::get(2),
1659					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1660				);
1661
1662				assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(1)));
1663				assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1664
1665				assert_eq!(Voting::<Test>::get(1), Default::default());
1666				assert_eq!(Voting::<Test>::get(2), Default::default());
1667			})
1668	}
1669
1670	#[test]
1671	fn genesis_members_unsorted_should_work() {
1672		ExtBuilder::default()
1673			.genesis_members(vec![(2, 20), (1, 10)])
1674			.build_and_execute(|| {
1675				System::set_block_number(1);
1676				assert_eq!(
1677					elections_phragmen::Members::<Test>::get(),
1678					vec![
1679						SeatHolder { who: 1, stake: 10, deposit: 0 },
1680						SeatHolder { who: 2, stake: 20, deposit: 0 },
1681					]
1682				);
1683
1684				assert_eq!(
1685					Voting::<Test>::get(1),
1686					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1687				);
1688				assert_eq!(
1689					Voting::<Test>::get(2),
1690					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1691				);
1692
1693				// they will persist since they have self vote.
1694				System::set_block_number(5);
1695				Elections::on_initialize(System::block_number());
1696
1697				assert_eq!(members_ids(), vec![1, 2]);
1698			})
1699	}
1700
1701	#[test]
1702	#[should_panic = "Genesis member does not have enough stake"]
1703	fn genesis_members_cannot_over_stake_0() {
1704		// 10 cannot lock 20 as their stake and extra genesis will panic.
1705		ExtBuilder::default()
1706			.genesis_members(vec![(1, 20), (2, 20)])
1707			.build_and_execute(|| {});
1708	}
1709
1710	#[test]
1711	#[should_panic = "Duplicate member in elections-phragmen genesis: 2"]
1712	fn genesis_members_cannot_be_duplicate() {
1713		ExtBuilder::default()
1714			.desired_members(3)
1715			.genesis_members(vec![(1, 10), (2, 10), (2, 10)])
1716			.build_and_execute(|| {});
1717	}
1718
1719	#[test]
1720	#[should_panic = "Cannot accept more than DesiredMembers genesis member"]
1721	fn genesis_members_cannot_too_many() {
1722		ExtBuilder::default()
1723			.genesis_members(vec![(1, 10), (2, 10), (3, 30)])
1724			.desired_members(2)
1725			.build_and_execute(|| {});
1726	}
1727
1728	#[test]
1729	fn term_duration_zero_is_passive() {
1730		ExtBuilder::default().term_duration(0).build_and_execute(|| {
1731			assert_eq!(<Test as Config>::TermDuration::get(), 0);
1732			assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1733			assert_eq!(ElectionRounds::<Test>::get(), 0);
1734
1735			assert!(members_ids().is_empty());
1736			assert!(RunnersUp::<Test>::get().is_empty());
1737			assert!(candidate_ids().is_empty());
1738
1739			System::set_block_number(5);
1740			Elections::on_initialize(System::block_number());
1741
1742			assert!(members_ids().is_empty());
1743			assert!(RunnersUp::<Test>::get().is_empty());
1744			assert!(candidate_ids().is_empty());
1745		});
1746	}
1747
1748	#[test]
1749	fn simple_candidate_submission_should_work() {
1750		ExtBuilder::default().build_and_execute(|| {
1751			assert_eq!(candidate_ids(), Vec::<u64>::new());
1752			assert!(Elections::is_candidate(&1).is_err());
1753			assert!(Elections::is_candidate(&2).is_err());
1754
1755			assert_eq!(balances(&1), (10, 0));
1756			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1757			assert_eq!(balances(&1), (7, 3));
1758
1759			assert_eq!(candidate_ids(), vec![1]);
1760
1761			assert!(Elections::is_candidate(&1).is_ok());
1762			assert!(Elections::is_candidate(&2).is_err());
1763
1764			assert_eq!(balances(&2), (20, 0));
1765			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1766			assert_eq!(balances(&2), (17, 3));
1767
1768			assert_eq!(candidate_ids(), vec![1, 2]);
1769
1770			assert!(Elections::is_candidate(&1).is_ok());
1771			assert!(Elections::is_candidate(&2).is_ok());
1772
1773			assert_eq!(candidate_deposit(&1), 3);
1774			assert_eq!(candidate_deposit(&2), 3);
1775			assert_eq!(candidate_deposit(&3), 0);
1776		});
1777	}
1778
1779	#[test]
1780	fn updating_candidacy_bond_works() {
1781		ExtBuilder::default().build_and_execute(|| {
1782			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1783			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
1784			assert_eq!(Candidates::<Test>::get(), vec![(5, 3)]);
1785
1786			// a runtime upgrade changes the bond.
1787			CANDIDACY_BOND.with(|v| *v.borrow_mut() = 4);
1788
1789			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1790			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
1791			assert_eq!(Candidates::<Test>::get(), vec![(4, 4), (5, 3)]);
1792
1793			// once elected, they each hold their candidacy bond, no more.
1794			System::set_block_number(5);
1795			Elections::on_initialize(System::block_number());
1796
1797			assert_eq!(balances(&4), (34, 6));
1798			assert_eq!(balances(&5), (45, 5));
1799			assert_eq!(
1800				elections_phragmen::Members::<Test>::get(),
1801				vec![
1802					SeatHolder { who: 4, stake: 34, deposit: 4 },
1803					SeatHolder { who: 5, stake: 45, deposit: 3 },
1804				]
1805			);
1806		})
1807	}
1808
1809	#[test]
1810	fn candidates_are_always_sorted() {
1811		ExtBuilder::default().build_and_execute(|| {
1812			assert_eq!(candidate_ids(), Vec::<u64>::new());
1813
1814			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1815			assert_eq!(candidate_ids(), vec![3]);
1816			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1817			assert_eq!(candidate_ids(), vec![1, 3]);
1818			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1819			assert_eq!(candidate_ids(), vec![1, 2, 3]);
1820			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1821			assert_eq!(candidate_ids(), vec![1, 2, 3, 4]);
1822		});
1823	}
1824
1825	#[test]
1826	fn candidate_submission_is_capped_at_max_candidates() {
1827		ExtBuilder::default().max_candidates(2).build_and_execute(|| {
1828			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1829			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1830			assert_eq!(candidate_ids(), vec![1, 2]);
1831
1832			// The candidate list is full; reject further candidacies.
1833			assert_noop!(
1834				submit_candidacy(RuntimeOrigin::signed(3)),
1835				Error::<Test>::TooManyCandidates
1836			);
1837			assert_eq!(candidate_ids(), vec![1, 2]);
1838		});
1839	}
1840
1841	#[test]
1842	fn dupe_candidate_submission_should_not_work() {
1843		ExtBuilder::default().build_and_execute(|| {
1844			assert_eq!(candidate_ids(), Vec::<u64>::new());
1845			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1846			assert_eq!(candidate_ids(), vec![1]);
1847			assert_noop!(
1848				submit_candidacy(RuntimeOrigin::signed(1)),
1849				Error::<Test>::DuplicatedCandidate
1850			);
1851		});
1852	}
1853
1854	#[test]
1855	fn member_candidacy_submission_should_not_work() {
1856		// critically important to make sure that outgoing candidates and losers are not mixed up.
1857		ExtBuilder::default().build_and_execute(|| {
1858			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1859			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1860
1861			System::set_block_number(5);
1862			Elections::on_initialize(System::block_number());
1863
1864			assert_eq!(members_ids(), vec![5]);
1865			assert!(RunnersUp::<Test>::get().is_empty());
1866			assert!(candidate_ids().is_empty());
1867
1868			assert_noop!(submit_candidacy(RuntimeOrigin::signed(5)), Error::<Test>::MemberSubmit);
1869		});
1870	}
1871
1872	#[test]
1873	fn runner_candidate_submission_should_not_work() {
1874		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
1875			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1876			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1877			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1878
1879			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 20));
1880			assert_ok!(vote(RuntimeOrigin::signed(1), vec![3], 10));
1881
1882			System::set_block_number(5);
1883			Elections::on_initialize(System::block_number());
1884
1885			assert_eq!(members_ids(), vec![4, 5]);
1886			assert_eq!(runners_up_ids(), vec![3]);
1887
1888			assert_noop!(submit_candidacy(RuntimeOrigin::signed(3)), Error::<Test>::RunnerUpSubmit);
1889		});
1890	}
1891
1892	#[test]
1893	fn poor_candidate_submission_should_not_work() {
1894		ExtBuilder::default().build_and_execute(|| {
1895			assert_eq!(candidate_ids(), Vec::<u64>::new());
1896			assert_noop!(
1897				submit_candidacy(RuntimeOrigin::signed(7)),
1898				Error::<Test>::InsufficientCandidateFunds,
1899			);
1900		});
1901	}
1902
1903	#[test]
1904	fn simple_voting_should_work() {
1905		ExtBuilder::default().build_and_execute(|| {
1906			assert_eq!(candidate_ids(), Vec::<u64>::new());
1907			assert_eq!(balances(&2), (20, 0));
1908
1909			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1910			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1911
1912			assert_eq!(balances(&2), (18, 2));
1913			assert_eq!(has_lock(&2), 18);
1914		});
1915	}
1916
1917	#[test]
1918	fn can_vote_with_custom_stake() {
1919		ExtBuilder::default().build_and_execute(|| {
1920			assert_eq!(candidate_ids(), Vec::<u64>::new());
1921			assert_eq!(balances(&2), (20, 0));
1922
1923			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1924			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 12));
1925
1926			assert_eq!(balances(&2), (18, 2));
1927			assert_eq!(has_lock(&2), 12);
1928		});
1929	}
1930
1931	#[test]
1932	fn can_update_votes_and_stake() {
1933		ExtBuilder::default().build_and_execute(|| {
1934			assert_eq!(balances(&2), (20, 0));
1935
1936			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1937			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1938			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1939
1940			// User only locks up to their free balance.
1941			assert_eq!(balances(&2), (18, 2));
1942			assert_eq!(has_lock(&2), 18);
1943			assert_eq!(locked_stake_of(&2), 18);
1944
1945			// can update; different stake; different lock and reserve.
1946			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1947			assert_eq!(balances(&2), (18, 2));
1948			assert_eq!(has_lock(&2), 15);
1949			assert_eq!(locked_stake_of(&2), 15);
1950		});
1951	}
1952
1953	#[test]
1954	fn updated_voting_bond_works() {
1955		ExtBuilder::default().build_and_execute(|| {
1956			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1957
1958			assert_eq!(balances(&2), (20, 0));
1959			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 5));
1960			assert_eq!(balances(&2), (18, 2));
1961			assert_eq!(voter_deposit(&2), 2);
1962
1963			// a runtime upgrade lowers the voting bond to 1. This guy still un-reserves 2 when
1964			// leaving.
1965			VOTING_BOND_BASE.with(|v| *v.borrow_mut() = 1);
1966
1967			// proof that bond changed.
1968			assert_eq!(balances(&1), (10, 0));
1969			assert_ok!(vote(RuntimeOrigin::signed(1), vec![5], 5));
1970			assert_eq!(balances(&1), (9, 1));
1971			assert_eq!(voter_deposit(&1), 1);
1972
1973			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1974			assert_eq!(balances(&2), (20, 0));
1975		})
1976	}
1977
1978	#[test]
1979	fn voting_reserves_bond_per_vote() {
1980		ExtBuilder::default().voter_bond_factor(1).build_and_execute(|| {
1981			assert_eq!(balances(&2), (20, 0));
1982
1983			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1984			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1985
1986			// initial vote.
1987			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 10));
1988
1989			// 2 + 1
1990			assert_eq!(balances(&2), (17, 3));
1991			assert_eq!(Voting::<Test>::get(&2).deposit, 3);
1992			assert_eq!(has_lock(&2), 10);
1993			assert_eq!(locked_stake_of(&2), 10);
1994
1995			// can update; different stake; different lock and reserve.
1996			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1997			// 2 + 2
1998			assert_eq!(balances(&2), (16, 4));
1999			assert_eq!(Voting::<Test>::get(&2).deposit, 4);
2000			assert_eq!(has_lock(&2), 15);
2001			assert_eq!(locked_stake_of(&2), 15);
2002
2003			// stay at two votes with different stake.
2004			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 3], 18));
2005			// 2 + 2
2006			assert_eq!(balances(&2), (16, 4));
2007			assert_eq!(Voting::<Test>::get(&2).deposit, 4);
2008			assert_eq!(has_lock(&2), 16);
2009			assert_eq!(locked_stake_of(&2), 16);
2010
2011			// back to 1 vote.
2012			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 12));
2013			// 2 + 1
2014			assert_eq!(balances(&2), (17, 3));
2015			assert_eq!(Voting::<Test>::get(&2).deposit, 3);
2016			assert_eq!(has_lock(&2), 12);
2017			assert_eq!(locked_stake_of(&2), 12);
2018		});
2019	}
2020
2021	#[test]
2022	fn cannot_vote_for_no_candidate() {
2023		ExtBuilder::default().build_and_execute(|| {
2024			assert_noop!(vote(RuntimeOrigin::signed(2), vec![], 20), Error::<Test>::NoVotes);
2025		});
2026	}
2027
2028	#[test]
2029	fn can_vote_for_old_members_even_when_no_new_candidates() {
2030		ExtBuilder::default().build_and_execute(|| {
2031			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2032			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2033
2034			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 20));
2035
2036			System::set_block_number(5);
2037			Elections::on_initialize(System::block_number());
2038
2039			assert_eq!(members_ids(), vec![4, 5]);
2040			assert!(candidate_ids().is_empty());
2041
2042			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2043		});
2044	}
2045
2046	#[test]
2047	fn prime_works() {
2048		ExtBuilder::default().build_and_execute(|| {
2049			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2050			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2051			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2052
2053			assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2054			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2055			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2056			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2057			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2058
2059			System::set_block_number(5);
2060			Elections::on_initialize(System::block_number());
2061
2062			assert_eq!(members_ids(), vec![4, 5]);
2063			assert!(candidate_ids().is_empty());
2064
2065			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2066			assert_eq!(PRIME.with(|p| *p.borrow()), Some(4));
2067		});
2068	}
2069
2070	#[test]
2071	fn ballots_longer_than_a_reduced_max_votes_per_voter_do_not_skew_the_prime() {
2072		ExtBuilder::default().desired_members(3).build_and_execute(|| {
2073			// GIVEN a ballot ranking three candidates, cast while `MaxVotesPerVoter` allowed it.
2074			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2075			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2076			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2077			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3, 4, 5], 20));
2078
2079			// WHEN the bound drops below the stored ballot length, as a runtime upgrade without a
2080			// migration would leave it, and the term ends.
2081			MaxVotesPerVoter::set(1);
2082			System::set_block_number(5);
2083			Elections::on_initialize(System::block_number());
2084
2085			// THEN the election still runs, and the Borda count credits only the ranks the current
2086			// bound covers: rank 0 carries the whole stake, every rank past it weighs nothing.
2087			assert_eq!(members_ids(), vec![3, 4, 5]);
2088			assert_eq!(PRIME.with(|p| *p.borrow()), Some(3));
2089		});
2090	}
2091
2092	#[test]
2093	fn prime_votes_for_exiting_members_are_removed() {
2094		ExtBuilder::default().build_and_execute(|| {
2095			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2096			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2097			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2098
2099			assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2100			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2101			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2102			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2103			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2104
2105			assert_ok!(Elections::renounce_candidacy(
2106				RuntimeOrigin::signed(4),
2107				Renouncing::Candidate(3)
2108			));
2109
2110			System::set_block_number(5);
2111			Elections::on_initialize(System::block_number());
2112
2113			assert_eq!(members_ids(), vec![3, 5]);
2114			assert!(candidate_ids().is_empty());
2115
2116			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2117		});
2118	}
2119
2120	#[test]
2121	fn prime_is_kept_if_other_members_leave() {
2122		ExtBuilder::default().build_and_execute(|| {
2123			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2124			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2125
2126			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2127			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2128
2129			System::set_block_number(5);
2130			Elections::on_initialize(System::block_number());
2131
2132			assert_eq!(members_ids(), vec![4, 5]);
2133			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2134			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2135
2136			assert_eq!(members_ids(), vec![5]);
2137			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2138		})
2139	}
2140
2141	#[test]
2142	fn prime_is_gone_if_renouncing() {
2143		ExtBuilder::default().build_and_execute(|| {
2144			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2145			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2146
2147			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2148			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2149
2150			System::set_block_number(5);
2151			Elections::on_initialize(System::block_number());
2152
2153			assert_eq!(members_ids(), vec![4, 5]);
2154			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2155			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member));
2156
2157			assert_eq!(members_ids(), vec![4]);
2158			assert_eq!(PRIME.with(|p| *p.borrow()), None);
2159		})
2160	}
2161
2162	#[test]
2163	fn cannot_vote_for_more_than_candidates_and_members_and_runners() {
2164		ExtBuilder::default()
2165			.desired_runners_up(1)
2166			.balance_factor(10)
2167			.build_and_execute(|| {
2168				// when we have only candidates
2169				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2170				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2171				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2172
2173				assert_noop!(
2174					// content of the vote is irrelevant.
2175					vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5),
2176					Error::<Test>::TooManyVotes,
2177				);
2178
2179				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2180				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2181				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2182
2183				System::set_block_number(5);
2184				Elections::on_initialize(System::block_number());
2185
2186				// now we have 2 members, 1 runner-up, and 1 new candidate
2187				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2188
2189				assert_ok!(vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5));
2190				assert_noop!(
2191					vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9_999, 99_999], 5),
2192					Error::<Test>::TooManyVotes,
2193				);
2194			});
2195	}
2196
2197	#[test]
2198	fn cannot_vote_for_less_than_ed() {
2199		ExtBuilder::default().build_and_execute(|| {
2200			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2201			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2202
2203			assert_noop!(vote(RuntimeOrigin::signed(2), vec![4], 1), Error::<Test>::LowBalance);
2204		})
2205	}
2206
2207	#[test]
2208	fn can_vote_for_more_than_free_balance_but_moot() {
2209		ExtBuilder::default().build_and_execute(|| {
2210			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2211			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2212
2213			// User has 100 free and 50 reserved.
2214			assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 150));
2215			assert_ok!(Balances::reserve(&2, 50));
2216			// User tries to vote with 150 tokens.
2217			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 150));
2218			// We truncate to only their free balance, after reserving additional for voting.
2219			assert_eq!(locked_stake_of(&2), 98);
2220			assert_eq!(has_lock(&2), 98);
2221		});
2222	}
2223
2224	#[test]
2225	fn remove_voter_should_work() {
2226		ExtBuilder::default().voter_bond(8).build_and_execute(|| {
2227			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2228
2229			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2230			assert_ok!(vote(RuntimeOrigin::signed(3), vec![5], 30));
2231
2232			assert_eq_uvec!(all_voters(), vec![2, 3]);
2233			assert_eq!(balances(&2), (12, 8));
2234			assert_eq!(locked_stake_of(&2), 12);
2235			assert_eq!(balances(&3), (22, 8));
2236			assert_eq!(locked_stake_of(&3), 22);
2237			assert_eq!(votes_of(&2), vec![5]);
2238			assert_eq!(votes_of(&3), vec![5]);
2239
2240			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2241
2242			assert_eq_uvec!(all_voters(), vec![3]);
2243			assert!(votes_of(&2).is_empty());
2244			assert_eq!(locked_stake_of(&2), 0);
2245
2246			assert_eq!(balances(&2), (20, 0));
2247			assert_eq!(pallet_balances::Locks::<Test>::get(&2).len(), 0);
2248		});
2249	}
2250
2251	#[test]
2252	fn non_voter_remove_should_not_work() {
2253		ExtBuilder::default().build_and_execute(|| {
2254			assert_noop!(
2255				Elections::remove_voter(RuntimeOrigin::signed(3)),
2256				Error::<Test>::MustBeVoter
2257			);
2258		});
2259	}
2260
2261	#[test]
2262	fn dupe_remove_should_fail() {
2263		ExtBuilder::default().build_and_execute(|| {
2264			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2265			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2266
2267			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2268			assert!(all_voters().is_empty());
2269
2270			assert_noop!(
2271				Elections::remove_voter(RuntimeOrigin::signed(2)),
2272				Error::<Test>::MustBeVoter
2273			);
2274		});
2275	}
2276
2277	#[test]
2278	fn removed_voter_should_not_be_counted() {
2279		ExtBuilder::default().build_and_execute(|| {
2280			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2281			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2282			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2283
2284			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2285			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2286			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2287
2288			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2289
2290			System::set_block_number(5);
2291			Elections::on_initialize(System::block_number());
2292
2293			assert_eq!(members_ids(), vec![3, 5]);
2294		});
2295	}
2296
2297	#[test]
2298	fn simple_voting_rounds_should_work() {
2299		ExtBuilder::default().build_and_execute(|| {
2300			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2301			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2302			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2303
2304			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2305			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 15));
2306			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2307
2308			assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2309
2310			assert_eq!(votes_of(&2), vec![5]);
2311			assert_eq!(votes_of(&3), vec![3]);
2312			assert_eq!(votes_of(&4), vec![4]);
2313
2314			assert_eq!(candidate_ids(), vec![3, 4, 5]);
2315			assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2316
2317			assert_eq!(ElectionRounds::<Test>::get(), 0);
2318
2319			System::set_block_number(5);
2320			Elections::on_initialize(System::block_number());
2321
2322			assert_eq!(balances(&3), (25, 5));
2323			// votes for 5
2324			assert_eq!(balances(&2), (18, 2));
2325			assert_eq!(members_and_stake(), vec![(3, 25), (5, 18)]);
2326			assert!(RunnersUp::<Test>::get().is_empty());
2327
2328			assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2329			assert!(candidate_ids().is_empty());
2330			assert_eq!(Candidates::<Test>::decode_len(), None);
2331
2332			assert_eq!(ElectionRounds::<Test>::get(), 1);
2333		});
2334	}
2335
2336	#[test]
2337	fn empty_term() {
2338		ExtBuilder::default().build_and_execute(|| {
2339			// no candidates, no nothing.
2340			System::set_block_number(5);
2341			Elections::on_initialize(System::block_number());
2342
2343			System::assert_last_event(RuntimeEvent::Elections(super::Event::EmptyTerm));
2344		})
2345	}
2346
2347	#[test]
2348	fn all_outgoing() {
2349		ExtBuilder::default().build_and_execute(|| {
2350			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2351			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2352
2353			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2354			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2355
2356			System::set_block_number(5);
2357			Elections::on_initialize(System::block_number());
2358
2359			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2360				new_members: vec![(4, 35), (5, 45)],
2361			}));
2362
2363			assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2364			assert_eq!(runners_up_and_stake(), vec![]);
2365
2366			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2367			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2368
2369			System::set_block_number(10);
2370			Elections::on_initialize(System::block_number());
2371
2372			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2373				new_members: vec![],
2374			}));
2375
2376			// outgoing have lost their bond.
2377			assert_eq!(balances(&4), (37, 0));
2378			assert_eq!(balances(&5), (47, 0));
2379		});
2380	}
2381
2382	#[test]
2383	fn defunct_voter_will_be_counted() {
2384		ExtBuilder::default().build_and_execute(|| {
2385			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2386
2387			// This guy's vote is pointless for this round.
2388			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4], 30));
2389			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2390
2391			System::set_block_number(5);
2392			Elections::on_initialize(System::block_number());
2393
2394			assert_eq!(members_and_stake(), vec![(5, 45)]);
2395			assert_eq!(ElectionRounds::<Test>::get(), 1);
2396
2397			// but now it has a valid target.
2398			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2399
2400			System::set_block_number(10);
2401			Elections::on_initialize(System::block_number());
2402
2403			// candidate 4 is affected by an old vote.
2404			assert_eq!(members_and_stake(), vec![(4, 28), (5, 45)]);
2405			assert_eq!(ElectionRounds::<Test>::get(), 2);
2406			assert_eq_uvec!(all_voters(), vec![3, 5]);
2407		});
2408	}
2409
2410	#[test]
2411	fn only_desired_seats_are_chosen() {
2412		ExtBuilder::default().build_and_execute(|| {
2413			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2414			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2415			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2416			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2417
2418			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2419			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2420			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2421			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2422
2423			System::set_block_number(5);
2424			Elections::on_initialize(System::block_number());
2425
2426			assert_eq!(ElectionRounds::<Test>::get(), 1);
2427			assert_eq!(members_ids(), vec![4, 5]);
2428		});
2429	}
2430
2431	#[test]
2432	fn phragmen_should_not_self_vote() {
2433		ExtBuilder::default().build_and_execute(|| {
2434			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2435			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2436
2437			System::set_block_number(5);
2438			Elections::on_initialize(System::block_number());
2439
2440			assert!(candidate_ids().is_empty());
2441			assert_eq!(ElectionRounds::<Test>::get(), 1);
2442			assert!(members_ids().is_empty());
2443
2444			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2445				new_members: vec![],
2446			}));
2447		});
2448	}
2449
2450	#[test]
2451	fn runners_up_should_be_kept() {
2452		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2453			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2454			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2455			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2456			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2457
2458			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2459			assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2460			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2461			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2462
2463			System::set_block_number(5);
2464			Elections::on_initialize(System::block_number());
2465			// sorted based on account id.
2466			assert_eq!(members_ids(), vec![4, 5]);
2467			// sorted based on merit (least -> most)
2468			assert_eq!(runners_up_ids(), vec![3, 2]);
2469
2470			// runner ups are still locked.
2471			assert_eq!(balances(&4), (35, 5));
2472			assert_eq!(balances(&5), (45, 5));
2473			assert_eq!(balances(&3), (25, 5));
2474		});
2475	}
2476
2477	#[test]
2478	fn runners_up_should_be_next_candidates() {
2479		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2480			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2481			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2482			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2483			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2484
2485			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2486			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2487			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2488			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2489
2490			System::set_block_number(5);
2491			Elections::on_initialize(System::block_number());
2492			assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2493			assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2494
2495			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 10));
2496
2497			System::set_block_number(10);
2498			Elections::on_initialize(System::block_number());
2499
2500			assert_eq!(members_and_stake(), vec![(3, 25), (4, 35)]);
2501			assert_eq!(runners_up_and_stake(), vec![(5, 10), (2, 15)]);
2502		});
2503	}
2504
2505	#[test]
2506	fn runners_up_lose_bond_once_outgoing() {
2507		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2508			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2509			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2510			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2511
2512			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2513			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2514			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2515
2516			System::set_block_number(5);
2517			Elections::on_initialize(System::block_number());
2518			assert_eq!(members_ids(), vec![4, 5]);
2519			assert_eq!(runners_up_ids(), vec![2]);
2520			assert_eq!(balances(&2), (15, 5));
2521
2522			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2523			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2524
2525			System::set_block_number(10);
2526			Elections::on_initialize(System::block_number());
2527
2528			assert_eq!(runners_up_ids(), vec![3]);
2529			assert_eq!(balances(&2), (15, 2));
2530		});
2531	}
2532
2533	#[test]
2534	fn members_lose_bond_once_outgoing() {
2535		ExtBuilder::default().build_and_execute(|| {
2536			assert_eq!(balances(&5), (50, 0));
2537
2538			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2539			assert_eq!(balances(&5), (47, 3));
2540
2541			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2542			assert_eq!(balances(&5), (45, 5));
2543
2544			System::set_block_number(5);
2545			Elections::on_initialize(System::block_number());
2546			assert_eq!(members_ids(), vec![5]);
2547
2548			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2549			assert_eq!(balances(&5), (47, 3));
2550
2551			System::set_block_number(10);
2552			Elections::on_initialize(System::block_number());
2553			assert!(members_ids().is_empty());
2554
2555			assert_eq!(balances(&5), (47, 0));
2556		});
2557	}
2558
2559	#[test]
2560	fn candidates_lose_the_bond_when_outgoing() {
2561		ExtBuilder::default().build_and_execute(|| {
2562			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2563			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2564
2565			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2566
2567			assert_eq!(balances(&5), (47, 3));
2568			assert_eq!(balances(&3), (27, 3));
2569
2570			System::set_block_number(5);
2571			Elections::on_initialize(System::block_number());
2572
2573			assert_eq!(members_ids(), vec![5]);
2574
2575			// winner
2576			assert_eq!(balances(&5), (47, 3));
2577			// loser
2578			assert_eq!(balances(&3), (27, 0));
2579		});
2580	}
2581
2582	#[test]
2583	fn current_members_are_always_next_candidate() {
2584		ExtBuilder::default().build_and_execute(|| {
2585			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2586			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2587
2588			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2589			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2590
2591			System::set_block_number(5);
2592			Elections::on_initialize(System::block_number());
2593
2594			assert_eq!(members_ids(), vec![4, 5]);
2595			assert_eq!(ElectionRounds::<Test>::get(), 1);
2596
2597			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2598			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2599
2600			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2601			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2602
2603			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2604
2605			// 5 will persist as candidates despite not being in the list.
2606			assert_eq!(candidate_ids(), vec![2, 3]);
2607
2608			System::set_block_number(10);
2609			Elections::on_initialize(System::block_number());
2610
2611			// 4 removed; 5 and 3 are the new best.
2612			assert_eq!(members_ids(), vec![3, 5]);
2613		});
2614	}
2615
2616	#[test]
2617	fn election_state_is_uninterrupted() {
2618		// what I mean by uninterrupted:
2619		// given no input or stimulants the same members are re-elected.
2620		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2621			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2622			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2623			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2624			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2625
2626			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2627			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2628			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2629			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2630
2631			let check_at_block = |b: u32| {
2632				System::set_block_number(b.into());
2633				Elections::on_initialize(System::block_number());
2634				// we keep re-electing the same folks.
2635				assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2636				assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2637				// no new candidates but old members and runners-up are always added.
2638				assert!(candidate_ids().is_empty());
2639				assert_eq!(ElectionRounds::<Test>::get(), b / 5);
2640				assert_eq_uvec!(all_voters(), vec![2, 3, 4, 5]);
2641			};
2642
2643			// this state will always persist when no further input is given.
2644			check_at_block(5);
2645			check_at_block(10);
2646			check_at_block(15);
2647			check_at_block(20);
2648		});
2649	}
2650
2651	#[test]
2652	fn remove_members_triggers_election() {
2653		ExtBuilder::default().build_and_execute(|| {
2654			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2655			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2656
2657			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2658			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2659
2660			System::set_block_number(5);
2661			Elections::on_initialize(System::block_number());
2662			assert_eq!(members_ids(), vec![4, 5]);
2663			assert_eq!(ElectionRounds::<Test>::get(), 1);
2664
2665			// a new candidate
2666			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2667			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2668
2669			assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 4, true, true));
2670
2671			assert_eq!(balances(&4), (35, 2)); // slashed
2672			assert_eq!(ElectionRounds::<Test>::get(), 2); // new election round
2673			assert_eq!(members_ids(), vec![3, 5]); // new members
2674		});
2675	}
2676
2677	#[test]
2678	fn seats_should_be_released_when_no_vote() {
2679		ExtBuilder::default().build_and_execute(|| {
2680			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2681			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2682			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2683
2684			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2685			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2686			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2687			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2688
2689			assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2690
2691			assert_eq!(ElectionRounds::<Test>::get(), 0);
2692
2693			System::set_block_number(5);
2694			Elections::on_initialize(System::block_number());
2695			assert_eq!(members_ids(), vec![3, 5]);
2696			assert_eq!(ElectionRounds::<Test>::get(), 1);
2697
2698			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2699			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(3)));
2700			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2701			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2702
2703			// meanwhile, no one cares to become a candidate again.
2704			System::set_block_number(10);
2705			Elections::on_initialize(System::block_number());
2706			assert!(members_ids().is_empty());
2707			assert_eq!(ElectionRounds::<Test>::get(), 2);
2708		});
2709	}
2710
2711	#[test]
2712	fn incoming_outgoing_are_reported() {
2713		ExtBuilder::default().build_and_execute(|| {
2714			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2715			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2716
2717			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2718			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2719
2720			System::set_block_number(5);
2721			Elections::on_initialize(System::block_number());
2722			assert_eq!(members_ids(), vec![4, 5]);
2723
2724			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
2725			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2726			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2727
2728			// 5 will change their vote and becomes an `outgoing`
2729			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 8));
2730			// 4 will stay in the set
2731			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2732			// 3 will become a winner
2733			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2734			// these two are losers.
2735			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2736			assert_ok!(vote(RuntimeOrigin::signed(1), vec![1], 10));
2737
2738			System::set_block_number(10);
2739			Elections::on_initialize(System::block_number());
2740
2741			// 3, 4 are new members, must still be bonded, nothing slashed.
2742			assert_eq!(members_and_stake(), vec![(3, 25), (4, 43)]);
2743			assert_eq!(balances(&3), (25, 5));
2744			assert_eq!(balances(&4), (35, 5));
2745
2746			// 1 is a loser, slashed by 3.
2747			assert_eq!(balances(&1), (5, 2));
2748
2749			// 5 is an outgoing loser. will also get slashed.
2750			assert_eq!(balances(&5), (45, 2));
2751
2752			System::assert_has_event(RuntimeEvent::Elections(super::Event::NewTerm {
2753				new_members: vec![(4, 35), (5, 45)],
2754			}));
2755		})
2756	}
2757
2758	#[test]
2759	fn invalid_votes_are_moot() {
2760		ExtBuilder::default().build_and_execute(|| {
2761			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2762			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2763
2764			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2765			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2766			assert_ok!(vote(RuntimeOrigin::signed(5), vec![10], 50));
2767
2768			System::set_block_number(5);
2769			Elections::on_initialize(System::block_number());
2770
2771			assert_eq_uvec!(members_ids(), vec![3, 4]);
2772			assert_eq!(ElectionRounds::<Test>::get(), 1);
2773		});
2774	}
2775
2776	#[test]
2777	fn members_are_sorted_based_on_id_runners_on_merit() {
2778		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2779			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2780			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2781			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2782			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2783
2784			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2785			assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2786			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2787			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2788
2789			System::set_block_number(5);
2790			Elections::on_initialize(System::block_number());
2791			// id: low -> high.
2792			assert_eq!(members_and_stake(), vec![(4, 45), (5, 35)]);
2793			// merit: low -> high.
2794			assert_eq!(runners_up_and_stake(), vec![(3, 15), (2, 25)]);
2795		});
2796	}
2797
2798	#[test]
2799	fn runner_up_replacement_maintains_members_order() {
2800		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2801			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2802			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2803			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2804
2805			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2806			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2807			assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2808
2809			System::set_block_number(5);
2810			Elections::on_initialize(System::block_number());
2811
2812			assert_eq!(members_ids(), vec![2, 4]);
2813			assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 2, true, false));
2814			assert_eq!(members_ids(), vec![4, 5]);
2815		});
2816	}
2817
2818	#[test]
2819	fn can_renounce_candidacy_member_with_runners_bond_is_refunded() {
2820		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2821			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2822			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2823			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2824			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2825
2826			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2827			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2828			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2829			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2830
2831			System::set_block_number(5);
2832			Elections::on_initialize(System::block_number());
2833
2834			assert_eq!(members_ids(), vec![4, 5]);
2835			assert_eq!(runners_up_ids(), vec![2, 3]);
2836
2837			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2838			assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
2839
2840			assert_eq!(members_ids(), vec![3, 5]);
2841			assert_eq!(runners_up_ids(), vec![2]);
2842		})
2843	}
2844
2845	#[test]
2846	fn can_renounce_candidacy_member_without_runners_bond_is_refunded() {
2847		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2848			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2849			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2850
2851			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2852			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2853
2854			System::set_block_number(5);
2855			Elections::on_initialize(System::block_number());
2856
2857			assert_eq!(members_ids(), vec![4, 5]);
2858			assert!(runners_up_ids().is_empty());
2859
2860			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2861			assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
2862
2863			// no replacement
2864			assert_eq!(members_ids(), vec![5]);
2865			assert!(runners_up_ids().is_empty());
2866		})
2867	}
2868
2869	#[test]
2870	fn can_renounce_candidacy_runner_up() {
2871		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2872			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2873			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2874			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2875			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2876
2877			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2878			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2879			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2880			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2881
2882			System::set_block_number(5);
2883			Elections::on_initialize(System::block_number());
2884
2885			assert_eq!(members_ids(), vec![4, 5]);
2886			assert_eq!(runners_up_ids(), vec![2, 3]);
2887
2888			assert_ok!(Elections::renounce_candidacy(
2889				RuntimeOrigin::signed(3),
2890				Renouncing::RunnerUp
2891			));
2892			assert_eq!(balances(&3), (28, 2)); // 2 is voting bond.
2893
2894			assert_eq!(members_ids(), vec![4, 5]);
2895			assert_eq!(runners_up_ids(), vec![2]);
2896		})
2897	}
2898
2899	#[test]
2900	fn runner_up_replacement_works_when_out_of_order() {
2901		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2902			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2903			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2904			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2905			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2906
2907			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2908			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2909			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2910			assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2911
2912			System::set_block_number(5);
2913			Elections::on_initialize(System::block_number());
2914
2915			assert_eq!(members_ids(), vec![2, 4]);
2916			assert_eq!(runners_up_ids(), vec![5, 3]);
2917			assert_ok!(Elections::renounce_candidacy(
2918				RuntimeOrigin::signed(3),
2919				Renouncing::RunnerUp
2920			));
2921			assert_eq!(members_ids(), vec![2, 4]);
2922			assert_eq!(runners_up_ids(), vec![5]);
2923		});
2924	}
2925
2926	#[test]
2927	fn can_renounce_candidacy_candidate() {
2928		ExtBuilder::default().build_and_execute(|| {
2929			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2930			assert_eq!(balances(&5), (47, 3));
2931			assert_eq!(candidate_ids(), vec![5]);
2932
2933			assert_ok!(Elections::renounce_candidacy(
2934				RuntimeOrigin::signed(5),
2935				Renouncing::Candidate(1)
2936			));
2937			assert_eq!(balances(&5), (50, 0));
2938			assert!(candidate_ids().is_empty());
2939		})
2940	}
2941
2942	#[test]
2943	fn wrong_renounce_candidacy_should_fail() {
2944		ExtBuilder::default().build_and_execute(|| {
2945			assert_noop!(
2946				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Candidate(0)),
2947				Error::<Test>::InvalidRenouncing,
2948			);
2949			assert_noop!(
2950				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member),
2951				Error::<Test>::InvalidRenouncing,
2952			);
2953			assert_noop!(
2954				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::RunnerUp),
2955				Error::<Test>::InvalidRenouncing,
2956			);
2957		})
2958	}
2959
2960	#[test]
2961	fn non_member_renounce_member_should_fail() {
2962		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2963			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2964			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2965			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2966
2967			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2968			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2969			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2970
2971			System::set_block_number(5);
2972			Elections::on_initialize(System::block_number());
2973
2974			assert_eq!(members_ids(), vec![4, 5]);
2975			assert_eq!(runners_up_ids(), vec![3]);
2976
2977			assert_noop!(
2978				Elections::renounce_candidacy(RuntimeOrigin::signed(3), Renouncing::Member),
2979				Error::<Test>::InvalidRenouncing,
2980			);
2981		})
2982	}
2983
2984	#[test]
2985	fn non_runner_up_renounce_runner_up_should_fail() {
2986		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2987			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2988			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2989			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2990
2991			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2992			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2993			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2994
2995			System::set_block_number(5);
2996			Elections::on_initialize(System::block_number());
2997
2998			assert_eq!(members_ids(), vec![4, 5]);
2999			assert_eq!(runners_up_ids(), vec![3]);
3000
3001			assert_noop!(
3002				Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::RunnerUp),
3003				Error::<Test>::InvalidRenouncing,
3004			);
3005		})
3006	}
3007
3008	#[test]
3009	fn wrong_candidate_count_renounce_should_fail() {
3010		ExtBuilder::default().build_and_execute(|| {
3011			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3012			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3013			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3014
3015			assert_noop!(
3016				Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Candidate(2)),
3017				Error::<Test>::InvalidWitnessData,
3018			);
3019
3020			assert_ok!(Elections::renounce_candidacy(
3021				RuntimeOrigin::signed(4),
3022				Renouncing::Candidate(3)
3023			));
3024		})
3025	}
3026
3027	#[test]
3028	fn renounce_candidacy_count_can_overestimate() {
3029		ExtBuilder::default().build_and_execute(|| {
3030			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3031			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3032			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3033			// while we have only 3 candidates.
3034			assert_ok!(Elections::renounce_candidacy(
3035				RuntimeOrigin::signed(4),
3036				Renouncing::Candidate(4)
3037			));
3038		})
3039	}
3040
3041	#[test]
3042	fn unsorted_runners_up_are_detected() {
3043		ExtBuilder::default()
3044			.desired_runners_up(2)
3045			.desired_members(1)
3046			.build_and_execute(|| {
3047				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3048				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3049				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3050
3051				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3052				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3053				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 15));
3054
3055				System::set_block_number(5);
3056				Elections::on_initialize(System::block_number());
3057
3058				assert_eq!(members_ids(), vec![5]);
3059				assert_eq!(runners_up_ids(), vec![4, 3]);
3060
3061				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3062				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 10));
3063
3064				System::set_block_number(10);
3065				Elections::on_initialize(System::block_number());
3066
3067				assert_eq!(members_ids(), vec![5]);
3068				assert_eq!(runners_up_ids(), vec![2, 3]);
3069
3070				// 4 is outgoing runner-up. Slash candidacy bond.
3071				assert_eq!(balances(&4), (35, 2));
3072				// 3 stays.
3073				assert_eq!(balances(&3), (25, 5));
3074			})
3075	}
3076
3077	#[test]
3078	fn member_to_runner_up_wont_slash() {
3079		ExtBuilder::default()
3080			.desired_runners_up(2)
3081			.desired_members(1)
3082			.build_and_execute(|| {
3083				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3084				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3085				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3086
3087				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3088				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3089				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3090
3091				System::set_block_number(5);
3092				Elections::on_initialize(System::block_number());
3093
3094				assert_eq!(members_ids(), vec![4]);
3095				assert_eq!(runners_up_ids(), vec![2, 3]);
3096
3097				assert_eq!(balances(&4), (35, 5));
3098				assert_eq!(balances(&3), (25, 5));
3099				assert_eq!(balances(&2), (15, 5));
3100
3101				// this guy will shift everyone down.
3102				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3103				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3104
3105				System::set_block_number(10);
3106				Elections::on_initialize(System::block_number());
3107
3108				assert_eq!(members_ids(), vec![5]);
3109				assert_eq!(runners_up_ids(), vec![3, 4]);
3110
3111				// 4 went from member to runner-up -- don't slash.
3112				assert_eq!(balances(&4), (35, 5));
3113				// 3 stayed runner-up -- don't slash.
3114				assert_eq!(balances(&3), (25, 5));
3115				// 2 was removed -- slash.
3116				assert_eq!(balances(&2), (15, 2));
3117			});
3118	}
3119
3120	#[test]
3121	fn runner_up_to_member_wont_slash() {
3122		ExtBuilder::default()
3123			.desired_runners_up(2)
3124			.desired_members(1)
3125			.build_and_execute(|| {
3126				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3127				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3128				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3129
3130				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3131				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3132				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3133
3134				System::set_block_number(5);
3135				Elections::on_initialize(System::block_number());
3136
3137				assert_eq!(members_ids(), vec![4]);
3138				assert_eq!(runners_up_ids(), vec![2, 3]);
3139
3140				assert_eq!(balances(&4), (35, 5));
3141				assert_eq!(balances(&3), (25, 5));
3142				assert_eq!(balances(&2), (15, 5));
3143
3144				// swap some votes.
3145				assert_ok!(vote(RuntimeOrigin::signed(4), vec![2], 40));
3146				assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
3147
3148				System::set_block_number(10);
3149				Elections::on_initialize(System::block_number());
3150
3151				assert_eq!(members_ids(), vec![2]);
3152				assert_eq!(runners_up_ids(), vec![4, 3]);
3153
3154				// 2 went from runner to member, don't slash
3155				assert_eq!(balances(&2), (15, 5));
3156				// 4 went from member to runner, don't slash
3157				assert_eq!(balances(&4), (35, 5));
3158				// 3 stayed the same
3159				assert_eq!(balances(&3), (25, 5));
3160			});
3161	}
3162
3163	#[test]
3164	fn remove_and_replace_member_works() {
3165		let setup = || {
3166			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3167			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3168			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3169
3170			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3171			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3172			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3173
3174			System::set_block_number(5);
3175			Elections::on_initialize(System::block_number());
3176
3177			assert_eq!(members_ids(), vec![4, 5]);
3178			assert_eq!(runners_up_ids(), vec![3]);
3179		};
3180
3181		// member removed, replacement found.
3182		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3183			setup();
3184			assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(true));
3185
3186			assert_eq!(members_ids(), vec![3, 5]);
3187			assert_eq!(runners_up_ids().len(), 0);
3188		});
3189
3190		// member removed, no replacement found.
3191		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3192			setup();
3193			assert_ok!(Elections::renounce_candidacy(
3194				RuntimeOrigin::signed(3),
3195				Renouncing::RunnerUp
3196			));
3197			assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(false));
3198
3199			assert_eq!(members_ids(), vec![5]);
3200			assert_eq!(runners_up_ids().len(), 0);
3201		});
3202
3203		// wrong member to remove.
3204		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3205			setup();
3206			assert!(matches!(Elections::remove_and_replace_member(&2, false), Err(_)));
3207		});
3208	}
3209
3210	#[test]
3211	fn no_desired_members() {
3212		// not interested in anything
3213		ExtBuilder::default()
3214			.desired_members(0)
3215			.desired_runners_up(0)
3216			.build_and_execute(|| {
3217				assert_eq!(Candidates::<Test>::get().len(), 0);
3218
3219				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3220				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3221				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3222
3223				assert_eq!(Candidates::<Test>::get().len(), 3);
3224
3225				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3226				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3227				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3228
3229				System::set_block_number(5);
3230				Elections::on_initialize(System::block_number());
3231
3232				assert_eq!(members_ids().len(), 0);
3233				assert_eq!(runners_up_ids().len(), 0);
3234				assert_eq!(all_voters().len(), 3);
3235				assert_eq!(Candidates::<Test>::get().len(), 0);
3236			});
3237
3238		// not interested in members
3239		ExtBuilder::default()
3240			.desired_members(0)
3241			.desired_runners_up(2)
3242			.build_and_execute(|| {
3243				assert_eq!(Candidates::<Test>::get().len(), 0);
3244
3245				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3246				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3247				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3248
3249				assert_eq!(Candidates::<Test>::get().len(), 3);
3250
3251				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3252				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3253				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3254
3255				System::set_block_number(5);
3256				Elections::on_initialize(System::block_number());
3257
3258				assert_eq!(members_ids().len(), 0);
3259				assert_eq!(runners_up_ids(), vec![3, 4]);
3260				assert_eq!(all_voters().len(), 3);
3261				assert_eq!(Candidates::<Test>::get().len(), 0);
3262			});
3263
3264		// not interested in runners-up
3265		ExtBuilder::default()
3266			.desired_members(2)
3267			.desired_runners_up(0)
3268			.build_and_execute(|| {
3269				assert_eq!(Candidates::<Test>::get().len(), 0);
3270
3271				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3272				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3273				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3274
3275				assert_eq!(Candidates::<Test>::get().len(), 3);
3276
3277				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3278				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3279				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3280
3281				System::set_block_number(5);
3282				Elections::on_initialize(System::block_number());
3283
3284				assert_eq!(members_ids(), vec![3, 4]);
3285				assert_eq!(runners_up_ids().len(), 0);
3286				assert_eq!(all_voters().len(), 3);
3287				assert_eq!(Candidates::<Test>::get().len(), 0);
3288			});
3289	}
3290
3291	#[test]
3292	fn dupe_vote_is_moot() {
3293		ExtBuilder::default().desired_members(1).build_and_execute(|| {
3294			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3295			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3296			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3297			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3298			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
3299
3300			// all these duplicate votes will not cause 2 to win.
3301			assert_ok!(vote(RuntimeOrigin::signed(1), vec![2, 2, 2, 2], 5));
3302			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2, 2, 2, 2], 20));
3303
3304			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3305
3306			System::set_block_number(5);
3307			Elections::on_initialize(System::block_number());
3308
3309			assert_eq!(members_ids(), vec![3]);
3310		})
3311	}
3312
3313	#[test]
3314	fn remove_defunct_voter_works() {
3315		ExtBuilder::default().build_and_execute(|| {
3316			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3317			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3318			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3319
3320			// defunct
3321			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5, 4], 5));
3322			// defunct
3323			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3324			// ok
3325			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 5));
3326			// ok
3327			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3, 4], 5));
3328
3329			assert_ok!(Elections::renounce_candidacy(
3330				RuntimeOrigin::signed(5),
3331				Renouncing::Candidate(3)
3332			));
3333			assert_ok!(Elections::renounce_candidacy(
3334				RuntimeOrigin::signed(4),
3335				Renouncing::Candidate(2)
3336			));
3337			assert_ok!(Elections::renounce_candidacy(
3338				RuntimeOrigin::signed(3),
3339				Renouncing::Candidate(1)
3340			));
3341
3342			assert_ok!(Elections::clean_defunct_voters(RuntimeOrigin::root(), 4, 2));
3343		})
3344	}
3345}