Skip to main content

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					let mut prime_votes = new_members_sorted_by_id
1057						.iter()
1058						.map(|c| (&c.0, BalanceOf::<T>::zero()))
1059						.collect::<Vec<_>>();
1060					for (_, stake, votes) in voters_and_stakes.into_iter() {
1061						for (vote_multiplier, who) in
1062							votes.iter().enumerate().map(|(vote_position, who)| {
1063								((T::MaxVotesPerVoter::get() as usize - vote_position) as u32, who)
1064							}) {
1065							if let Ok(i) = prime_votes.binary_search_by_key(&who, |k| k.0) {
1066								prime_votes[i].1 = prime_votes[i]
1067									.1
1068									.saturating_add(stake.saturating_mul(vote_multiplier.into()));
1069							}
1070						}
1071					}
1072					// We then select the new member with the highest weighted stake. In the case of
1073					// a tie, the last person in the list with the tied score is selected. This is
1074					// the person with the "highest" account id based on the sort above.
1075					let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone());
1076
1077					// new_members_sorted_by_id is sorted by account id.
1078					let new_members_ids_sorted = new_members_sorted_by_id
1079						.iter()
1080						.map(|(m, _)| m.clone())
1081						.collect::<Vec<T::AccountId>>();
1082
1083					// report member changes. We compute diff because we need the outgoing list.
1084					let (incoming, outgoing) = T::ChangeMembers::compute_members_diff_sorted(
1085						&new_members_ids_sorted,
1086						&old_members_ids_sorted,
1087					);
1088					T::ChangeMembers::change_members_sorted(
1089						&incoming,
1090						&outgoing,
1091						&new_members_ids_sorted,
1092					);
1093					T::ChangeMembers::set_prime(prime);
1094
1095					// All candidates/members/runners-up who are no longer retaining a position as a
1096					// seat holder will lose their bond.
1097					candidates_and_deposit.iter().for_each(|(c, d)| {
1098						if new_members_ids_sorted.binary_search(c).is_err() &&
1099							new_runners_up_ids_sorted.binary_search(c).is_err()
1100						{
1101							let (imbalance, _) = T::Currency::slash_reserved(c, *d);
1102							T::LoserCandidate::on_unbalanced(imbalance);
1103							Self::deposit_event(Event::CandidateSlashed {
1104								candidate: c.clone(),
1105								amount: *d,
1106							});
1107						}
1108					});
1109
1110					// write final values to storage.
1111					let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf<T> {
1112						// defensive-only. This closure is used against the new members and new
1113						// runners-up, both of which are phragmen winners and thus must have
1114						// deposit.
1115						candidates_and_deposit
1116							.iter()
1117							.find_map(|(c, d)| if c == x { Some(*d) } else { None })
1118							.defensive_unwrap_or_default()
1119					};
1120					// fetch deposits from the one recorded one. This will make sure that a
1121					// candidate who submitted candidacy before a change to candidacy deposit will
1122					// have the correct amount recorded.
1123					Members::<T>::put(
1124						new_members_sorted_by_id
1125							.iter()
1126							.map(|(who, stake)| SeatHolder {
1127								deposit: deposit_of_candidate(who),
1128								who: who.clone(),
1129								stake: *stake,
1130							})
1131							.collect::<Vec<_>>(),
1132					);
1133					RunnersUp::<T>::put(
1134						new_runners_up_sorted_by_rank
1135							.into_iter()
1136							.map(|(who, stake)| SeatHolder {
1137								deposit: deposit_of_candidate(&who),
1138								who,
1139								stake,
1140							})
1141							.collect::<Vec<_>>(),
1142					);
1143
1144					// clean candidates.
1145					Candidates::<T>::kill();
1146
1147					Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id });
1148					ElectionRounds::<T>::mutate(|v| *v += 1);
1149				})
1150				.map_err(|e| {
1151					log::error!(target: LOG_TARGET, "Failed to run election [{:?}].", e,);
1152					Self::deposit_event(Event::ElectionError);
1153				});
1154
1155		T::WeightInfo::election_phragmen(weight_candidates, weight_voters, weight_edges)
1156	}
1157}
1158
1159impl<T: Config> Contains<T::AccountId> for Pallet<T> {
1160	fn contains(who: &T::AccountId) -> bool {
1161		Self::is_member(who)
1162	}
1163}
1164
1165impl<T: Config> SortedMembers<T::AccountId> for Pallet<T> {
1166	fn contains(who: &T::AccountId) -> bool {
1167		Self::is_member(who)
1168	}
1169
1170	fn sorted_members() -> Vec<T::AccountId> {
1171		Self::members_ids()
1172	}
1173
1174	// A special function to populate members in this pallet for passing Origin
1175	// checks in runtime benchmarking.
1176	#[cfg(feature = "runtime-benchmarks")]
1177	fn add(who: &T::AccountId) {
1178		Members::<T>::mutate(|members| match members.binary_search_by(|m| m.who.cmp(who)) {
1179			Ok(_) => (),
1180			Err(pos) => {
1181				let s = SeatHolder {
1182					who: who.clone(),
1183					stake: Default::default(),
1184					deposit: Default::default(),
1185				};
1186				members.insert(pos, s)
1187			},
1188		})
1189	}
1190}
1191
1192impl<T: Config> ContainsLengthBound for Pallet<T> {
1193	fn min_len() -> usize {
1194		0
1195	}
1196
1197	/// Implementation uses a parameter type so calling is cost-free.
1198	fn max_len() -> usize {
1199		T::DesiredMembers::get() as usize
1200	}
1201}
1202
1203#[cfg(any(feature = "try-runtime", test))]
1204impl<T: Config> Pallet<T> {
1205	fn do_try_state() -> Result<(), TryRuntimeError> {
1206		Self::try_state_members()?;
1207		Self::try_state_runners_up()?;
1208		Self::try_state_candidates()?;
1209		Self::try_state_candidates_runners_up_disjoint()?;
1210		Self::try_state_members_disjoint()?;
1211		Self::try_state_members_approval_stake()
1212	}
1213
1214	/// [`Members`] state checks. Invariants:
1215	///  - Members are always sorted based on account ID.
1216	fn try_state_members() -> Result<(), TryRuntimeError> {
1217		let mut members = Members::<T>::get().clone();
1218		members.sort_by_key(|m| m.who.clone());
1219
1220		if Members::<T>::get() == members {
1221			Ok(())
1222		} else {
1223			Err("try_state checks: Members must be always sorted by account ID".into())
1224		}
1225	}
1226
1227	// [`RunnersUp`] state checks. Invariants:
1228	//  - Elements are sorted based on weight (worst to best).
1229	fn try_state_runners_up() -> Result<(), TryRuntimeError> {
1230		let mut sorted = RunnersUp::<T>::get();
1231		// worst stake first
1232		sorted.sort_by(|a, b| a.stake.cmp(&b.stake));
1233
1234		if RunnersUp::<T>::get() == sorted {
1235			Ok(())
1236		} else {
1237			Err("try_state checks: Runners Up must always be sorted by stake (worst to best)"
1238				.into())
1239		}
1240	}
1241
1242	// [`Candidates`] state checks. Invariants:
1243	//  - Always sorted based on account ID.
1244	fn try_state_candidates() -> Result<(), TryRuntimeError> {
1245		let mut candidates = Candidates::<T>::get().clone();
1246		candidates.sort_by_key(|(c, _)| c.clone());
1247
1248		if Candidates::<T>::get() == candidates {
1249			Ok(())
1250		} else {
1251			Err("try_state checks: Candidates must be always sorted by account ID".into())
1252		}
1253	}
1254	// [`Candidates`] and [`RunnersUp`] state checks. Invariants:
1255	//  - Candidates and runners-ups sets are disjoint.
1256	fn try_state_candidates_runners_up_disjoint() -> Result<(), TryRuntimeError> {
1257		match Self::intersects(&Self::candidates_ids(), &Self::runners_up_ids()) {
1258			true => Err("Candidates and runners up sets should always be disjoint".into()),
1259			false => Ok(()),
1260		}
1261	}
1262
1263	// [`Members`], [`Candidates`] and [`RunnersUp`] state checks. Invariants:
1264	//  - Members and candidates sets are disjoint;
1265	//  - Members and runners-ups sets are disjoint.
1266	fn try_state_members_disjoint() -> Result<(), TryRuntimeError> {
1267		match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) &&
1268			Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids())
1269		{
1270			true => {
1271				Err("Members set should be disjoint from candidates and runners-up sets".into())
1272			},
1273			false => Ok(()),
1274		}
1275	}
1276
1277	// [`Members`], [`RunnersUp`] and approval stake state checks. Invariants:
1278	// - Selected members should have approval stake;
1279	// - Selected RunnersUp should have approval stake.
1280	fn try_state_members_approval_stake() -> Result<(), TryRuntimeError> {
1281		match Members::<T>::get()
1282			.iter()
1283			.chain(RunnersUp::<T>::get().iter())
1284			.all(|s| s.stake != BalanceOf::<T>::zero())
1285		{
1286			true => Ok(()),
1287			false => Err("Members and RunnersUp must have approval stake".into()),
1288		}
1289	}
1290
1291	fn intersects<P: PartialEq>(a: &[P], b: &[P]) -> bool {
1292		a.iter().any(|e| b.contains(e))
1293	}
1294
1295	fn candidates_ids() -> Vec<T::AccountId> {
1296		Candidates::<T>::get().iter().map(|(x, _)| x).cloned().collect::<Vec<_>>()
1297	}
1298
1299	fn runners_up_ids() -> Vec<T::AccountId> {
1300		RunnersUp::<T>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1301	}
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306	use super::*;
1307	use crate as elections_phragmen;
1308	use frame_support::{
1309		assert_noop, assert_ok, derive_impl,
1310		dispatch::DispatchResultWithPostInfo,
1311		parameter_types,
1312		traits::{ConstU32, OnInitialize},
1313	};
1314	use frame_system::ensure_signed;
1315	use sp_runtime::{testing::Header, BuildStorage};
1316	use substrate_test_utils::assert_eq_uvec;
1317
1318	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
1319	impl frame_system::Config for Test {
1320		type Block = Block;
1321		type AccountData = pallet_balances::AccountData<u64>;
1322	}
1323
1324	#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
1325	impl pallet_balances::Config for Test {
1326		type AccountStore = frame_system::Pallet<Test>;
1327	}
1328
1329	frame_support::parameter_types! {
1330		pub static VotingBondBase: u64 = 2;
1331		pub static VotingBondFactor: u64 = 0;
1332		pub static CandidacyBond: u64 = 3;
1333		pub static DesiredMembers: u32 = 2;
1334		pub static DesiredRunnersUp: u32 = 0;
1335		pub static TermDuration: u64 = 5;
1336		pub static Members: Vec<u64> = vec![];
1337		pub static Prime: Option<u64> = None;
1338	}
1339
1340	pub struct TestChangeMembers;
1341	impl ChangeMembers<u64> for TestChangeMembers {
1342		fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) {
1343			// new, incoming, outgoing must be sorted.
1344			let mut new_sorted = new.to_vec();
1345			new_sorted.sort();
1346			assert_eq!(new, &new_sorted[..]);
1347
1348			let mut incoming_sorted = incoming.to_vec();
1349			incoming_sorted.sort();
1350			assert_eq!(incoming, &incoming_sorted[..]);
1351
1352			let mut outgoing_sorted = outgoing.to_vec();
1353			outgoing_sorted.sort();
1354			assert_eq!(outgoing, &outgoing_sorted[..]);
1355
1356			// incoming and outgoing must be disjoint
1357			for x in incoming.iter() {
1358				assert!(outgoing.binary_search(x).is_err());
1359			}
1360
1361			let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec());
1362			old_plus_incoming.extend_from_slice(incoming);
1363			old_plus_incoming.sort();
1364
1365			let mut new_plus_outgoing = new.to_vec();
1366			new_plus_outgoing.extend_from_slice(outgoing);
1367			new_plus_outgoing.sort();
1368
1369			assert_eq!(old_plus_incoming, new_plus_outgoing, "change members call is incorrect!");
1370
1371			MEMBERS.with(|m| *m.borrow_mut() = new.to_vec());
1372			PRIME.with(|p| *p.borrow_mut() = None);
1373		}
1374
1375		fn set_prime(who: Option<u64>) {
1376			PRIME.with(|p| *p.borrow_mut() = who);
1377		}
1378
1379		fn get_prime() -> Option<u64> {
1380			PRIME.with(|p| *p.borrow())
1381		}
1382	}
1383
1384	parameter_types! {
1385		pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect";
1386		pub const PhragmenMaxVoters: u32 = 1000;
1387		pub const PhragmenMaxCandidates: u32 = 100;
1388	}
1389
1390	impl Config for Test {
1391		type PalletId = ElectionsPhragmenPalletId;
1392		type RuntimeEvent = RuntimeEvent;
1393		type Currency = Balances;
1394		type CurrencyToVote = ();
1395		type ChangeMembers = TestChangeMembers;
1396		type InitializeMembers = ();
1397		type CandidacyBond = CandidacyBond;
1398		type VotingBondBase = VotingBondBase;
1399		type VotingBondFactor = VotingBondFactor;
1400		type TermDuration = TermDuration;
1401		type DesiredMembers = DesiredMembers;
1402		type DesiredRunnersUp = DesiredRunnersUp;
1403		type LoserCandidate = ();
1404		type KickedMember = ();
1405		type WeightInfo = ();
1406		type MaxVoters = PhragmenMaxVoters;
1407		type MaxVotesPerVoter = ConstU32<16>;
1408		type MaxCandidates = PhragmenMaxCandidates;
1409	}
1410
1411	pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
1412	pub type UncheckedExtrinsic =
1413		sp_runtime::generic::UncheckedExtrinsic<u32, RuntimeCall, u64, ()>;
1414
1415	frame_support::construct_runtime!(
1416		pub enum Test
1417		{
1418			System: frame_system,
1419			Balances: pallet_balances,
1420			Elections: elections_phragmen,
1421		}
1422	);
1423
1424	pub struct ExtBuilder {
1425		balance_factor: u64,
1426		genesis_members: Vec<(u64, u64)>,
1427	}
1428
1429	impl Default for ExtBuilder {
1430		fn default() -> Self {
1431			Self { balance_factor: 1, genesis_members: vec![] }
1432		}
1433	}
1434
1435	impl ExtBuilder {
1436		pub fn voter_bond(self, bond: u64) -> Self {
1437			VOTING_BOND_BASE.with(|v| *v.borrow_mut() = bond);
1438			self
1439		}
1440		pub fn voter_bond_factor(self, bond: u64) -> Self {
1441			VOTING_BOND_FACTOR.with(|v| *v.borrow_mut() = bond);
1442			self
1443		}
1444		pub fn desired_runners_up(self, count: u32) -> Self {
1445			DESIRED_RUNNERS_UP.with(|v| *v.borrow_mut() = count);
1446			self
1447		}
1448		pub fn term_duration(self, duration: u64) -> Self {
1449			TERM_DURATION.with(|v| *v.borrow_mut() = duration);
1450			self
1451		}
1452		pub fn genesis_members(mut self, members: Vec<(u64, u64)>) -> Self {
1453			MEMBERS.with(|m| *m.borrow_mut() = members.iter().map(|(m, _)| *m).collect::<Vec<_>>());
1454			self.genesis_members = members;
1455			self
1456		}
1457		pub fn desired_members(self, count: u32) -> Self {
1458			DESIRED_MEMBERS.with(|m| *m.borrow_mut() = count);
1459			self
1460		}
1461		pub fn balance_factor(mut self, factor: u64) -> Self {
1462			self.balance_factor = factor;
1463			self
1464		}
1465		pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1466			sp_tracing::try_init_simple();
1467			MEMBERS.with(|m| {
1468				*m.borrow_mut() = self.genesis_members.iter().map(|(m, _)| *m).collect::<Vec<_>>()
1469			});
1470			let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig {
1471				system: frame_system::GenesisConfig::default(),
1472				balances: pallet_balances::GenesisConfig::<Test> {
1473					balances: vec![
1474						(1, 10 * self.balance_factor),
1475						(2, 20 * self.balance_factor),
1476						(3, 30 * self.balance_factor),
1477						(4, 40 * self.balance_factor),
1478						(5, 50 * self.balance_factor),
1479						(6, 60 * self.balance_factor),
1480					],
1481					..Default::default()
1482				},
1483				elections: elections_phragmen::GenesisConfig::<Test> {
1484					members: self.genesis_members,
1485				},
1486			}
1487			.build_storage()
1488			.unwrap()
1489			.into();
1490			ext.execute_with(pre_conditions);
1491			ext.execute_with(test);
1492
1493			#[cfg(feature = "try-runtime")]
1494			ext.execute_with(|| {
1495				assert_ok!(<Elections as frame_support::traits::Hooks<u64>>::try_state(
1496					System::block_number()
1497				));
1498			});
1499		}
1500	}
1501
1502	fn candidate_ids() -> Vec<u64> {
1503		Candidates::<Test>::get().into_iter().map(|(c, _)| c).collect::<Vec<_>>()
1504	}
1505
1506	fn candidate_deposit(who: &u64) -> u64 {
1507		Candidates::<Test>::get()
1508			.into_iter()
1509			.find_map(|(c, d)| if c == *who { Some(d) } else { None })
1510			.unwrap_or_default()
1511	}
1512
1513	fn voter_deposit(who: &u64) -> u64 {
1514		Voting::<Test>::get(who).deposit
1515	}
1516
1517	fn runners_up_ids() -> Vec<u64> {
1518		RunnersUp::<Test>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1519	}
1520
1521	fn members_ids() -> Vec<u64> {
1522		Elections::members_ids()
1523	}
1524
1525	fn members_and_stake() -> Vec<(u64, u64)> {
1526		elections_phragmen::Members::<Test>::get()
1527			.into_iter()
1528			.map(|m| (m.who, m.stake))
1529			.collect::<Vec<_>>()
1530	}
1531
1532	fn runners_up_and_stake() -> Vec<(u64, u64)> {
1533		RunnersUp::<Test>::get()
1534			.into_iter()
1535			.map(|r| (r.who, r.stake))
1536			.collect::<Vec<_>>()
1537	}
1538
1539	fn all_voters() -> Vec<u64> {
1540		Voting::<Test>::iter().map(|(v, _)| v).collect::<Vec<u64>>()
1541	}
1542
1543	fn balances(who: &u64) -> (u64, u64) {
1544		(Balances::free_balance(who), Balances::reserved_balance(who))
1545	}
1546
1547	fn has_lock(who: &u64) -> u64 {
1548		Balances::locks(who)
1549			.get(0)
1550			.cloned()
1551			.map(|lock| {
1552				assert_eq!(lock.id, ElectionsPhragmenPalletId::get());
1553				lock.amount
1554			})
1555			.unwrap_or_default()
1556	}
1557
1558	fn locked_stake_of(who: &u64) -> u64 {
1559		Voting::<Test>::get(who).stake
1560	}
1561
1562	fn pre_conditions() {
1563		System::set_block_number(1);
1564		Elections::do_try_state().unwrap();
1565	}
1566
1567	fn submit_candidacy(origin: RuntimeOrigin) -> sp_runtime::DispatchResult {
1568		Elections::submit_candidacy(origin, Candidates::<Test>::get().len() as u32)
1569	}
1570
1571	fn vote(origin: RuntimeOrigin, votes: Vec<u64>, stake: u64) -> DispatchResultWithPostInfo {
1572		// historical note: helper function was created in a period of time in which the API of vote
1573		// call was changing. Currently it is a wrapper for the original call and does not do much.
1574		// Nonetheless, totally harmless.
1575		ensure_signed(origin.clone()).expect("vote origin must be signed");
1576		Elections::vote(origin, votes, stake)
1577	}
1578
1579	fn votes_of(who: &u64) -> Vec<u64> {
1580		Voting::<Test>::get(who).votes
1581	}
1582
1583	#[test]
1584	fn params_should_work() {
1585		ExtBuilder::default().build_and_execute(|| {
1586			assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1587			assert_eq!(<Test as Config>::DesiredRunnersUp::get(), 0);
1588			assert_eq!(<Test as Config>::VotingBondBase::get(), 2);
1589			assert_eq!(<Test as Config>::VotingBondFactor::get(), 0);
1590			assert_eq!(<Test as Config>::CandidacyBond::get(), 3);
1591			assert_eq!(<Test as Config>::TermDuration::get(), 5);
1592			assert_eq!(ElectionRounds::<Test>::get(), 0);
1593
1594			assert!(elections_phragmen::Members::<Test>::get().is_empty());
1595			assert!(RunnersUp::<Test>::get().is_empty());
1596
1597			assert!(candidate_ids().is_empty());
1598			assert_eq!(Candidates::<Test>::decode_len(), None);
1599			assert!(Elections::is_candidate(&1).is_err());
1600
1601			assert!(all_voters().is_empty());
1602			assert!(votes_of(&1).is_empty());
1603		});
1604	}
1605
1606	#[test]
1607	fn genesis_members_should_work() {
1608		ExtBuilder::default()
1609			.genesis_members(vec![(1, 10), (2, 20)])
1610			.build_and_execute(|| {
1611				System::set_block_number(1);
1612				assert_eq!(
1613					elections_phragmen::Members::<Test>::get(),
1614					vec![
1615						SeatHolder { who: 1, stake: 10, deposit: 0 },
1616						SeatHolder { who: 2, stake: 20, deposit: 0 }
1617					]
1618				);
1619
1620				assert_eq!(
1621					Voting::<Test>::get(1),
1622					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1623				);
1624				assert_eq!(
1625					Voting::<Test>::get(2),
1626					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1627				);
1628
1629				// they will persist since they have self vote.
1630				System::set_block_number(5);
1631				Elections::on_initialize(System::block_number());
1632
1633				assert_eq!(members_ids(), vec![1, 2]);
1634			})
1635	}
1636
1637	#[test]
1638	fn genesis_voters_can_remove_lock() {
1639		ExtBuilder::default()
1640			.genesis_members(vec![(1, 10), (2, 20)])
1641			.build_and_execute(|| {
1642				System::set_block_number(1);
1643
1644				assert_eq!(
1645					Voting::<Test>::get(1),
1646					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1647				);
1648				assert_eq!(
1649					Voting::<Test>::get(2),
1650					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1651				);
1652
1653				assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(1)));
1654				assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1655
1656				assert_eq!(Voting::<Test>::get(1), Default::default());
1657				assert_eq!(Voting::<Test>::get(2), Default::default());
1658			})
1659	}
1660
1661	#[test]
1662	fn genesis_members_unsorted_should_work() {
1663		ExtBuilder::default()
1664			.genesis_members(vec![(2, 20), (1, 10)])
1665			.build_and_execute(|| {
1666				System::set_block_number(1);
1667				assert_eq!(
1668					elections_phragmen::Members::<Test>::get(),
1669					vec![
1670						SeatHolder { who: 1, stake: 10, deposit: 0 },
1671						SeatHolder { who: 2, stake: 20, deposit: 0 },
1672					]
1673				);
1674
1675				assert_eq!(
1676					Voting::<Test>::get(1),
1677					Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1678				);
1679				assert_eq!(
1680					Voting::<Test>::get(2),
1681					Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1682				);
1683
1684				// they will persist since they have self vote.
1685				System::set_block_number(5);
1686				Elections::on_initialize(System::block_number());
1687
1688				assert_eq!(members_ids(), vec![1, 2]);
1689			})
1690	}
1691
1692	#[test]
1693	#[should_panic = "Genesis member does not have enough stake"]
1694	fn genesis_members_cannot_over_stake_0() {
1695		// 10 cannot lock 20 as their stake and extra genesis will panic.
1696		ExtBuilder::default()
1697			.genesis_members(vec![(1, 20), (2, 20)])
1698			.build_and_execute(|| {});
1699	}
1700
1701	#[test]
1702	#[should_panic = "Duplicate member in elections-phragmen genesis: 2"]
1703	fn genesis_members_cannot_be_duplicate() {
1704		ExtBuilder::default()
1705			.desired_members(3)
1706			.genesis_members(vec![(1, 10), (2, 10), (2, 10)])
1707			.build_and_execute(|| {});
1708	}
1709
1710	#[test]
1711	#[should_panic = "Cannot accept more than DesiredMembers genesis member"]
1712	fn genesis_members_cannot_too_many() {
1713		ExtBuilder::default()
1714			.genesis_members(vec![(1, 10), (2, 10), (3, 30)])
1715			.desired_members(2)
1716			.build_and_execute(|| {});
1717	}
1718
1719	#[test]
1720	fn term_duration_zero_is_passive() {
1721		ExtBuilder::default().term_duration(0).build_and_execute(|| {
1722			assert_eq!(<Test as Config>::TermDuration::get(), 0);
1723			assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1724			assert_eq!(ElectionRounds::<Test>::get(), 0);
1725
1726			assert!(members_ids().is_empty());
1727			assert!(RunnersUp::<Test>::get().is_empty());
1728			assert!(candidate_ids().is_empty());
1729
1730			System::set_block_number(5);
1731			Elections::on_initialize(System::block_number());
1732
1733			assert!(members_ids().is_empty());
1734			assert!(RunnersUp::<Test>::get().is_empty());
1735			assert!(candidate_ids().is_empty());
1736		});
1737	}
1738
1739	#[test]
1740	fn simple_candidate_submission_should_work() {
1741		ExtBuilder::default().build_and_execute(|| {
1742			assert_eq!(candidate_ids(), Vec::<u64>::new());
1743			assert!(Elections::is_candidate(&1).is_err());
1744			assert!(Elections::is_candidate(&2).is_err());
1745
1746			assert_eq!(balances(&1), (10, 0));
1747			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1748			assert_eq!(balances(&1), (7, 3));
1749
1750			assert_eq!(candidate_ids(), vec![1]);
1751
1752			assert!(Elections::is_candidate(&1).is_ok());
1753			assert!(Elections::is_candidate(&2).is_err());
1754
1755			assert_eq!(balances(&2), (20, 0));
1756			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1757			assert_eq!(balances(&2), (17, 3));
1758
1759			assert_eq!(candidate_ids(), vec![1, 2]);
1760
1761			assert!(Elections::is_candidate(&1).is_ok());
1762			assert!(Elections::is_candidate(&2).is_ok());
1763
1764			assert_eq!(candidate_deposit(&1), 3);
1765			assert_eq!(candidate_deposit(&2), 3);
1766			assert_eq!(candidate_deposit(&3), 0);
1767		});
1768	}
1769
1770	#[test]
1771	fn updating_candidacy_bond_works() {
1772		ExtBuilder::default().build_and_execute(|| {
1773			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1774			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
1775			assert_eq!(Candidates::<Test>::get(), vec![(5, 3)]);
1776
1777			// a runtime upgrade changes the bond.
1778			CANDIDACY_BOND.with(|v| *v.borrow_mut() = 4);
1779
1780			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1781			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
1782			assert_eq!(Candidates::<Test>::get(), vec![(4, 4), (5, 3)]);
1783
1784			// once elected, they each hold their candidacy bond, no more.
1785			System::set_block_number(5);
1786			Elections::on_initialize(System::block_number());
1787
1788			assert_eq!(balances(&4), (34, 6));
1789			assert_eq!(balances(&5), (45, 5));
1790			assert_eq!(
1791				elections_phragmen::Members::<Test>::get(),
1792				vec![
1793					SeatHolder { who: 4, stake: 34, deposit: 4 },
1794					SeatHolder { who: 5, stake: 45, deposit: 3 },
1795				]
1796			);
1797		})
1798	}
1799
1800	#[test]
1801	fn candidates_are_always_sorted() {
1802		ExtBuilder::default().build_and_execute(|| {
1803			assert_eq!(candidate_ids(), Vec::<u64>::new());
1804
1805			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1806			assert_eq!(candidate_ids(), vec![3]);
1807			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1808			assert_eq!(candidate_ids(), vec![1, 3]);
1809			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1810			assert_eq!(candidate_ids(), vec![1, 2, 3]);
1811			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1812			assert_eq!(candidate_ids(), vec![1, 2, 3, 4]);
1813		});
1814	}
1815
1816	#[test]
1817	fn dupe_candidate_submission_should_not_work() {
1818		ExtBuilder::default().build_and_execute(|| {
1819			assert_eq!(candidate_ids(), Vec::<u64>::new());
1820			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1821			assert_eq!(candidate_ids(), vec![1]);
1822			assert_noop!(
1823				submit_candidacy(RuntimeOrigin::signed(1)),
1824				Error::<Test>::DuplicatedCandidate
1825			);
1826		});
1827	}
1828
1829	#[test]
1830	fn member_candidacy_submission_should_not_work() {
1831		// critically important to make sure that outgoing candidates and losers are not mixed up.
1832		ExtBuilder::default().build_and_execute(|| {
1833			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1834			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1835
1836			System::set_block_number(5);
1837			Elections::on_initialize(System::block_number());
1838
1839			assert_eq!(members_ids(), vec![5]);
1840			assert!(RunnersUp::<Test>::get().is_empty());
1841			assert!(candidate_ids().is_empty());
1842
1843			assert_noop!(submit_candidacy(RuntimeOrigin::signed(5)), Error::<Test>::MemberSubmit);
1844		});
1845	}
1846
1847	#[test]
1848	fn runner_candidate_submission_should_not_work() {
1849		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
1850			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1851			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1852			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1853
1854			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 20));
1855			assert_ok!(vote(RuntimeOrigin::signed(1), vec![3], 10));
1856
1857			System::set_block_number(5);
1858			Elections::on_initialize(System::block_number());
1859
1860			assert_eq!(members_ids(), vec![4, 5]);
1861			assert_eq!(runners_up_ids(), vec![3]);
1862
1863			assert_noop!(submit_candidacy(RuntimeOrigin::signed(3)), Error::<Test>::RunnerUpSubmit);
1864		});
1865	}
1866
1867	#[test]
1868	fn poor_candidate_submission_should_not_work() {
1869		ExtBuilder::default().build_and_execute(|| {
1870			assert_eq!(candidate_ids(), Vec::<u64>::new());
1871			assert_noop!(
1872				submit_candidacy(RuntimeOrigin::signed(7)),
1873				Error::<Test>::InsufficientCandidateFunds,
1874			);
1875		});
1876	}
1877
1878	#[test]
1879	fn simple_voting_should_work() {
1880		ExtBuilder::default().build_and_execute(|| {
1881			assert_eq!(candidate_ids(), Vec::<u64>::new());
1882			assert_eq!(balances(&2), (20, 0));
1883
1884			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1885			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1886
1887			assert_eq!(balances(&2), (18, 2));
1888			assert_eq!(has_lock(&2), 18);
1889		});
1890	}
1891
1892	#[test]
1893	fn can_vote_with_custom_stake() {
1894		ExtBuilder::default().build_and_execute(|| {
1895			assert_eq!(candidate_ids(), Vec::<u64>::new());
1896			assert_eq!(balances(&2), (20, 0));
1897
1898			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1899			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 12));
1900
1901			assert_eq!(balances(&2), (18, 2));
1902			assert_eq!(has_lock(&2), 12);
1903		});
1904	}
1905
1906	#[test]
1907	fn can_update_votes_and_stake() {
1908		ExtBuilder::default().build_and_execute(|| {
1909			assert_eq!(balances(&2), (20, 0));
1910
1911			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1912			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1913			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1914
1915			// User only locks up to their free balance.
1916			assert_eq!(balances(&2), (18, 2));
1917			assert_eq!(has_lock(&2), 18);
1918			assert_eq!(locked_stake_of(&2), 18);
1919
1920			// can update; different stake; different lock and reserve.
1921			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1922			assert_eq!(balances(&2), (18, 2));
1923			assert_eq!(has_lock(&2), 15);
1924			assert_eq!(locked_stake_of(&2), 15);
1925		});
1926	}
1927
1928	#[test]
1929	fn updated_voting_bond_works() {
1930		ExtBuilder::default().build_and_execute(|| {
1931			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1932
1933			assert_eq!(balances(&2), (20, 0));
1934			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 5));
1935			assert_eq!(balances(&2), (18, 2));
1936			assert_eq!(voter_deposit(&2), 2);
1937
1938			// a runtime upgrade lowers the voting bond to 1. This guy still un-reserves 2 when
1939			// leaving.
1940			VOTING_BOND_BASE.with(|v| *v.borrow_mut() = 1);
1941
1942			// proof that bond changed.
1943			assert_eq!(balances(&1), (10, 0));
1944			assert_ok!(vote(RuntimeOrigin::signed(1), vec![5], 5));
1945			assert_eq!(balances(&1), (9, 1));
1946			assert_eq!(voter_deposit(&1), 1);
1947
1948			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1949			assert_eq!(balances(&2), (20, 0));
1950		})
1951	}
1952
1953	#[test]
1954	fn voting_reserves_bond_per_vote() {
1955		ExtBuilder::default().voter_bond_factor(1).build_and_execute(|| {
1956			assert_eq!(balances(&2), (20, 0));
1957
1958			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1959			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1960
1961			// initial vote.
1962			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 10));
1963
1964			// 2 + 1
1965			assert_eq!(balances(&2), (17, 3));
1966			assert_eq!(Voting::<Test>::get(&2).deposit, 3);
1967			assert_eq!(has_lock(&2), 10);
1968			assert_eq!(locked_stake_of(&2), 10);
1969
1970			// can update; different stake; different lock and reserve.
1971			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1972			// 2 + 2
1973			assert_eq!(balances(&2), (16, 4));
1974			assert_eq!(Voting::<Test>::get(&2).deposit, 4);
1975			assert_eq!(has_lock(&2), 15);
1976			assert_eq!(locked_stake_of(&2), 15);
1977
1978			// stay at two votes with different stake.
1979			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 3], 18));
1980			// 2 + 2
1981			assert_eq!(balances(&2), (16, 4));
1982			assert_eq!(Voting::<Test>::get(&2).deposit, 4);
1983			assert_eq!(has_lock(&2), 16);
1984			assert_eq!(locked_stake_of(&2), 16);
1985
1986			// back to 1 vote.
1987			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 12));
1988			// 2 + 1
1989			assert_eq!(balances(&2), (17, 3));
1990			assert_eq!(Voting::<Test>::get(&2).deposit, 3);
1991			assert_eq!(has_lock(&2), 12);
1992			assert_eq!(locked_stake_of(&2), 12);
1993		});
1994	}
1995
1996	#[test]
1997	fn cannot_vote_for_no_candidate() {
1998		ExtBuilder::default().build_and_execute(|| {
1999			assert_noop!(vote(RuntimeOrigin::signed(2), vec![], 20), Error::<Test>::NoVotes);
2000		});
2001	}
2002
2003	#[test]
2004	fn can_vote_for_old_members_even_when_no_new_candidates() {
2005		ExtBuilder::default().build_and_execute(|| {
2006			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2007			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2008
2009			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 20));
2010
2011			System::set_block_number(5);
2012			Elections::on_initialize(System::block_number());
2013
2014			assert_eq!(members_ids(), vec![4, 5]);
2015			assert!(candidate_ids().is_empty());
2016
2017			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2018		});
2019	}
2020
2021	#[test]
2022	fn prime_works() {
2023		ExtBuilder::default().build_and_execute(|| {
2024			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2025			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2026			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2027
2028			assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2029			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2030			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2031			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2032			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2033
2034			System::set_block_number(5);
2035			Elections::on_initialize(System::block_number());
2036
2037			assert_eq!(members_ids(), vec![4, 5]);
2038			assert!(candidate_ids().is_empty());
2039
2040			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2041			assert_eq!(PRIME.with(|p| *p.borrow()), Some(4));
2042		});
2043	}
2044
2045	#[test]
2046	fn prime_votes_for_exiting_members_are_removed() {
2047		ExtBuilder::default().build_and_execute(|| {
2048			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2049			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2050			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2051
2052			assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2053			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2054			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2055			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2056			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2057
2058			assert_ok!(Elections::renounce_candidacy(
2059				RuntimeOrigin::signed(4),
2060				Renouncing::Candidate(3)
2061			));
2062
2063			System::set_block_number(5);
2064			Elections::on_initialize(System::block_number());
2065
2066			assert_eq!(members_ids(), vec![3, 5]);
2067			assert!(candidate_ids().is_empty());
2068
2069			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2070		});
2071	}
2072
2073	#[test]
2074	fn prime_is_kept_if_other_members_leave() {
2075		ExtBuilder::default().build_and_execute(|| {
2076			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2077			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2078
2079			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2080			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2081
2082			System::set_block_number(5);
2083			Elections::on_initialize(System::block_number());
2084
2085			assert_eq!(members_ids(), vec![4, 5]);
2086			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2087			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2088
2089			assert_eq!(members_ids(), vec![5]);
2090			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2091		})
2092	}
2093
2094	#[test]
2095	fn prime_is_gone_if_renouncing() {
2096		ExtBuilder::default().build_and_execute(|| {
2097			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2098			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2099
2100			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2101			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2102
2103			System::set_block_number(5);
2104			Elections::on_initialize(System::block_number());
2105
2106			assert_eq!(members_ids(), vec![4, 5]);
2107			assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2108			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member));
2109
2110			assert_eq!(members_ids(), vec![4]);
2111			assert_eq!(PRIME.with(|p| *p.borrow()), None);
2112		})
2113	}
2114
2115	#[test]
2116	fn cannot_vote_for_more_than_candidates_and_members_and_runners() {
2117		ExtBuilder::default()
2118			.desired_runners_up(1)
2119			.balance_factor(10)
2120			.build_and_execute(|| {
2121				// when we have only candidates
2122				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2123				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2124				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2125
2126				assert_noop!(
2127					// content of the vote is irrelevant.
2128					vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5),
2129					Error::<Test>::TooManyVotes,
2130				);
2131
2132				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2133				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2134				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2135
2136				System::set_block_number(5);
2137				Elections::on_initialize(System::block_number());
2138
2139				// now we have 2 members, 1 runner-up, and 1 new candidate
2140				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2141
2142				assert_ok!(vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5));
2143				assert_noop!(
2144					vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9_999, 99_999], 5),
2145					Error::<Test>::TooManyVotes,
2146				);
2147			});
2148	}
2149
2150	#[test]
2151	fn cannot_vote_for_less_than_ed() {
2152		ExtBuilder::default().build_and_execute(|| {
2153			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2154			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2155
2156			assert_noop!(vote(RuntimeOrigin::signed(2), vec![4], 1), Error::<Test>::LowBalance);
2157		})
2158	}
2159
2160	#[test]
2161	fn can_vote_for_more_than_free_balance_but_moot() {
2162		ExtBuilder::default().build_and_execute(|| {
2163			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2164			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2165
2166			// User has 100 free and 50 reserved.
2167			assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 150));
2168			assert_ok!(Balances::reserve(&2, 50));
2169			// User tries to vote with 150 tokens.
2170			assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 150));
2171			// We truncate to only their free balance, after reserving additional for voting.
2172			assert_eq!(locked_stake_of(&2), 98);
2173			assert_eq!(has_lock(&2), 98);
2174		});
2175	}
2176
2177	#[test]
2178	fn remove_voter_should_work() {
2179		ExtBuilder::default().voter_bond(8).build_and_execute(|| {
2180			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2181
2182			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2183			assert_ok!(vote(RuntimeOrigin::signed(3), vec![5], 30));
2184
2185			assert_eq_uvec!(all_voters(), vec![2, 3]);
2186			assert_eq!(balances(&2), (12, 8));
2187			assert_eq!(locked_stake_of(&2), 12);
2188			assert_eq!(balances(&3), (22, 8));
2189			assert_eq!(locked_stake_of(&3), 22);
2190			assert_eq!(votes_of(&2), vec![5]);
2191			assert_eq!(votes_of(&3), vec![5]);
2192
2193			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2194
2195			assert_eq_uvec!(all_voters(), vec![3]);
2196			assert!(votes_of(&2).is_empty());
2197			assert_eq!(locked_stake_of(&2), 0);
2198
2199			assert_eq!(balances(&2), (20, 0));
2200			assert_eq!(pallet_balances::Locks::<Test>::get(&2).len(), 0);
2201		});
2202	}
2203
2204	#[test]
2205	fn non_voter_remove_should_not_work() {
2206		ExtBuilder::default().build_and_execute(|| {
2207			assert_noop!(
2208				Elections::remove_voter(RuntimeOrigin::signed(3)),
2209				Error::<Test>::MustBeVoter
2210			);
2211		});
2212	}
2213
2214	#[test]
2215	fn dupe_remove_should_fail() {
2216		ExtBuilder::default().build_and_execute(|| {
2217			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2218			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2219
2220			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2221			assert!(all_voters().is_empty());
2222
2223			assert_noop!(
2224				Elections::remove_voter(RuntimeOrigin::signed(2)),
2225				Error::<Test>::MustBeVoter
2226			);
2227		});
2228	}
2229
2230	#[test]
2231	fn removed_voter_should_not_be_counted() {
2232		ExtBuilder::default().build_and_execute(|| {
2233			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2234			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2235			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2236
2237			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2238			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2239			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2240
2241			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2242
2243			System::set_block_number(5);
2244			Elections::on_initialize(System::block_number());
2245
2246			assert_eq!(members_ids(), vec![3, 5]);
2247		});
2248	}
2249
2250	#[test]
2251	fn simple_voting_rounds_should_work() {
2252		ExtBuilder::default().build_and_execute(|| {
2253			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2254			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2255			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2256
2257			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2258			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 15));
2259			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2260
2261			assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2262
2263			assert_eq!(votes_of(&2), vec![5]);
2264			assert_eq!(votes_of(&3), vec![3]);
2265			assert_eq!(votes_of(&4), vec![4]);
2266
2267			assert_eq!(candidate_ids(), vec![3, 4, 5]);
2268			assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2269
2270			assert_eq!(ElectionRounds::<Test>::get(), 0);
2271
2272			System::set_block_number(5);
2273			Elections::on_initialize(System::block_number());
2274
2275			assert_eq!(balances(&3), (25, 5));
2276			// votes for 5
2277			assert_eq!(balances(&2), (18, 2));
2278			assert_eq!(members_and_stake(), vec![(3, 25), (5, 18)]);
2279			assert!(RunnersUp::<Test>::get().is_empty());
2280
2281			assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2282			assert!(candidate_ids().is_empty());
2283			assert_eq!(Candidates::<Test>::decode_len(), None);
2284
2285			assert_eq!(ElectionRounds::<Test>::get(), 1);
2286		});
2287	}
2288
2289	#[test]
2290	fn empty_term() {
2291		ExtBuilder::default().build_and_execute(|| {
2292			// no candidates, no nothing.
2293			System::set_block_number(5);
2294			Elections::on_initialize(System::block_number());
2295
2296			System::assert_last_event(RuntimeEvent::Elections(super::Event::EmptyTerm));
2297		})
2298	}
2299
2300	#[test]
2301	fn all_outgoing() {
2302		ExtBuilder::default().build_and_execute(|| {
2303			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2304			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2305
2306			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2307			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2308
2309			System::set_block_number(5);
2310			Elections::on_initialize(System::block_number());
2311
2312			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2313				new_members: vec![(4, 35), (5, 45)],
2314			}));
2315
2316			assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2317			assert_eq!(runners_up_and_stake(), vec![]);
2318
2319			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2320			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2321
2322			System::set_block_number(10);
2323			Elections::on_initialize(System::block_number());
2324
2325			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2326				new_members: vec![],
2327			}));
2328
2329			// outgoing have lost their bond.
2330			assert_eq!(balances(&4), (37, 0));
2331			assert_eq!(balances(&5), (47, 0));
2332		});
2333	}
2334
2335	#[test]
2336	fn defunct_voter_will_be_counted() {
2337		ExtBuilder::default().build_and_execute(|| {
2338			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2339
2340			// This guy's vote is pointless for this round.
2341			assert_ok!(vote(RuntimeOrigin::signed(3), vec![4], 30));
2342			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2343
2344			System::set_block_number(5);
2345			Elections::on_initialize(System::block_number());
2346
2347			assert_eq!(members_and_stake(), vec![(5, 45)]);
2348			assert_eq!(ElectionRounds::<Test>::get(), 1);
2349
2350			// but now it has a valid target.
2351			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2352
2353			System::set_block_number(10);
2354			Elections::on_initialize(System::block_number());
2355
2356			// candidate 4 is affected by an old vote.
2357			assert_eq!(members_and_stake(), vec![(4, 28), (5, 45)]);
2358			assert_eq!(ElectionRounds::<Test>::get(), 2);
2359			assert_eq_uvec!(all_voters(), vec![3, 5]);
2360		});
2361	}
2362
2363	#[test]
2364	fn only_desired_seats_are_chosen() {
2365		ExtBuilder::default().build_and_execute(|| {
2366			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2367			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2368			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2369			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2370
2371			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2372			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2373			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2374			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2375
2376			System::set_block_number(5);
2377			Elections::on_initialize(System::block_number());
2378
2379			assert_eq!(ElectionRounds::<Test>::get(), 1);
2380			assert_eq!(members_ids(), vec![4, 5]);
2381		});
2382	}
2383
2384	#[test]
2385	fn phragmen_should_not_self_vote() {
2386		ExtBuilder::default().build_and_execute(|| {
2387			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2388			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2389
2390			System::set_block_number(5);
2391			Elections::on_initialize(System::block_number());
2392
2393			assert!(candidate_ids().is_empty());
2394			assert_eq!(ElectionRounds::<Test>::get(), 1);
2395			assert!(members_ids().is_empty());
2396
2397			System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2398				new_members: vec![],
2399			}));
2400		});
2401	}
2402
2403	#[test]
2404	fn runners_up_should_be_kept() {
2405		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2406			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2407			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2408			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2409			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2410
2411			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2412			assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2413			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2414			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2415
2416			System::set_block_number(5);
2417			Elections::on_initialize(System::block_number());
2418			// sorted based on account id.
2419			assert_eq!(members_ids(), vec![4, 5]);
2420			// sorted based on merit (least -> most)
2421			assert_eq!(runners_up_ids(), vec![3, 2]);
2422
2423			// runner ups are still locked.
2424			assert_eq!(balances(&4), (35, 5));
2425			assert_eq!(balances(&5), (45, 5));
2426			assert_eq!(balances(&3), (25, 5));
2427		});
2428	}
2429
2430	#[test]
2431	fn runners_up_should_be_next_candidates() {
2432		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2433			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2434			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2435			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2436			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2437
2438			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2439			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2440			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2441			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2442
2443			System::set_block_number(5);
2444			Elections::on_initialize(System::block_number());
2445			assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2446			assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2447
2448			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 10));
2449
2450			System::set_block_number(10);
2451			Elections::on_initialize(System::block_number());
2452
2453			assert_eq!(members_and_stake(), vec![(3, 25), (4, 35)]);
2454			assert_eq!(runners_up_and_stake(), vec![(5, 10), (2, 15)]);
2455		});
2456	}
2457
2458	#[test]
2459	fn runners_up_lose_bond_once_outgoing() {
2460		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2461			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2462			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2463			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2464
2465			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2466			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2467			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2468
2469			System::set_block_number(5);
2470			Elections::on_initialize(System::block_number());
2471			assert_eq!(members_ids(), vec![4, 5]);
2472			assert_eq!(runners_up_ids(), vec![2]);
2473			assert_eq!(balances(&2), (15, 5));
2474
2475			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2476			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2477
2478			System::set_block_number(10);
2479			Elections::on_initialize(System::block_number());
2480
2481			assert_eq!(runners_up_ids(), vec![3]);
2482			assert_eq!(balances(&2), (15, 2));
2483		});
2484	}
2485
2486	#[test]
2487	fn members_lose_bond_once_outgoing() {
2488		ExtBuilder::default().build_and_execute(|| {
2489			assert_eq!(balances(&5), (50, 0));
2490
2491			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2492			assert_eq!(balances(&5), (47, 3));
2493
2494			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2495			assert_eq!(balances(&5), (45, 5));
2496
2497			System::set_block_number(5);
2498			Elections::on_initialize(System::block_number());
2499			assert_eq!(members_ids(), vec![5]);
2500
2501			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2502			assert_eq!(balances(&5), (47, 3));
2503
2504			System::set_block_number(10);
2505			Elections::on_initialize(System::block_number());
2506			assert!(members_ids().is_empty());
2507
2508			assert_eq!(balances(&5), (47, 0));
2509		});
2510	}
2511
2512	#[test]
2513	fn candidates_lose_the_bond_when_outgoing() {
2514		ExtBuilder::default().build_and_execute(|| {
2515			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2516			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2517
2518			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2519
2520			assert_eq!(balances(&5), (47, 3));
2521			assert_eq!(balances(&3), (27, 3));
2522
2523			System::set_block_number(5);
2524			Elections::on_initialize(System::block_number());
2525
2526			assert_eq!(members_ids(), vec![5]);
2527
2528			// winner
2529			assert_eq!(balances(&5), (47, 3));
2530			// loser
2531			assert_eq!(balances(&3), (27, 0));
2532		});
2533	}
2534
2535	#[test]
2536	fn current_members_are_always_next_candidate() {
2537		ExtBuilder::default().build_and_execute(|| {
2538			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2539			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2540
2541			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2542			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2543
2544			System::set_block_number(5);
2545			Elections::on_initialize(System::block_number());
2546
2547			assert_eq!(members_ids(), vec![4, 5]);
2548			assert_eq!(ElectionRounds::<Test>::get(), 1);
2549
2550			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2551			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2552
2553			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2554			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2555
2556			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2557
2558			// 5 will persist as candidates despite not being in the list.
2559			assert_eq!(candidate_ids(), vec![2, 3]);
2560
2561			System::set_block_number(10);
2562			Elections::on_initialize(System::block_number());
2563
2564			// 4 removed; 5 and 3 are the new best.
2565			assert_eq!(members_ids(), vec![3, 5]);
2566		});
2567	}
2568
2569	#[test]
2570	fn election_state_is_uninterrupted() {
2571		// what I mean by uninterrupted:
2572		// given no input or stimulants the same members are re-elected.
2573		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2574			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2575			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2576			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2577			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2578
2579			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2580			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2581			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2582			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2583
2584			let check_at_block = |b: u32| {
2585				System::set_block_number(b.into());
2586				Elections::on_initialize(System::block_number());
2587				// we keep re-electing the same folks.
2588				assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2589				assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2590				// no new candidates but old members and runners-up are always added.
2591				assert!(candidate_ids().is_empty());
2592				assert_eq!(ElectionRounds::<Test>::get(), b / 5);
2593				assert_eq_uvec!(all_voters(), vec![2, 3, 4, 5]);
2594			};
2595
2596			// this state will always persist when no further input is given.
2597			check_at_block(5);
2598			check_at_block(10);
2599			check_at_block(15);
2600			check_at_block(20);
2601		});
2602	}
2603
2604	#[test]
2605	fn remove_members_triggers_election() {
2606		ExtBuilder::default().build_and_execute(|| {
2607			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2608			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2609
2610			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2611			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2612
2613			System::set_block_number(5);
2614			Elections::on_initialize(System::block_number());
2615			assert_eq!(members_ids(), vec![4, 5]);
2616			assert_eq!(ElectionRounds::<Test>::get(), 1);
2617
2618			// a new candidate
2619			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2620			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2621
2622			assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 4, true, true));
2623
2624			assert_eq!(balances(&4), (35, 2)); // slashed
2625			assert_eq!(ElectionRounds::<Test>::get(), 2); // new election round
2626			assert_eq!(members_ids(), vec![3, 5]); // new members
2627		});
2628	}
2629
2630	#[test]
2631	fn seats_should_be_released_when_no_vote() {
2632		ExtBuilder::default().build_and_execute(|| {
2633			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2634			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2635			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2636
2637			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2638			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2639			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2640			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2641
2642			assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2643
2644			assert_eq!(ElectionRounds::<Test>::get(), 0);
2645
2646			System::set_block_number(5);
2647			Elections::on_initialize(System::block_number());
2648			assert_eq!(members_ids(), vec![3, 5]);
2649			assert_eq!(ElectionRounds::<Test>::get(), 1);
2650
2651			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2652			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(3)));
2653			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2654			assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2655
2656			// meanwhile, no one cares to become a candidate again.
2657			System::set_block_number(10);
2658			Elections::on_initialize(System::block_number());
2659			assert!(members_ids().is_empty());
2660			assert_eq!(ElectionRounds::<Test>::get(), 2);
2661		});
2662	}
2663
2664	#[test]
2665	fn incoming_outgoing_are_reported() {
2666		ExtBuilder::default().build_and_execute(|| {
2667			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2668			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2669
2670			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2671			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2672
2673			System::set_block_number(5);
2674			Elections::on_initialize(System::block_number());
2675			assert_eq!(members_ids(), vec![4, 5]);
2676
2677			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
2678			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2679			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2680
2681			// 5 will change their vote and becomes an `outgoing`
2682			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 8));
2683			// 4 will stay in the set
2684			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2685			// 3 will become a winner
2686			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2687			// these two are losers.
2688			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2689			assert_ok!(vote(RuntimeOrigin::signed(1), vec![1], 10));
2690
2691			System::set_block_number(10);
2692			Elections::on_initialize(System::block_number());
2693
2694			// 3, 4 are new members, must still be bonded, nothing slashed.
2695			assert_eq!(members_and_stake(), vec![(3, 25), (4, 43)]);
2696			assert_eq!(balances(&3), (25, 5));
2697			assert_eq!(balances(&4), (35, 5));
2698
2699			// 1 is a loser, slashed by 3.
2700			assert_eq!(balances(&1), (5, 2));
2701
2702			// 5 is an outgoing loser. will also get slashed.
2703			assert_eq!(balances(&5), (45, 2));
2704
2705			System::assert_has_event(RuntimeEvent::Elections(super::Event::NewTerm {
2706				new_members: vec![(4, 35), (5, 45)],
2707			}));
2708		})
2709	}
2710
2711	#[test]
2712	fn invalid_votes_are_moot() {
2713		ExtBuilder::default().build_and_execute(|| {
2714			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2715			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2716
2717			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2718			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2719			assert_ok!(vote(RuntimeOrigin::signed(5), vec![10], 50));
2720
2721			System::set_block_number(5);
2722			Elections::on_initialize(System::block_number());
2723
2724			assert_eq_uvec!(members_ids(), vec![3, 4]);
2725			assert_eq!(ElectionRounds::<Test>::get(), 1);
2726		});
2727	}
2728
2729	#[test]
2730	fn members_are_sorted_based_on_id_runners_on_merit() {
2731		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2732			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2733			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2734			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2735			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2736
2737			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2738			assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2739			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2740			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2741
2742			System::set_block_number(5);
2743			Elections::on_initialize(System::block_number());
2744			// id: low -> high.
2745			assert_eq!(members_and_stake(), vec![(4, 45), (5, 35)]);
2746			// merit: low -> high.
2747			assert_eq!(runners_up_and_stake(), vec![(3, 15), (2, 25)]);
2748		});
2749	}
2750
2751	#[test]
2752	fn runner_up_replacement_maintains_members_order() {
2753		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2754			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2755			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2756			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2757
2758			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2759			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2760			assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2761
2762			System::set_block_number(5);
2763			Elections::on_initialize(System::block_number());
2764
2765			assert_eq!(members_ids(), vec![2, 4]);
2766			assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 2, true, false));
2767			assert_eq!(members_ids(), vec![4, 5]);
2768		});
2769	}
2770
2771	#[test]
2772	fn can_renounce_candidacy_member_with_runners_bond_is_refunded() {
2773		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2774			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2775			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2776			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2777			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2778
2779			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2780			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2781			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2782			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2783
2784			System::set_block_number(5);
2785			Elections::on_initialize(System::block_number());
2786
2787			assert_eq!(members_ids(), vec![4, 5]);
2788			assert_eq!(runners_up_ids(), vec![2, 3]);
2789
2790			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2791			assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
2792
2793			assert_eq!(members_ids(), vec![3, 5]);
2794			assert_eq!(runners_up_ids(), vec![2]);
2795		})
2796	}
2797
2798	#[test]
2799	fn can_renounce_candidacy_member_without_runners_bond_is_refunded() {
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
2804			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2805			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2806
2807			System::set_block_number(5);
2808			Elections::on_initialize(System::block_number());
2809
2810			assert_eq!(members_ids(), vec![4, 5]);
2811			assert!(runners_up_ids().is_empty());
2812
2813			assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2814			assert_eq!(balances(&4), (38, 2)); // 2 is voting bond.
2815
2816			// no replacement
2817			assert_eq!(members_ids(), vec![5]);
2818			assert!(runners_up_ids().is_empty());
2819		})
2820	}
2821
2822	#[test]
2823	fn can_renounce_candidacy_runner_up() {
2824		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2825			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2826			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2827			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2828			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2829
2830			assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2831			assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2832			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2833			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2834
2835			System::set_block_number(5);
2836			Elections::on_initialize(System::block_number());
2837
2838			assert_eq!(members_ids(), vec![4, 5]);
2839			assert_eq!(runners_up_ids(), vec![2, 3]);
2840
2841			assert_ok!(Elections::renounce_candidacy(
2842				RuntimeOrigin::signed(3),
2843				Renouncing::RunnerUp
2844			));
2845			assert_eq!(balances(&3), (28, 2)); // 2 is voting bond.
2846
2847			assert_eq!(members_ids(), vec![4, 5]);
2848			assert_eq!(runners_up_ids(), vec![2]);
2849		})
2850	}
2851
2852	#[test]
2853	fn runner_up_replacement_works_when_out_of_order() {
2854		ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2855			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2856			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2857			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2858			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2859
2860			assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2861			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2862			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2863			assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2864
2865			System::set_block_number(5);
2866			Elections::on_initialize(System::block_number());
2867
2868			assert_eq!(members_ids(), vec![2, 4]);
2869			assert_eq!(runners_up_ids(), vec![5, 3]);
2870			assert_ok!(Elections::renounce_candidacy(
2871				RuntimeOrigin::signed(3),
2872				Renouncing::RunnerUp
2873			));
2874			assert_eq!(members_ids(), vec![2, 4]);
2875			assert_eq!(runners_up_ids(), vec![5]);
2876		});
2877	}
2878
2879	#[test]
2880	fn can_renounce_candidacy_candidate() {
2881		ExtBuilder::default().build_and_execute(|| {
2882			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2883			assert_eq!(balances(&5), (47, 3));
2884			assert_eq!(candidate_ids(), vec![5]);
2885
2886			assert_ok!(Elections::renounce_candidacy(
2887				RuntimeOrigin::signed(5),
2888				Renouncing::Candidate(1)
2889			));
2890			assert_eq!(balances(&5), (50, 0));
2891			assert!(candidate_ids().is_empty());
2892		})
2893	}
2894
2895	#[test]
2896	fn wrong_renounce_candidacy_should_fail() {
2897		ExtBuilder::default().build_and_execute(|| {
2898			assert_noop!(
2899				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Candidate(0)),
2900				Error::<Test>::InvalidRenouncing,
2901			);
2902			assert_noop!(
2903				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member),
2904				Error::<Test>::InvalidRenouncing,
2905			);
2906			assert_noop!(
2907				Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::RunnerUp),
2908				Error::<Test>::InvalidRenouncing,
2909			);
2910		})
2911	}
2912
2913	#[test]
2914	fn non_member_renounce_member_should_fail() {
2915		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2916			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2917			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2918			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2919
2920			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2921			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2922			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2923
2924			System::set_block_number(5);
2925			Elections::on_initialize(System::block_number());
2926
2927			assert_eq!(members_ids(), vec![4, 5]);
2928			assert_eq!(runners_up_ids(), vec![3]);
2929
2930			assert_noop!(
2931				Elections::renounce_candidacy(RuntimeOrigin::signed(3), Renouncing::Member),
2932				Error::<Test>::InvalidRenouncing,
2933			);
2934		})
2935	}
2936
2937	#[test]
2938	fn non_runner_up_renounce_runner_up_should_fail() {
2939		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2940			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2941			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2942			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2943
2944			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2945			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2946			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2947
2948			System::set_block_number(5);
2949			Elections::on_initialize(System::block_number());
2950
2951			assert_eq!(members_ids(), vec![4, 5]);
2952			assert_eq!(runners_up_ids(), vec![3]);
2953
2954			assert_noop!(
2955				Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::RunnerUp),
2956				Error::<Test>::InvalidRenouncing,
2957			);
2958		})
2959	}
2960
2961	#[test]
2962	fn wrong_candidate_count_renounce_should_fail() {
2963		ExtBuilder::default().build_and_execute(|| {
2964			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2965			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2966			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2967
2968			assert_noop!(
2969				Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Candidate(2)),
2970				Error::<Test>::InvalidWitnessData,
2971			);
2972
2973			assert_ok!(Elections::renounce_candidacy(
2974				RuntimeOrigin::signed(4),
2975				Renouncing::Candidate(3)
2976			));
2977		})
2978	}
2979
2980	#[test]
2981	fn renounce_candidacy_count_can_overestimate() {
2982		ExtBuilder::default().build_and_execute(|| {
2983			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2984			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2985			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2986			// while we have only 3 candidates.
2987			assert_ok!(Elections::renounce_candidacy(
2988				RuntimeOrigin::signed(4),
2989				Renouncing::Candidate(4)
2990			));
2991		})
2992	}
2993
2994	#[test]
2995	fn unsorted_runners_up_are_detected() {
2996		ExtBuilder::default()
2997			.desired_runners_up(2)
2998			.desired_members(1)
2999			.build_and_execute(|| {
3000				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3001				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3002				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3003
3004				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3005				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3006				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 15));
3007
3008				System::set_block_number(5);
3009				Elections::on_initialize(System::block_number());
3010
3011				assert_eq!(members_ids(), vec![5]);
3012				assert_eq!(runners_up_ids(), vec![4, 3]);
3013
3014				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3015				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 10));
3016
3017				System::set_block_number(10);
3018				Elections::on_initialize(System::block_number());
3019
3020				assert_eq!(members_ids(), vec![5]);
3021				assert_eq!(runners_up_ids(), vec![2, 3]);
3022
3023				// 4 is outgoing runner-up. Slash candidacy bond.
3024				assert_eq!(balances(&4), (35, 2));
3025				// 3 stays.
3026				assert_eq!(balances(&3), (25, 5));
3027			})
3028	}
3029
3030	#[test]
3031	fn member_to_runner_up_wont_slash() {
3032		ExtBuilder::default()
3033			.desired_runners_up(2)
3034			.desired_members(1)
3035			.build_and_execute(|| {
3036				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3037				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3038				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3039
3040				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3041				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3042				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3043
3044				System::set_block_number(5);
3045				Elections::on_initialize(System::block_number());
3046
3047				assert_eq!(members_ids(), vec![4]);
3048				assert_eq!(runners_up_ids(), vec![2, 3]);
3049
3050				assert_eq!(balances(&4), (35, 5));
3051				assert_eq!(balances(&3), (25, 5));
3052				assert_eq!(balances(&2), (15, 5));
3053
3054				// this guy will shift everyone down.
3055				assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3056				assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3057
3058				System::set_block_number(10);
3059				Elections::on_initialize(System::block_number());
3060
3061				assert_eq!(members_ids(), vec![5]);
3062				assert_eq!(runners_up_ids(), vec![3, 4]);
3063
3064				// 4 went from member to runner-up -- don't slash.
3065				assert_eq!(balances(&4), (35, 5));
3066				// 3 stayed runner-up -- don't slash.
3067				assert_eq!(balances(&3), (25, 5));
3068				// 2 was removed -- slash.
3069				assert_eq!(balances(&2), (15, 2));
3070			});
3071	}
3072
3073	#[test]
3074	fn runner_up_to_member_wont_slash() {
3075		ExtBuilder::default()
3076			.desired_runners_up(2)
3077			.desired_members(1)
3078			.build_and_execute(|| {
3079				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3080				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3081				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3082
3083				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3084				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3085				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3086
3087				System::set_block_number(5);
3088				Elections::on_initialize(System::block_number());
3089
3090				assert_eq!(members_ids(), vec![4]);
3091				assert_eq!(runners_up_ids(), vec![2, 3]);
3092
3093				assert_eq!(balances(&4), (35, 5));
3094				assert_eq!(balances(&3), (25, 5));
3095				assert_eq!(balances(&2), (15, 5));
3096
3097				// swap some votes.
3098				assert_ok!(vote(RuntimeOrigin::signed(4), vec![2], 40));
3099				assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
3100
3101				System::set_block_number(10);
3102				Elections::on_initialize(System::block_number());
3103
3104				assert_eq!(members_ids(), vec![2]);
3105				assert_eq!(runners_up_ids(), vec![4, 3]);
3106
3107				// 2 went from runner to member, don't slash
3108				assert_eq!(balances(&2), (15, 5));
3109				// 4 went from member to runner, don't slash
3110				assert_eq!(balances(&4), (35, 5));
3111				// 3 stayed the same
3112				assert_eq!(balances(&3), (25, 5));
3113			});
3114	}
3115
3116	#[test]
3117	fn remove_and_replace_member_works() {
3118		let setup = || {
3119			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3120			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3121			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3122
3123			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3124			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3125			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3126
3127			System::set_block_number(5);
3128			Elections::on_initialize(System::block_number());
3129
3130			assert_eq!(members_ids(), vec![4, 5]);
3131			assert_eq!(runners_up_ids(), vec![3]);
3132		};
3133
3134		// member removed, replacement found.
3135		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3136			setup();
3137			assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(true));
3138
3139			assert_eq!(members_ids(), vec![3, 5]);
3140			assert_eq!(runners_up_ids().len(), 0);
3141		});
3142
3143		// member removed, no replacement found.
3144		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3145			setup();
3146			assert_ok!(Elections::renounce_candidacy(
3147				RuntimeOrigin::signed(3),
3148				Renouncing::RunnerUp
3149			));
3150			assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(false));
3151
3152			assert_eq!(members_ids(), vec![5]);
3153			assert_eq!(runners_up_ids().len(), 0);
3154		});
3155
3156		// wrong member to remove.
3157		ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3158			setup();
3159			assert!(matches!(Elections::remove_and_replace_member(&2, false), Err(_)));
3160		});
3161	}
3162
3163	#[test]
3164	fn no_desired_members() {
3165		// not interested in anything
3166		ExtBuilder::default()
3167			.desired_members(0)
3168			.desired_runners_up(0)
3169			.build_and_execute(|| {
3170				assert_eq!(Candidates::<Test>::get().len(), 0);
3171
3172				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3173				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3174				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3175
3176				assert_eq!(Candidates::<Test>::get().len(), 3);
3177
3178				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3179				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3180				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3181
3182				System::set_block_number(5);
3183				Elections::on_initialize(System::block_number());
3184
3185				assert_eq!(members_ids().len(), 0);
3186				assert_eq!(runners_up_ids().len(), 0);
3187				assert_eq!(all_voters().len(), 3);
3188				assert_eq!(Candidates::<Test>::get().len(), 0);
3189			});
3190
3191		// not interested in members
3192		ExtBuilder::default()
3193			.desired_members(0)
3194			.desired_runners_up(2)
3195			.build_and_execute(|| {
3196				assert_eq!(Candidates::<Test>::get().len(), 0);
3197
3198				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3199				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3200				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3201
3202				assert_eq!(Candidates::<Test>::get().len(), 3);
3203
3204				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3205				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3206				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3207
3208				System::set_block_number(5);
3209				Elections::on_initialize(System::block_number());
3210
3211				assert_eq!(members_ids().len(), 0);
3212				assert_eq!(runners_up_ids(), vec![3, 4]);
3213				assert_eq!(all_voters().len(), 3);
3214				assert_eq!(Candidates::<Test>::get().len(), 0);
3215			});
3216
3217		// not interested in runners-up
3218		ExtBuilder::default()
3219			.desired_members(2)
3220			.desired_runners_up(0)
3221			.build_and_execute(|| {
3222				assert_eq!(Candidates::<Test>::get().len(), 0);
3223
3224				assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3225				assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3226				assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3227
3228				assert_eq!(Candidates::<Test>::get().len(), 3);
3229
3230				assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3231				assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3232				assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3233
3234				System::set_block_number(5);
3235				Elections::on_initialize(System::block_number());
3236
3237				assert_eq!(members_ids(), vec![3, 4]);
3238				assert_eq!(runners_up_ids().len(), 0);
3239				assert_eq!(all_voters().len(), 3);
3240				assert_eq!(Candidates::<Test>::get().len(), 0);
3241			});
3242	}
3243
3244	#[test]
3245	fn dupe_vote_is_moot() {
3246		ExtBuilder::default().desired_members(1).build_and_execute(|| {
3247			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3248			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3249			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3250			assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3251			assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
3252
3253			// all these duplicate votes will not cause 2 to win.
3254			assert_ok!(vote(RuntimeOrigin::signed(1), vec![2, 2, 2, 2], 5));
3255			assert_ok!(vote(RuntimeOrigin::signed(2), vec![2, 2, 2, 2], 20));
3256
3257			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3258
3259			System::set_block_number(5);
3260			Elections::on_initialize(System::block_number());
3261
3262			assert_eq!(members_ids(), vec![3]);
3263		})
3264	}
3265
3266	#[test]
3267	fn remove_defunct_voter_works() {
3268		ExtBuilder::default().build_and_execute(|| {
3269			assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3270			assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3271			assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3272
3273			// defunct
3274			assert_ok!(vote(RuntimeOrigin::signed(5), vec![5, 4], 5));
3275			// defunct
3276			assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3277			// ok
3278			assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 5));
3279			// ok
3280			assert_ok!(vote(RuntimeOrigin::signed(2), vec![3, 4], 5));
3281
3282			assert_ok!(Elections::renounce_candidacy(
3283				RuntimeOrigin::signed(5),
3284				Renouncing::Candidate(3)
3285			));
3286			assert_ok!(Elections::renounce_candidacy(
3287				RuntimeOrigin::signed(4),
3288				Renouncing::Candidate(2)
3289			));
3290			assert_ok!(Elections::renounce_candidacy(
3291				RuntimeOrigin::signed(3),
3292				Renouncing::Candidate(1)
3293			));
3294
3295			assert_ok!(Elections::clean_defunct_voters(RuntimeOrigin::root(), 4, 2));
3296		})
3297	}
3298}