referrerpolicy=no-referrer-when-downgrade

pallet_elections_phragmen/
lib.rs

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