referrerpolicy=no-referrer-when-downgrade

pallet_elections_phragmen/
lib.rs

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