1#![cfg_attr(not(feature = "std"), no_std)]
100
101extern crate alloc;
102
103use alloc::{vec, vec::Vec};
104use codec::{Decode, DecodeWithMemTracking, Encode};
105use core::cmp::Ordering;
106use frame_support::{
107 traits::{
108 defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, Get,
109 InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency,
110 SortedMembers, WithdrawReasons,
111 },
112 weights::Weight,
113};
114use log;
115use scale_info::TypeInfo;
116use sp_npos_elections::{ElectionResult, ExtendedBalance};
117use sp_runtime::{
118 traits::{Saturating, StaticLookup, Zero},
119 DispatchError, Perbill, RuntimeDebug,
120};
121use sp_staking::currency_to_vote::CurrencyToVote;
122
123#[cfg(any(feature = "try-runtime", test))]
124use sp_runtime::TryRuntimeError;
125
126mod benchmarking;
127pub mod weights;
128pub use weights::WeightInfo;
129
130pub 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#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, RuntimeDebug, TypeInfo)]
143pub enum Renouncing {
144 Member,
146 RunnerUp,
148 Candidate(#[codec(compact)] u32),
150}
151
152#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, TypeInfo)]
154pub struct Voter<AccountId, Balance> {
155 pub votes: Vec<AccountId>,
157 pub stake: Balance,
159 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#[derive(Encode, Decode, Clone, Default, RuntimeDebug, PartialEq, TypeInfo)]
173pub struct SeatHolder<AccountId, Balance> {
174 pub who: AccountId,
176 pub stake: Balance,
178 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 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 #[pallet::constant]
209 type PalletId: Get<LockIdentifier>;
210
211 type Currency: LockableCurrency<Self::AccountId, Moment = BlockNumberFor<Self>>
213 + ReservableCurrency<Self::AccountId>;
214
215 type ChangeMembers: ChangeMembers<Self::AccountId>;
217
218 type InitializeMembers: InitializeMembers<Self::AccountId>;
220
221 type CurrencyToVote: CurrencyToVote<BalanceOf<Self>>;
224
225 #[pallet::constant]
227 type CandidacyBond: Get<BalanceOf<Self>>;
228
229 #[pallet::constant]
234 type VotingBondBase: Get<BalanceOf<Self>>;
235
236 #[pallet::constant]
238 type VotingBondFactor: Get<BalanceOf<Self>>;
239
240 type LoserCandidate: OnUnbalanced<NegativeImbalanceOf<Self>>;
242
243 type KickedMember: OnUnbalanced<NegativeImbalanceOf<Self>>;
245
246 #[pallet::constant]
248 type DesiredMembers: Get<u32>;
249
250 #[pallet::constant]
252 type DesiredRunnersUp: Get<u32>;
253
254 #[pallet::constant]
258 type TermDuration: Get<BlockNumberFor<Self>>;
259
260 #[pallet::constant]
267 type MaxCandidates: Get<u32>;
268
269 #[pallet::constant]
276 type MaxVoters: Get<u32>;
277
278 #[pallet::constant]
283 type MaxVotesPerVoter: Get<u32>;
284
285 type WeightInfo: WeightInfo;
287 }
288
289 #[pallet::hooks]
290 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
291 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 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 #[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 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 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 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 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 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 #[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 #[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 #[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 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 #[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 Ok(())
586 }
587
588 #[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 NewTerm { new_members: Vec<(<T as frame_system::Config>::AccountId, BalanceOf<T>)> },
625 EmptyTerm,
628 ElectionError,
630 MemberKicked { member: <T as frame_system::Config>::AccountId },
633 Renounced { candidate: <T as frame_system::Config>::AccountId },
635 CandidateSlashed { candidate: <T as frame_system::Config>::AccountId, amount: BalanceOf<T> },
640 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 UnableToVote,
651 NoVotes,
653 TooManyVotes,
655 MaximumVotesExceeded,
657 LowBalance,
659 UnableToPayBond,
661 MustBeVoter,
663 DuplicatedCandidate,
665 TooManyCandidates,
667 MemberSubmit,
669 RunnerUpSubmit,
671 InsufficientCandidateFunds,
673 NotMember,
675 InvalidWitnessData,
677 InvalidVoteCount,
679 InvalidRenouncing,
681 InvalidReplacement,
683 }
684
685 #[pallet::storage]
689 pub type Members<T: Config> =
690 StorageValue<_, Vec<SeatHolder<T::AccountId, BalanceOf<T>>>, ValueQuery>;
691
692 #[pallet::storage]
697 pub type RunnersUp<T: Config> =
698 StorageValue<_, Vec<SeatHolder<T::AccountId, BalanceOf<T>>>, ValueQuery>;
699
700 #[pallet::storage]
707 pub type Candidates<T: Config> = StorageValue<_, Vec<(T::AccountId, BalanceOf<T>)>, ValueQuery>;
708
709 #[pallet::storage]
711 pub type ElectionRounds<T: Config> = StorageValue<_, u32, ValueQuery>;
712
713 #[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 assert!(
739 T::Currency::free_balance(member) >= *stake,
740 "Genesis member does not have enough stake.",
741 );
742
743 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 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 T::InitializeMembers::initialize_members(&members);
781 }
782 }
783}
784
785impl<T: Config> Pallet<T> {
786 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 fn remove_and_replace_member(who: &T::AccountId, slash: bool) -> Result<bool, DispatchError> {
809 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 let removed = members.remove(remove_index);
819
820 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 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 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 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 let Some(current_prime) = maybe_current_prime {
874 if ¤t_prime != who {
875 T::ChangeMembers::set_prime(Some(current_prime));
876 }
877 }
878
879 Ok(return_value)
880 }
881
882 fn is_candidate(who: &T::AccountId) -> Result<(), usize> {
885 Candidates::<T>::get().binary_search_by(|c| c.0.cmp(who)).map(|_| ())
886 }
887
888 fn is_voter(who: &T::AccountId) -> bool {
890 Voting::<T>::contains_key(who)
891 }
892
893 fn is_member(who: &T::AccountId) -> bool {
895 Members::<T>::get().binary_search_by(|m| m.who.cmp(who)).is_ok()
896 }
897
898 fn is_runner_up(who: &T::AccountId) -> bool {
900 RunnersUp::<T>::get().iter().any(|r| &r.who == who)
901 }
902
903 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 fn implicit_candidates_with_deposit() -> Vec<(T::AccountId, BalanceOf<T>)> {
912 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 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 fn do_remove_voter(who: &T::AccountId) {
933 let Voter { deposit, .. } = Voting::<T>::take(who);
934
935 T::Currency::remove_lock(T::PalletId::get(), who);
937
938 let _remainder = T::Currency::unreserve(who, deposit);
941 debug_assert!(_remainder.is_zero());
942 }
943
944 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 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 let candidate_ids =
964 candidates_and_deposit.iter().map(|(x, _)| x).cloned().collect::<Vec<_>>();
965
966 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 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 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 let old_members_ids_sorted = Members::<T>::take()
1013 .into_iter()
1014 .map(|m| m.who)
1015 .collect::<Vec<T::AccountId>>();
1016 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 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 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 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 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 let prime = prime_votes.into_iter().max_by_key(|x| x.1).map(|x| x.0.clone());
1076
1077 let new_members_ids_sorted = new_members_sorted_by_id
1079 .iter()
1080 .map(|(m, _)| m.clone())
1081 .collect::<Vec<T::AccountId>>();
1082
1083 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 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 let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf<T> {
1112 candidates_and_deposit
1116 .iter()
1117 .find_map(|(c, d)| if c == x { Some(*d) } else { None })
1118 .defensive_unwrap_or_default()
1119 };
1120 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 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 #[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 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 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 fn try_state_runners_up() -> Result<(), TryRuntimeError> {
1230 let mut sorted = RunnersUp::<T>::get();
1231 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 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 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 fn try_state_members_disjoint() -> Result<(), TryRuntimeError> {
1267 match Self::intersects(&Pallet::<T>::members_ids(), &Self::candidates_ids()) &&
1268 Self::intersects(&Pallet::<T>::members_ids(), &Self::runners_up_ids())
1269 {
1270 true =>
1271 Err("Members set should be disjoint from candidates and runners-up sets".into()),
1272 false => Ok(()),
1273 }
1274 }
1275
1276 fn try_state_members_approval_stake() -> Result<(), TryRuntimeError> {
1280 match Members::<T>::get()
1281 .iter()
1282 .chain(RunnersUp::<T>::get().iter())
1283 .all(|s| s.stake != BalanceOf::<T>::zero())
1284 {
1285 true => Ok(()),
1286 false => Err("Members and RunnersUp must have approval stake".into()),
1287 }
1288 }
1289
1290 fn intersects<P: PartialEq>(a: &[P], b: &[P]) -> bool {
1291 a.iter().any(|e| b.contains(e))
1292 }
1293
1294 fn candidates_ids() -> Vec<T::AccountId> {
1295 Candidates::<T>::get().iter().map(|(x, _)| x).cloned().collect::<Vec<_>>()
1296 }
1297
1298 fn runners_up_ids() -> Vec<T::AccountId> {
1299 RunnersUp::<T>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1300 }
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305 use super::*;
1306 use crate as elections_phragmen;
1307 use frame_support::{
1308 assert_noop, assert_ok, derive_impl,
1309 dispatch::DispatchResultWithPostInfo,
1310 parameter_types,
1311 traits::{ConstU32, OnInitialize},
1312 };
1313 use frame_system::ensure_signed;
1314 use sp_runtime::{testing::Header, BuildStorage};
1315 use substrate_test_utils::assert_eq_uvec;
1316
1317 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
1318 impl frame_system::Config for Test {
1319 type Block = Block;
1320 type AccountData = pallet_balances::AccountData<u64>;
1321 }
1322
1323 #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
1324 impl pallet_balances::Config for Test {
1325 type AccountStore = frame_system::Pallet<Test>;
1326 }
1327
1328 frame_support::parameter_types! {
1329 pub static VotingBondBase: u64 = 2;
1330 pub static VotingBondFactor: u64 = 0;
1331 pub static CandidacyBond: u64 = 3;
1332 pub static DesiredMembers: u32 = 2;
1333 pub static DesiredRunnersUp: u32 = 0;
1334 pub static TermDuration: u64 = 5;
1335 pub static Members: Vec<u64> = vec![];
1336 pub static Prime: Option<u64> = None;
1337 }
1338
1339 pub struct TestChangeMembers;
1340 impl ChangeMembers<u64> for TestChangeMembers {
1341 fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) {
1342 let mut new_sorted = new.to_vec();
1344 new_sorted.sort();
1345 assert_eq!(new, &new_sorted[..]);
1346
1347 let mut incoming_sorted = incoming.to_vec();
1348 incoming_sorted.sort();
1349 assert_eq!(incoming, &incoming_sorted[..]);
1350
1351 let mut outgoing_sorted = outgoing.to_vec();
1352 outgoing_sorted.sort();
1353 assert_eq!(outgoing, &outgoing_sorted[..]);
1354
1355 for x in incoming.iter() {
1357 assert!(outgoing.binary_search(x).is_err());
1358 }
1359
1360 let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec());
1361 old_plus_incoming.extend_from_slice(incoming);
1362 old_plus_incoming.sort();
1363
1364 let mut new_plus_outgoing = new.to_vec();
1365 new_plus_outgoing.extend_from_slice(outgoing);
1366 new_plus_outgoing.sort();
1367
1368 assert_eq!(old_plus_incoming, new_plus_outgoing, "change members call is incorrect!");
1369
1370 MEMBERS.with(|m| *m.borrow_mut() = new.to_vec());
1371 PRIME.with(|p| *p.borrow_mut() = None);
1372 }
1373
1374 fn set_prime(who: Option<u64>) {
1375 PRIME.with(|p| *p.borrow_mut() = who);
1376 }
1377
1378 fn get_prime() -> Option<u64> {
1379 PRIME.with(|p| *p.borrow())
1380 }
1381 }
1382
1383 parameter_types! {
1384 pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect";
1385 pub const PhragmenMaxVoters: u32 = 1000;
1386 pub const PhragmenMaxCandidates: u32 = 100;
1387 }
1388
1389 impl Config for Test {
1390 type PalletId = ElectionsPhragmenPalletId;
1391 type RuntimeEvent = RuntimeEvent;
1392 type Currency = Balances;
1393 type CurrencyToVote = ();
1394 type ChangeMembers = TestChangeMembers;
1395 type InitializeMembers = ();
1396 type CandidacyBond = CandidacyBond;
1397 type VotingBondBase = VotingBondBase;
1398 type VotingBondFactor = VotingBondFactor;
1399 type TermDuration = TermDuration;
1400 type DesiredMembers = DesiredMembers;
1401 type DesiredRunnersUp = DesiredRunnersUp;
1402 type LoserCandidate = ();
1403 type KickedMember = ();
1404 type WeightInfo = ();
1405 type MaxVoters = PhragmenMaxVoters;
1406 type MaxVotesPerVoter = ConstU32<16>;
1407 type MaxCandidates = PhragmenMaxCandidates;
1408 }
1409
1410 pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
1411 pub type UncheckedExtrinsic =
1412 sp_runtime::generic::UncheckedExtrinsic<u32, RuntimeCall, u64, ()>;
1413
1414 frame_support::construct_runtime!(
1415 pub enum Test
1416 {
1417 System: frame_system,
1418 Balances: pallet_balances,
1419 Elections: elections_phragmen,
1420 }
1421 );
1422
1423 pub struct ExtBuilder {
1424 balance_factor: u64,
1425 genesis_members: Vec<(u64, u64)>,
1426 }
1427
1428 impl Default for ExtBuilder {
1429 fn default() -> Self {
1430 Self { balance_factor: 1, genesis_members: vec![] }
1431 }
1432 }
1433
1434 impl ExtBuilder {
1435 pub fn voter_bond(self, bond: u64) -> Self {
1436 VOTING_BOND_BASE.with(|v| *v.borrow_mut() = bond);
1437 self
1438 }
1439 pub fn voter_bond_factor(self, bond: u64) -> Self {
1440 VOTING_BOND_FACTOR.with(|v| *v.borrow_mut() = bond);
1441 self
1442 }
1443 pub fn desired_runners_up(self, count: u32) -> Self {
1444 DESIRED_RUNNERS_UP.with(|v| *v.borrow_mut() = count);
1445 self
1446 }
1447 pub fn term_duration(self, duration: u64) -> Self {
1448 TERM_DURATION.with(|v| *v.borrow_mut() = duration);
1449 self
1450 }
1451 pub fn genesis_members(mut self, members: Vec<(u64, u64)>) -> Self {
1452 MEMBERS.with(|m| *m.borrow_mut() = members.iter().map(|(m, _)| *m).collect::<Vec<_>>());
1453 self.genesis_members = members;
1454 self
1455 }
1456 pub fn desired_members(self, count: u32) -> Self {
1457 DESIRED_MEMBERS.with(|m| *m.borrow_mut() = count);
1458 self
1459 }
1460 pub fn balance_factor(mut self, factor: u64) -> Self {
1461 self.balance_factor = factor;
1462 self
1463 }
1464 pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
1465 sp_tracing::try_init_simple();
1466 MEMBERS.with(|m| {
1467 *m.borrow_mut() = self.genesis_members.iter().map(|(m, _)| *m).collect::<Vec<_>>()
1468 });
1469 let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig {
1470 system: frame_system::GenesisConfig::default(),
1471 balances: pallet_balances::GenesisConfig::<Test> {
1472 balances: vec![
1473 (1, 10 * self.balance_factor),
1474 (2, 20 * self.balance_factor),
1475 (3, 30 * self.balance_factor),
1476 (4, 40 * self.balance_factor),
1477 (5, 50 * self.balance_factor),
1478 (6, 60 * self.balance_factor),
1479 ],
1480 ..Default::default()
1481 },
1482 elections: elections_phragmen::GenesisConfig::<Test> {
1483 members: self.genesis_members,
1484 },
1485 }
1486 .build_storage()
1487 .unwrap()
1488 .into();
1489 ext.execute_with(pre_conditions);
1490 ext.execute_with(test);
1491
1492 #[cfg(feature = "try-runtime")]
1493 ext.execute_with(|| {
1494 assert_ok!(<Elections as frame_support::traits::Hooks<u64>>::try_state(
1495 System::block_number()
1496 ));
1497 });
1498 }
1499 }
1500
1501 fn candidate_ids() -> Vec<u64> {
1502 Candidates::<Test>::get().into_iter().map(|(c, _)| c).collect::<Vec<_>>()
1503 }
1504
1505 fn candidate_deposit(who: &u64) -> u64 {
1506 Candidates::<Test>::get()
1507 .into_iter()
1508 .find_map(|(c, d)| if c == *who { Some(d) } else { None })
1509 .unwrap_or_default()
1510 }
1511
1512 fn voter_deposit(who: &u64) -> u64 {
1513 Voting::<Test>::get(who).deposit
1514 }
1515
1516 fn runners_up_ids() -> Vec<u64> {
1517 RunnersUp::<Test>::get().into_iter().map(|r| r.who).collect::<Vec<_>>()
1518 }
1519
1520 fn members_ids() -> Vec<u64> {
1521 Elections::members_ids()
1522 }
1523
1524 fn members_and_stake() -> Vec<(u64, u64)> {
1525 elections_phragmen::Members::<Test>::get()
1526 .into_iter()
1527 .map(|m| (m.who, m.stake))
1528 .collect::<Vec<_>>()
1529 }
1530
1531 fn runners_up_and_stake() -> Vec<(u64, u64)> {
1532 RunnersUp::<Test>::get()
1533 .into_iter()
1534 .map(|r| (r.who, r.stake))
1535 .collect::<Vec<_>>()
1536 }
1537
1538 fn all_voters() -> Vec<u64> {
1539 Voting::<Test>::iter().map(|(v, _)| v).collect::<Vec<u64>>()
1540 }
1541
1542 fn balances(who: &u64) -> (u64, u64) {
1543 (Balances::free_balance(who), Balances::reserved_balance(who))
1544 }
1545
1546 fn has_lock(who: &u64) -> u64 {
1547 Balances::locks(who)
1548 .get(0)
1549 .cloned()
1550 .map(|lock| {
1551 assert_eq!(lock.id, ElectionsPhragmenPalletId::get());
1552 lock.amount
1553 })
1554 .unwrap_or_default()
1555 }
1556
1557 fn locked_stake_of(who: &u64) -> u64 {
1558 Voting::<Test>::get(who).stake
1559 }
1560
1561 fn pre_conditions() {
1562 System::set_block_number(1);
1563 Elections::do_try_state().unwrap();
1564 }
1565
1566 fn submit_candidacy(origin: RuntimeOrigin) -> sp_runtime::DispatchResult {
1567 Elections::submit_candidacy(origin, Candidates::<Test>::get().len() as u32)
1568 }
1569
1570 fn vote(origin: RuntimeOrigin, votes: Vec<u64>, stake: u64) -> DispatchResultWithPostInfo {
1571 ensure_signed(origin.clone()).expect("vote origin must be signed");
1575 Elections::vote(origin, votes, stake)
1576 }
1577
1578 fn votes_of(who: &u64) -> Vec<u64> {
1579 Voting::<Test>::get(who).votes
1580 }
1581
1582 #[test]
1583 fn params_should_work() {
1584 ExtBuilder::default().build_and_execute(|| {
1585 assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1586 assert_eq!(<Test as Config>::DesiredRunnersUp::get(), 0);
1587 assert_eq!(<Test as Config>::VotingBondBase::get(), 2);
1588 assert_eq!(<Test as Config>::VotingBondFactor::get(), 0);
1589 assert_eq!(<Test as Config>::CandidacyBond::get(), 3);
1590 assert_eq!(<Test as Config>::TermDuration::get(), 5);
1591 assert_eq!(ElectionRounds::<Test>::get(), 0);
1592
1593 assert!(elections_phragmen::Members::<Test>::get().is_empty());
1594 assert!(RunnersUp::<Test>::get().is_empty());
1595
1596 assert!(candidate_ids().is_empty());
1597 assert_eq!(Candidates::<Test>::decode_len(), None);
1598 assert!(Elections::is_candidate(&1).is_err());
1599
1600 assert!(all_voters().is_empty());
1601 assert!(votes_of(&1).is_empty());
1602 });
1603 }
1604
1605 #[test]
1606 fn genesis_members_should_work() {
1607 ExtBuilder::default()
1608 .genesis_members(vec![(1, 10), (2, 20)])
1609 .build_and_execute(|| {
1610 System::set_block_number(1);
1611 assert_eq!(
1612 elections_phragmen::Members::<Test>::get(),
1613 vec![
1614 SeatHolder { who: 1, stake: 10, deposit: 0 },
1615 SeatHolder { who: 2, stake: 20, deposit: 0 }
1616 ]
1617 );
1618
1619 assert_eq!(
1620 Voting::<Test>::get(1),
1621 Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1622 );
1623 assert_eq!(
1624 Voting::<Test>::get(2),
1625 Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1626 );
1627
1628 System::set_block_number(5);
1630 Elections::on_initialize(System::block_number());
1631
1632 assert_eq!(members_ids(), vec![1, 2]);
1633 })
1634 }
1635
1636 #[test]
1637 fn genesis_voters_can_remove_lock() {
1638 ExtBuilder::default()
1639 .genesis_members(vec![(1, 10), (2, 20)])
1640 .build_and_execute(|| {
1641 System::set_block_number(1);
1642
1643 assert_eq!(
1644 Voting::<Test>::get(1),
1645 Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1646 );
1647 assert_eq!(
1648 Voting::<Test>::get(2),
1649 Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1650 );
1651
1652 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(1)));
1653 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1654
1655 assert_eq!(Voting::<Test>::get(1), Default::default());
1656 assert_eq!(Voting::<Test>::get(2), Default::default());
1657 })
1658 }
1659
1660 #[test]
1661 fn genesis_members_unsorted_should_work() {
1662 ExtBuilder::default()
1663 .genesis_members(vec![(2, 20), (1, 10)])
1664 .build_and_execute(|| {
1665 System::set_block_number(1);
1666 assert_eq!(
1667 elections_phragmen::Members::<Test>::get(),
1668 vec![
1669 SeatHolder { who: 1, stake: 10, deposit: 0 },
1670 SeatHolder { who: 2, stake: 20, deposit: 0 },
1671 ]
1672 );
1673
1674 assert_eq!(
1675 Voting::<Test>::get(1),
1676 Voter { stake: 10u64, votes: vec![1], deposit: 0 }
1677 );
1678 assert_eq!(
1679 Voting::<Test>::get(2),
1680 Voter { stake: 20u64, votes: vec![2], deposit: 0 }
1681 );
1682
1683 System::set_block_number(5);
1685 Elections::on_initialize(System::block_number());
1686
1687 assert_eq!(members_ids(), vec![1, 2]);
1688 })
1689 }
1690
1691 #[test]
1692 #[should_panic = "Genesis member does not have enough stake"]
1693 fn genesis_members_cannot_over_stake_0() {
1694 ExtBuilder::default()
1696 .genesis_members(vec![(1, 20), (2, 20)])
1697 .build_and_execute(|| {});
1698 }
1699
1700 #[test]
1701 #[should_panic = "Duplicate member in elections-phragmen genesis: 2"]
1702 fn genesis_members_cannot_be_duplicate() {
1703 ExtBuilder::default()
1704 .desired_members(3)
1705 .genesis_members(vec![(1, 10), (2, 10), (2, 10)])
1706 .build_and_execute(|| {});
1707 }
1708
1709 #[test]
1710 #[should_panic = "Cannot accept more than DesiredMembers genesis member"]
1711 fn genesis_members_cannot_too_many() {
1712 ExtBuilder::default()
1713 .genesis_members(vec![(1, 10), (2, 10), (3, 30)])
1714 .desired_members(2)
1715 .build_and_execute(|| {});
1716 }
1717
1718 #[test]
1719 fn term_duration_zero_is_passive() {
1720 ExtBuilder::default().term_duration(0).build_and_execute(|| {
1721 assert_eq!(<Test as Config>::TermDuration::get(), 0);
1722 assert_eq!(<Test as Config>::DesiredMembers::get(), 2);
1723 assert_eq!(ElectionRounds::<Test>::get(), 0);
1724
1725 assert!(members_ids().is_empty());
1726 assert!(RunnersUp::<Test>::get().is_empty());
1727 assert!(candidate_ids().is_empty());
1728
1729 System::set_block_number(5);
1730 Elections::on_initialize(System::block_number());
1731
1732 assert!(members_ids().is_empty());
1733 assert!(RunnersUp::<Test>::get().is_empty());
1734 assert!(candidate_ids().is_empty());
1735 });
1736 }
1737
1738 #[test]
1739 fn simple_candidate_submission_should_work() {
1740 ExtBuilder::default().build_and_execute(|| {
1741 assert_eq!(candidate_ids(), Vec::<u64>::new());
1742 assert!(Elections::is_candidate(&1).is_err());
1743 assert!(Elections::is_candidate(&2).is_err());
1744
1745 assert_eq!(balances(&1), (10, 0));
1746 assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1747 assert_eq!(balances(&1), (7, 3));
1748
1749 assert_eq!(candidate_ids(), vec![1]);
1750
1751 assert!(Elections::is_candidate(&1).is_ok());
1752 assert!(Elections::is_candidate(&2).is_err());
1753
1754 assert_eq!(balances(&2), (20, 0));
1755 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1756 assert_eq!(balances(&2), (17, 3));
1757
1758 assert_eq!(candidate_ids(), vec![1, 2]);
1759
1760 assert!(Elections::is_candidate(&1).is_ok());
1761 assert!(Elections::is_candidate(&2).is_ok());
1762
1763 assert_eq!(candidate_deposit(&1), 3);
1764 assert_eq!(candidate_deposit(&2), 3);
1765 assert_eq!(candidate_deposit(&3), 0);
1766 });
1767 }
1768
1769 #[test]
1770 fn updating_candidacy_bond_works() {
1771 ExtBuilder::default().build_and_execute(|| {
1772 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1773 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
1774 assert_eq!(Candidates::<Test>::get(), vec![(5, 3)]);
1775
1776 CANDIDACY_BOND.with(|v| *v.borrow_mut() = 4);
1778
1779 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1780 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
1781 assert_eq!(Candidates::<Test>::get(), vec![(4, 4), (5, 3)]);
1782
1783 System::set_block_number(5);
1785 Elections::on_initialize(System::block_number());
1786
1787 assert_eq!(balances(&4), (34, 6));
1788 assert_eq!(balances(&5), (45, 5));
1789 assert_eq!(
1790 elections_phragmen::Members::<Test>::get(),
1791 vec![
1792 SeatHolder { who: 4, stake: 34, deposit: 4 },
1793 SeatHolder { who: 5, stake: 45, deposit: 3 },
1794 ]
1795 );
1796 })
1797 }
1798
1799 #[test]
1800 fn candidates_are_always_sorted() {
1801 ExtBuilder::default().build_and_execute(|| {
1802 assert_eq!(candidate_ids(), Vec::<u64>::new());
1803
1804 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1805 assert_eq!(candidate_ids(), vec![3]);
1806 assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1807 assert_eq!(candidate_ids(), vec![1, 3]);
1808 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
1809 assert_eq!(candidate_ids(), vec![1, 2, 3]);
1810 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1811 assert_eq!(candidate_ids(), vec![1, 2, 3, 4]);
1812 });
1813 }
1814
1815 #[test]
1816 fn dupe_candidate_submission_should_not_work() {
1817 ExtBuilder::default().build_and_execute(|| {
1818 assert_eq!(candidate_ids(), Vec::<u64>::new());
1819 assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
1820 assert_eq!(candidate_ids(), vec![1]);
1821 assert_noop!(
1822 submit_candidacy(RuntimeOrigin::signed(1)),
1823 Error::<Test>::DuplicatedCandidate
1824 );
1825 });
1826 }
1827
1828 #[test]
1829 fn member_candidacy_submission_should_not_work() {
1830 ExtBuilder::default().build_and_execute(|| {
1832 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1833 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1834
1835 System::set_block_number(5);
1836 Elections::on_initialize(System::block_number());
1837
1838 assert_eq!(members_ids(), vec![5]);
1839 assert!(RunnersUp::<Test>::get().is_empty());
1840 assert!(candidate_ids().is_empty());
1841
1842 assert_noop!(submit_candidacy(RuntimeOrigin::signed(5)), Error::<Test>::MemberSubmit);
1843 });
1844 }
1845
1846 #[test]
1847 fn runner_candidate_submission_should_not_work() {
1848 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
1849 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1850 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1851 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
1852
1853 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 20));
1854 assert_ok!(vote(RuntimeOrigin::signed(1), vec![3], 10));
1855
1856 System::set_block_number(5);
1857 Elections::on_initialize(System::block_number());
1858
1859 assert_eq!(members_ids(), vec![4, 5]);
1860 assert_eq!(runners_up_ids(), vec![3]);
1861
1862 assert_noop!(submit_candidacy(RuntimeOrigin::signed(3)), Error::<Test>::RunnerUpSubmit);
1863 });
1864 }
1865
1866 #[test]
1867 fn poor_candidate_submission_should_not_work() {
1868 ExtBuilder::default().build_and_execute(|| {
1869 assert_eq!(candidate_ids(), Vec::<u64>::new());
1870 assert_noop!(
1871 submit_candidacy(RuntimeOrigin::signed(7)),
1872 Error::<Test>::InsufficientCandidateFunds,
1873 );
1874 });
1875 }
1876
1877 #[test]
1878 fn simple_voting_should_work() {
1879 ExtBuilder::default().build_and_execute(|| {
1880 assert_eq!(candidate_ids(), Vec::<u64>::new());
1881 assert_eq!(balances(&2), (20, 0));
1882
1883 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1884 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1885
1886 assert_eq!(balances(&2), (18, 2));
1887 assert_eq!(has_lock(&2), 18);
1888 });
1889 }
1890
1891 #[test]
1892 fn can_vote_with_custom_stake() {
1893 ExtBuilder::default().build_and_execute(|| {
1894 assert_eq!(candidate_ids(), Vec::<u64>::new());
1895 assert_eq!(balances(&2), (20, 0));
1896
1897 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1898 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 12));
1899
1900 assert_eq!(balances(&2), (18, 2));
1901 assert_eq!(has_lock(&2), 12);
1902 });
1903 }
1904
1905 #[test]
1906 fn can_update_votes_and_stake() {
1907 ExtBuilder::default().build_and_execute(|| {
1908 assert_eq!(balances(&2), (20, 0));
1909
1910 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1911 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1912 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
1913
1914 assert_eq!(balances(&2), (18, 2));
1916 assert_eq!(has_lock(&2), 18);
1917 assert_eq!(locked_stake_of(&2), 18);
1918
1919 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1921 assert_eq!(balances(&2), (18, 2));
1922 assert_eq!(has_lock(&2), 15);
1923 assert_eq!(locked_stake_of(&2), 15);
1924 });
1925 }
1926
1927 #[test]
1928 fn updated_voting_bond_works() {
1929 ExtBuilder::default().build_and_execute(|| {
1930 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1931
1932 assert_eq!(balances(&2), (20, 0));
1933 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 5));
1934 assert_eq!(balances(&2), (18, 2));
1935 assert_eq!(voter_deposit(&2), 2);
1936
1937 VOTING_BOND_BASE.with(|v| *v.borrow_mut() = 1);
1940
1941 assert_eq!(balances(&1), (10, 0));
1943 assert_ok!(vote(RuntimeOrigin::signed(1), vec![5], 5));
1944 assert_eq!(balances(&1), (9, 1));
1945 assert_eq!(voter_deposit(&1), 1);
1946
1947 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
1948 assert_eq!(balances(&2), (20, 0));
1949 })
1950 }
1951
1952 #[test]
1953 fn voting_reserves_bond_per_vote() {
1954 ExtBuilder::default().voter_bond_factor(1).build_and_execute(|| {
1955 assert_eq!(balances(&2), (20, 0));
1956
1957 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
1958 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
1959
1960 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 10));
1962
1963 assert_eq!(balances(&2), (17, 3));
1965 assert_eq!(Voting::<Test>::get(&2).deposit, 3);
1966 assert_eq!(has_lock(&2), 10);
1967 assert_eq!(locked_stake_of(&2), 10);
1968
1969 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 4], 15));
1971 assert_eq!(balances(&2), (16, 4));
1973 assert_eq!(Voting::<Test>::get(&2).deposit, 4);
1974 assert_eq!(has_lock(&2), 15);
1975 assert_eq!(locked_stake_of(&2), 15);
1976
1977 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5, 3], 18));
1979 assert_eq!(balances(&2), (16, 4));
1981 assert_eq!(Voting::<Test>::get(&2).deposit, 4);
1982 assert_eq!(has_lock(&2), 16);
1983 assert_eq!(locked_stake_of(&2), 16);
1984
1985 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 12));
1987 assert_eq!(balances(&2), (17, 3));
1989 assert_eq!(Voting::<Test>::get(&2).deposit, 3);
1990 assert_eq!(has_lock(&2), 12);
1991 assert_eq!(locked_stake_of(&2), 12);
1992 });
1993 }
1994
1995 #[test]
1996 fn cannot_vote_for_no_candidate() {
1997 ExtBuilder::default().build_and_execute(|| {
1998 assert_noop!(vote(RuntimeOrigin::signed(2), vec![], 20), Error::<Test>::NoVotes);
1999 });
2000 }
2001
2002 #[test]
2003 fn can_vote_for_old_members_even_when_no_new_candidates() {
2004 ExtBuilder::default().build_and_execute(|| {
2005 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2006 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2007
2008 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 20));
2009
2010 System::set_block_number(5);
2011 Elections::on_initialize(System::block_number());
2012
2013 assert_eq!(members_ids(), vec![4, 5]);
2014 assert!(candidate_ids().is_empty());
2015
2016 assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2017 });
2018 }
2019
2020 #[test]
2021 fn prime_works() {
2022 ExtBuilder::default().build_and_execute(|| {
2023 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2024 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2025 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2026
2027 assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2028 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2029 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2030 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2031 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2032
2033 System::set_block_number(5);
2034 Elections::on_initialize(System::block_number());
2035
2036 assert_eq!(members_ids(), vec![4, 5]);
2037 assert!(candidate_ids().is_empty());
2038
2039 assert_ok!(vote(RuntimeOrigin::signed(3), vec![4, 5], 10));
2040 assert_eq!(PRIME.with(|p| *p.borrow()), Some(4));
2041 });
2042 }
2043
2044 #[test]
2045 fn prime_votes_for_exiting_members_are_removed() {
2046 ExtBuilder::default().build_and_execute(|| {
2047 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2048 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2049 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2050
2051 assert_ok!(vote(RuntimeOrigin::signed(1), vec![4, 3], 10));
2052 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
2053 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2054 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2055 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2056
2057 assert_ok!(Elections::renounce_candidacy(
2058 RuntimeOrigin::signed(4),
2059 Renouncing::Candidate(3)
2060 ));
2061
2062 System::set_block_number(5);
2063 Elections::on_initialize(System::block_number());
2064
2065 assert_eq!(members_ids(), vec![3, 5]);
2066 assert!(candidate_ids().is_empty());
2067
2068 assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2069 });
2070 }
2071
2072 #[test]
2073 fn prime_is_kept_if_other_members_leave() {
2074 ExtBuilder::default().build_and_execute(|| {
2075 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2076 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2077
2078 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2079 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2080
2081 System::set_block_number(5);
2082 Elections::on_initialize(System::block_number());
2083
2084 assert_eq!(members_ids(), vec![4, 5]);
2085 assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2086 assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2087
2088 assert_eq!(members_ids(), vec![5]);
2089 assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2090 })
2091 }
2092
2093 #[test]
2094 fn prime_is_gone_if_renouncing() {
2095 ExtBuilder::default().build_and_execute(|| {
2096 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2097 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2098
2099 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2100 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2101
2102 System::set_block_number(5);
2103 Elections::on_initialize(System::block_number());
2104
2105 assert_eq!(members_ids(), vec![4, 5]);
2106 assert_eq!(PRIME.with(|p| *p.borrow()), Some(5));
2107 assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member));
2108
2109 assert_eq!(members_ids(), vec![4]);
2110 assert_eq!(PRIME.with(|p| *p.borrow()), None);
2111 })
2112 }
2113
2114 #[test]
2115 fn cannot_vote_for_more_than_candidates_and_members_and_runners() {
2116 ExtBuilder::default()
2117 .desired_runners_up(1)
2118 .balance_factor(10)
2119 .build_and_execute(|| {
2120 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2122 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2123 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2124
2125 assert_noop!(
2126 vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5),
2128 Error::<Test>::TooManyVotes,
2129 );
2130
2131 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2132 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2133 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2134
2135 System::set_block_number(5);
2136 Elections::on_initialize(System::block_number());
2137
2138 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2140
2141 assert_ok!(vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9999], 5));
2142 assert_noop!(
2143 vote(RuntimeOrigin::signed(1), vec![9, 99, 999, 9_999, 99_999], 5),
2144 Error::<Test>::TooManyVotes,
2145 );
2146 });
2147 }
2148
2149 #[test]
2150 fn cannot_vote_for_less_than_ed() {
2151 ExtBuilder::default().build_and_execute(|| {
2152 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2153 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2154
2155 assert_noop!(vote(RuntimeOrigin::signed(2), vec![4], 1), Error::<Test>::LowBalance);
2156 })
2157 }
2158
2159 #[test]
2160 fn can_vote_for_more_than_free_balance_but_moot() {
2161 ExtBuilder::default().build_and_execute(|| {
2162 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2163 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2164
2165 assert_ok!(Balances::force_set_balance(RuntimeOrigin::root(), 2, 150));
2167 assert_ok!(Balances::reserve(&2, 50));
2168 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4, 5], 150));
2170 assert_eq!(locked_stake_of(&2), 98);
2172 assert_eq!(has_lock(&2), 98);
2173 });
2174 }
2175
2176 #[test]
2177 fn remove_voter_should_work() {
2178 ExtBuilder::default().voter_bond(8).build_and_execute(|| {
2179 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2180
2181 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2182 assert_ok!(vote(RuntimeOrigin::signed(3), vec![5], 30));
2183
2184 assert_eq_uvec!(all_voters(), vec![2, 3]);
2185 assert_eq!(balances(&2), (12, 8));
2186 assert_eq!(locked_stake_of(&2), 12);
2187 assert_eq!(balances(&3), (22, 8));
2188 assert_eq!(locked_stake_of(&3), 22);
2189 assert_eq!(votes_of(&2), vec![5]);
2190 assert_eq!(votes_of(&3), vec![5]);
2191
2192 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2193
2194 assert_eq_uvec!(all_voters(), vec![3]);
2195 assert!(votes_of(&2).is_empty());
2196 assert_eq!(locked_stake_of(&2), 0);
2197
2198 assert_eq!(balances(&2), (20, 0));
2199 assert_eq!(pallet_balances::Locks::<Test>::get(&2).len(), 0);
2200 });
2201 }
2202
2203 #[test]
2204 fn non_voter_remove_should_not_work() {
2205 ExtBuilder::default().build_and_execute(|| {
2206 assert_noop!(
2207 Elections::remove_voter(RuntimeOrigin::signed(3)),
2208 Error::<Test>::MustBeVoter
2209 );
2210 });
2211 }
2212
2213 #[test]
2214 fn dupe_remove_should_fail() {
2215 ExtBuilder::default().build_and_execute(|| {
2216 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2217 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2218
2219 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2220 assert!(all_voters().is_empty());
2221
2222 assert_noop!(
2223 Elections::remove_voter(RuntimeOrigin::signed(2)),
2224 Error::<Test>::MustBeVoter
2225 );
2226 });
2227 }
2228
2229 #[test]
2230 fn removed_voter_should_not_be_counted() {
2231 ExtBuilder::default().build_and_execute(|| {
2232 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2233 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2234 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2235
2236 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2237 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2238 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2239
2240 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2241
2242 System::set_block_number(5);
2243 Elections::on_initialize(System::block_number());
2244
2245 assert_eq!(members_ids(), vec![3, 5]);
2246 });
2247 }
2248
2249 #[test]
2250 fn simple_voting_rounds_should_work() {
2251 ExtBuilder::default().build_and_execute(|| {
2252 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2253 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2254 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2255
2256 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2257 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 15));
2258 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2259
2260 assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2261
2262 assert_eq!(votes_of(&2), vec![5]);
2263 assert_eq!(votes_of(&3), vec![3]);
2264 assert_eq!(votes_of(&4), vec![4]);
2265
2266 assert_eq!(candidate_ids(), vec![3, 4, 5]);
2267 assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2268
2269 assert_eq!(ElectionRounds::<Test>::get(), 0);
2270
2271 System::set_block_number(5);
2272 Elections::on_initialize(System::block_number());
2273
2274 assert_eq!(balances(&3), (25, 5));
2275 assert_eq!(balances(&2), (18, 2));
2277 assert_eq!(members_and_stake(), vec![(3, 25), (5, 18)]);
2278 assert!(RunnersUp::<Test>::get().is_empty());
2279
2280 assert_eq_uvec!(all_voters(), vec![2, 3, 4]);
2281 assert!(candidate_ids().is_empty());
2282 assert_eq!(Candidates::<Test>::decode_len(), None);
2283
2284 assert_eq!(ElectionRounds::<Test>::get(), 1);
2285 });
2286 }
2287
2288 #[test]
2289 fn empty_term() {
2290 ExtBuilder::default().build_and_execute(|| {
2291 System::set_block_number(5);
2293 Elections::on_initialize(System::block_number());
2294
2295 System::assert_last_event(RuntimeEvent::Elections(super::Event::EmptyTerm));
2296 })
2297 }
2298
2299 #[test]
2300 fn all_outgoing() {
2301 ExtBuilder::default().build_and_execute(|| {
2302 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2303 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2304
2305 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2306 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2307
2308 System::set_block_number(5);
2309 Elections::on_initialize(System::block_number());
2310
2311 System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2312 new_members: vec![(4, 35), (5, 45)],
2313 }));
2314
2315 assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2316 assert_eq!(runners_up_and_stake(), vec![]);
2317
2318 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2319 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2320
2321 System::set_block_number(10);
2322 Elections::on_initialize(System::block_number());
2323
2324 System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2325 new_members: vec![],
2326 }));
2327
2328 assert_eq!(balances(&4), (37, 0));
2330 assert_eq!(balances(&5), (47, 0));
2331 });
2332 }
2333
2334 #[test]
2335 fn defunct_voter_will_be_counted() {
2336 ExtBuilder::default().build_and_execute(|| {
2337 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2338
2339 assert_ok!(vote(RuntimeOrigin::signed(3), vec![4], 30));
2341 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2342
2343 System::set_block_number(5);
2344 Elections::on_initialize(System::block_number());
2345
2346 assert_eq!(members_and_stake(), vec![(5, 45)]);
2347 assert_eq!(ElectionRounds::<Test>::get(), 1);
2348
2349 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2351
2352 System::set_block_number(10);
2353 Elections::on_initialize(System::block_number());
2354
2355 assert_eq!(members_and_stake(), vec![(4, 28), (5, 45)]);
2357 assert_eq!(ElectionRounds::<Test>::get(), 2);
2358 assert_eq_uvec!(all_voters(), vec![3, 5]);
2359 });
2360 }
2361
2362 #[test]
2363 fn only_desired_seats_are_chosen() {
2364 ExtBuilder::default().build_and_execute(|| {
2365 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2366 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2367 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2368 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2369
2370 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2371 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2372 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2373 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2374
2375 System::set_block_number(5);
2376 Elections::on_initialize(System::block_number());
2377
2378 assert_eq!(ElectionRounds::<Test>::get(), 1);
2379 assert_eq!(members_ids(), vec![4, 5]);
2380 });
2381 }
2382
2383 #[test]
2384 fn phragmen_should_not_self_vote() {
2385 ExtBuilder::default().build_and_execute(|| {
2386 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2387 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2388
2389 System::set_block_number(5);
2390 Elections::on_initialize(System::block_number());
2391
2392 assert!(candidate_ids().is_empty());
2393 assert_eq!(ElectionRounds::<Test>::get(), 1);
2394 assert!(members_ids().is_empty());
2395
2396 System::assert_last_event(RuntimeEvent::Elections(super::Event::NewTerm {
2397 new_members: vec![],
2398 }));
2399 });
2400 }
2401
2402 #[test]
2403 fn runners_up_should_be_kept() {
2404 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2405 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2406 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2407 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2408 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2409
2410 assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2411 assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2412 assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2413 assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2414
2415 System::set_block_number(5);
2416 Elections::on_initialize(System::block_number());
2417 assert_eq!(members_ids(), vec![4, 5]);
2419 assert_eq!(runners_up_ids(), vec![3, 2]);
2421
2422 assert_eq!(balances(&4), (35, 5));
2424 assert_eq!(balances(&5), (45, 5));
2425 assert_eq!(balances(&3), (25, 5));
2426 });
2427 }
2428
2429 #[test]
2430 fn runners_up_should_be_next_candidates() {
2431 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2432 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2433 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2434 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2435 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2436
2437 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2438 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2439 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2440 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2441
2442 System::set_block_number(5);
2443 Elections::on_initialize(System::block_number());
2444 assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2445 assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2446
2447 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 10));
2448
2449 System::set_block_number(10);
2450 Elections::on_initialize(System::block_number());
2451
2452 assert_eq!(members_and_stake(), vec![(3, 25), (4, 35)]);
2453 assert_eq!(runners_up_and_stake(), vec![(5, 10), (2, 15)]);
2454 });
2455 }
2456
2457 #[test]
2458 fn runners_up_lose_bond_once_outgoing() {
2459 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2460 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2461 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2462 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2463
2464 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2465 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2466 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2467
2468 System::set_block_number(5);
2469 Elections::on_initialize(System::block_number());
2470 assert_eq!(members_ids(), vec![4, 5]);
2471 assert_eq!(runners_up_ids(), vec![2]);
2472 assert_eq!(balances(&2), (15, 5));
2473
2474 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2475 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2476
2477 System::set_block_number(10);
2478 Elections::on_initialize(System::block_number());
2479
2480 assert_eq!(runners_up_ids(), vec![3]);
2481 assert_eq!(balances(&2), (15, 2));
2482 });
2483 }
2484
2485 #[test]
2486 fn members_lose_bond_once_outgoing() {
2487 ExtBuilder::default().build_and_execute(|| {
2488 assert_eq!(balances(&5), (50, 0));
2489
2490 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2491 assert_eq!(balances(&5), (47, 3));
2492
2493 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2494 assert_eq!(balances(&5), (45, 5));
2495
2496 System::set_block_number(5);
2497 Elections::on_initialize(System::block_number());
2498 assert_eq!(members_ids(), vec![5]);
2499
2500 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2501 assert_eq!(balances(&5), (47, 3));
2502
2503 System::set_block_number(10);
2504 Elections::on_initialize(System::block_number());
2505 assert!(members_ids().is_empty());
2506
2507 assert_eq!(balances(&5), (47, 0));
2508 });
2509 }
2510
2511 #[test]
2512 fn candidates_lose_the_bond_when_outgoing() {
2513 ExtBuilder::default().build_and_execute(|| {
2514 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2515 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2516
2517 assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2518
2519 assert_eq!(balances(&5), (47, 3));
2520 assert_eq!(balances(&3), (27, 3));
2521
2522 System::set_block_number(5);
2523 Elections::on_initialize(System::block_number());
2524
2525 assert_eq!(members_ids(), vec![5]);
2526
2527 assert_eq!(balances(&5), (47, 3));
2529 assert_eq!(balances(&3), (27, 0));
2531 });
2532 }
2533
2534 #[test]
2535 fn current_members_are_always_next_candidate() {
2536 ExtBuilder::default().build_and_execute(|| {
2537 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2538 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2539
2540 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2541 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2542
2543 System::set_block_number(5);
2544 Elections::on_initialize(System::block_number());
2545
2546 assert_eq!(members_ids(), vec![4, 5]);
2547 assert_eq!(ElectionRounds::<Test>::get(), 1);
2548
2549 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2550 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2551
2552 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2553 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2554
2555 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2556
2557 assert_eq!(candidate_ids(), vec![2, 3]);
2559
2560 System::set_block_number(10);
2561 Elections::on_initialize(System::block_number());
2562
2563 assert_eq!(members_ids(), vec![3, 5]);
2565 });
2566 }
2567
2568 #[test]
2569 fn election_state_is_uninterrupted() {
2570 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2573 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2574 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2575 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2576 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2577
2578 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2579 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2580 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2581 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2582
2583 let check_at_block = |b: u32| {
2584 System::set_block_number(b.into());
2585 Elections::on_initialize(System::block_number());
2586 assert_eq!(members_and_stake(), vec![(4, 35), (5, 45)]);
2588 assert_eq!(runners_up_and_stake(), vec![(2, 15), (3, 25)]);
2589 assert!(candidate_ids().is_empty());
2591 assert_eq!(ElectionRounds::<Test>::get(), b / 5);
2592 assert_eq_uvec!(all_voters(), vec![2, 3, 4, 5]);
2593 };
2594
2595 check_at_block(5);
2597 check_at_block(10);
2598 check_at_block(15);
2599 check_at_block(20);
2600 });
2601 }
2602
2603 #[test]
2604 fn remove_members_triggers_election() {
2605 ExtBuilder::default().build_and_execute(|| {
2606 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2607 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2608
2609 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2610 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2611
2612 System::set_block_number(5);
2613 Elections::on_initialize(System::block_number());
2614 assert_eq!(members_ids(), vec![4, 5]);
2615 assert_eq!(ElectionRounds::<Test>::get(), 1);
2616
2617 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2619 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2620
2621 assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 4, true, true));
2622
2623 assert_eq!(balances(&4), (35, 2)); assert_eq!(ElectionRounds::<Test>::get(), 2); assert_eq!(members_ids(), vec![3, 5]); });
2627 }
2628
2629 #[test]
2630 fn seats_should_be_released_when_no_vote() {
2631 ExtBuilder::default().build_and_execute(|| {
2632 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2633 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2634 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2635
2636 assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2637 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2638 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2639 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2640
2641 assert_eq!(Candidates::<Test>::decode_len().unwrap(), 3);
2642
2643 assert_eq!(ElectionRounds::<Test>::get(), 0);
2644
2645 System::set_block_number(5);
2646 Elections::on_initialize(System::block_number());
2647 assert_eq!(members_ids(), vec![3, 5]);
2648 assert_eq!(ElectionRounds::<Test>::get(), 1);
2649
2650 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(2)));
2651 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(3)));
2652 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(4)));
2653 assert_ok!(Elections::remove_voter(RuntimeOrigin::signed(5)));
2654
2655 System::set_block_number(10);
2657 Elections::on_initialize(System::block_number());
2658 assert!(members_ids().is_empty());
2659 assert_eq!(ElectionRounds::<Test>::get(), 2);
2660 });
2661 }
2662
2663 #[test]
2664 fn incoming_outgoing_are_reported() {
2665 ExtBuilder::default().build_and_execute(|| {
2666 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2667 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2668
2669 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2670 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2671
2672 System::set_block_number(5);
2673 Elections::on_initialize(System::block_number());
2674 assert_eq!(members_ids(), vec![4, 5]);
2675
2676 assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
2677 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2678 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2679
2680 assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 8));
2682 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2684 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2686 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2688 assert_ok!(vote(RuntimeOrigin::signed(1), vec![1], 10));
2689
2690 System::set_block_number(10);
2691 Elections::on_initialize(System::block_number());
2692
2693 assert_eq!(members_and_stake(), vec![(3, 25), (4, 43)]);
2695 assert_eq!(balances(&3), (25, 5));
2696 assert_eq!(balances(&4), (35, 5));
2697
2698 assert_eq!(balances(&1), (5, 2));
2700
2701 assert_eq!(balances(&5), (45, 2));
2703
2704 System::assert_has_event(RuntimeEvent::Elections(super::Event::NewTerm {
2705 new_members: vec![(4, 35), (5, 45)],
2706 }));
2707 })
2708 }
2709
2710 #[test]
2711 fn invalid_votes_are_moot() {
2712 ExtBuilder::default().build_and_execute(|| {
2713 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2714 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2715
2716 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2717 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2718 assert_ok!(vote(RuntimeOrigin::signed(5), vec![10], 50));
2719
2720 System::set_block_number(5);
2721 Elections::on_initialize(System::block_number());
2722
2723 assert_eq_uvec!(members_ids(), vec![3, 4]);
2724 assert_eq!(ElectionRounds::<Test>::get(), 1);
2725 });
2726 }
2727
2728 #[test]
2729 fn members_are_sorted_based_on_id_runners_on_merit() {
2730 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2731 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2732 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2733 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2734 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2735
2736 assert_ok!(vote(RuntimeOrigin::signed(2), vec![3], 20));
2737 assert_ok!(vote(RuntimeOrigin::signed(3), vec![2], 30));
2738 assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2739 assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2740
2741 System::set_block_number(5);
2742 Elections::on_initialize(System::block_number());
2743 assert_eq!(members_and_stake(), vec![(4, 45), (5, 35)]);
2745 assert_eq!(runners_up_and_stake(), vec![(3, 15), (2, 25)]);
2747 });
2748 }
2749
2750 #[test]
2751 fn runner_up_replacement_maintains_members_order() {
2752 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2753 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2754 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2755 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2756
2757 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2758 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2759 assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2760
2761 System::set_block_number(5);
2762 Elections::on_initialize(System::block_number());
2763
2764 assert_eq!(members_ids(), vec![2, 4]);
2765 assert_ok!(Elections::remove_member(RuntimeOrigin::root(), 2, true, false));
2766 assert_eq!(members_ids(), vec![4, 5]);
2767 });
2768 }
2769
2770 #[test]
2771 fn can_renounce_candidacy_member_with_runners_bond_is_refunded() {
2772 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2773 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2774 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2775 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2776 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2777
2778 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2779 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2780 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2781 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2782
2783 System::set_block_number(5);
2784 Elections::on_initialize(System::block_number());
2785
2786 assert_eq!(members_ids(), vec![4, 5]);
2787 assert_eq!(runners_up_ids(), vec![2, 3]);
2788
2789 assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2790 assert_eq!(balances(&4), (38, 2)); assert_eq!(members_ids(), vec![3, 5]);
2793 assert_eq!(runners_up_ids(), vec![2]);
2794 })
2795 }
2796
2797 #[test]
2798 fn can_renounce_candidacy_member_without_runners_bond_is_refunded() {
2799 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2800 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2801 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2802
2803 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2804 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2805
2806 System::set_block_number(5);
2807 Elections::on_initialize(System::block_number());
2808
2809 assert_eq!(members_ids(), vec![4, 5]);
2810 assert!(runners_up_ids().is_empty());
2811
2812 assert_ok!(Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Member));
2813 assert_eq!(balances(&4), (38, 2)); assert_eq!(members_ids(), vec![5]);
2817 assert!(runners_up_ids().is_empty());
2818 })
2819 }
2820
2821 #[test]
2822 fn can_renounce_candidacy_runner_up() {
2823 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2824 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2825 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2826 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2827 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2828
2829 assert_ok!(vote(RuntimeOrigin::signed(5), vec![4], 50));
2830 assert_ok!(vote(RuntimeOrigin::signed(4), vec![5], 40));
2831 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2832 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
2833
2834 System::set_block_number(5);
2835 Elections::on_initialize(System::block_number());
2836
2837 assert_eq!(members_ids(), vec![4, 5]);
2838 assert_eq!(runners_up_ids(), vec![2, 3]);
2839
2840 assert_ok!(Elections::renounce_candidacy(
2841 RuntimeOrigin::signed(3),
2842 Renouncing::RunnerUp
2843 ));
2844 assert_eq!(balances(&3), (28, 2)); assert_eq!(members_ids(), vec![4, 5]);
2847 assert_eq!(runners_up_ids(), vec![2]);
2848 })
2849 }
2850
2851 #[test]
2852 fn runner_up_replacement_works_when_out_of_order() {
2853 ExtBuilder::default().desired_runners_up(2).build_and_execute(|| {
2854 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2855 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2856 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2857 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
2858
2859 assert_ok!(vote(RuntimeOrigin::signed(2), vec![5], 20));
2860 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2861 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2862 assert_ok!(vote(RuntimeOrigin::signed(5), vec![2], 50));
2863
2864 System::set_block_number(5);
2865 Elections::on_initialize(System::block_number());
2866
2867 assert_eq!(members_ids(), vec![2, 4]);
2868 assert_eq!(runners_up_ids(), vec![5, 3]);
2869 assert_ok!(Elections::renounce_candidacy(
2870 RuntimeOrigin::signed(3),
2871 Renouncing::RunnerUp
2872 ));
2873 assert_eq!(members_ids(), vec![2, 4]);
2874 assert_eq!(runners_up_ids(), vec![5]);
2875 });
2876 }
2877
2878 #[test]
2879 fn can_renounce_candidacy_candidate() {
2880 ExtBuilder::default().build_and_execute(|| {
2881 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2882 assert_eq!(balances(&5), (47, 3));
2883 assert_eq!(candidate_ids(), vec![5]);
2884
2885 assert_ok!(Elections::renounce_candidacy(
2886 RuntimeOrigin::signed(5),
2887 Renouncing::Candidate(1)
2888 ));
2889 assert_eq!(balances(&5), (50, 0));
2890 assert!(candidate_ids().is_empty());
2891 })
2892 }
2893
2894 #[test]
2895 fn wrong_renounce_candidacy_should_fail() {
2896 ExtBuilder::default().build_and_execute(|| {
2897 assert_noop!(
2898 Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Candidate(0)),
2899 Error::<Test>::InvalidRenouncing,
2900 );
2901 assert_noop!(
2902 Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::Member),
2903 Error::<Test>::InvalidRenouncing,
2904 );
2905 assert_noop!(
2906 Elections::renounce_candidacy(RuntimeOrigin::signed(5), Renouncing::RunnerUp),
2907 Error::<Test>::InvalidRenouncing,
2908 );
2909 })
2910 }
2911
2912 #[test]
2913 fn non_member_renounce_member_should_fail() {
2914 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2915 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2916 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2917 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2918
2919 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2920 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2921 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2922
2923 System::set_block_number(5);
2924 Elections::on_initialize(System::block_number());
2925
2926 assert_eq!(members_ids(), vec![4, 5]);
2927 assert_eq!(runners_up_ids(), vec![3]);
2928
2929 assert_noop!(
2930 Elections::renounce_candidacy(RuntimeOrigin::signed(3), Renouncing::Member),
2931 Error::<Test>::InvalidRenouncing,
2932 );
2933 })
2934 }
2935
2936 #[test]
2937 fn non_runner_up_renounce_runner_up_should_fail() {
2938 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
2939 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2940 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2941 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2942
2943 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
2944 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
2945 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
2946
2947 System::set_block_number(5);
2948 Elections::on_initialize(System::block_number());
2949
2950 assert_eq!(members_ids(), vec![4, 5]);
2951 assert_eq!(runners_up_ids(), vec![3]);
2952
2953 assert_noop!(
2954 Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::RunnerUp),
2955 Error::<Test>::InvalidRenouncing,
2956 );
2957 })
2958 }
2959
2960 #[test]
2961 fn wrong_candidate_count_renounce_should_fail() {
2962 ExtBuilder::default().build_and_execute(|| {
2963 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2964 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2965 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2966
2967 assert_noop!(
2968 Elections::renounce_candidacy(RuntimeOrigin::signed(4), Renouncing::Candidate(2)),
2969 Error::<Test>::InvalidWitnessData,
2970 );
2971
2972 assert_ok!(Elections::renounce_candidacy(
2973 RuntimeOrigin::signed(4),
2974 Renouncing::Candidate(3)
2975 ));
2976 })
2977 }
2978
2979 #[test]
2980 fn renounce_candidacy_count_can_overestimate() {
2981 ExtBuilder::default().build_and_execute(|| {
2982 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
2983 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
2984 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
2985 assert_ok!(Elections::renounce_candidacy(
2987 RuntimeOrigin::signed(4),
2988 Renouncing::Candidate(4)
2989 ));
2990 })
2991 }
2992
2993 #[test]
2994 fn unsorted_runners_up_are_detected() {
2995 ExtBuilder::default()
2996 .desired_runners_up(2)
2997 .desired_members(1)
2998 .build_and_execute(|| {
2999 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3000 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3001 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3002
3003 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3004 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3005 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 15));
3006
3007 System::set_block_number(5);
3008 Elections::on_initialize(System::block_number());
3009
3010 assert_eq!(members_ids(), vec![5]);
3011 assert_eq!(runners_up_ids(), vec![4, 3]);
3012
3013 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3014 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 10));
3015
3016 System::set_block_number(10);
3017 Elections::on_initialize(System::block_number());
3018
3019 assert_eq!(members_ids(), vec![5]);
3020 assert_eq!(runners_up_ids(), vec![2, 3]);
3021
3022 assert_eq!(balances(&4), (35, 2));
3024 assert_eq!(balances(&3), (25, 5));
3026 })
3027 }
3028
3029 #[test]
3030 fn member_to_runner_up_wont_slash() {
3031 ExtBuilder::default()
3032 .desired_runners_up(2)
3033 .desired_members(1)
3034 .build_and_execute(|| {
3035 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3036 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3037 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3038
3039 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3040 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3041 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3042
3043 System::set_block_number(5);
3044 Elections::on_initialize(System::block_number());
3045
3046 assert_eq!(members_ids(), vec![4]);
3047 assert_eq!(runners_up_ids(), vec![2, 3]);
3048
3049 assert_eq!(balances(&4), (35, 5));
3050 assert_eq!(balances(&3), (25, 5));
3051 assert_eq!(balances(&2), (15, 5));
3052
3053 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3055 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3056
3057 System::set_block_number(10);
3058 Elections::on_initialize(System::block_number());
3059
3060 assert_eq!(members_ids(), vec![5]);
3061 assert_eq!(runners_up_ids(), vec![3, 4]);
3062
3063 assert_eq!(balances(&4), (35, 5));
3065 assert_eq!(balances(&3), (25, 5));
3067 assert_eq!(balances(&2), (15, 2));
3069 });
3070 }
3071
3072 #[test]
3073 fn runner_up_to_member_wont_slash() {
3074 ExtBuilder::default()
3075 .desired_runners_up(2)
3076 .desired_members(1)
3077 .build_and_execute(|| {
3078 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3079 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3080 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3081
3082 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3083 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3084 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3085
3086 System::set_block_number(5);
3087 Elections::on_initialize(System::block_number());
3088
3089 assert_eq!(members_ids(), vec![4]);
3090 assert_eq!(runners_up_ids(), vec![2, 3]);
3091
3092 assert_eq!(balances(&4), (35, 5));
3093 assert_eq!(balances(&3), (25, 5));
3094 assert_eq!(balances(&2), (15, 5));
3095
3096 assert_ok!(vote(RuntimeOrigin::signed(4), vec![2], 40));
3098 assert_ok!(vote(RuntimeOrigin::signed(2), vec![4], 20));
3099
3100 System::set_block_number(10);
3101 Elections::on_initialize(System::block_number());
3102
3103 assert_eq!(members_ids(), vec![2]);
3104 assert_eq!(runners_up_ids(), vec![4, 3]);
3105
3106 assert_eq!(balances(&2), (15, 5));
3108 assert_eq!(balances(&4), (35, 5));
3110 assert_eq!(balances(&3), (25, 5));
3112 });
3113 }
3114
3115 #[test]
3116 fn remove_and_replace_member_works() {
3117 let setup = || {
3118 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3119 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3120 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3121
3122 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5], 50));
3123 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3124 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3125
3126 System::set_block_number(5);
3127 Elections::on_initialize(System::block_number());
3128
3129 assert_eq!(members_ids(), vec![4, 5]);
3130 assert_eq!(runners_up_ids(), vec![3]);
3131 };
3132
3133 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3135 setup();
3136 assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(true));
3137
3138 assert_eq!(members_ids(), vec![3, 5]);
3139 assert_eq!(runners_up_ids().len(), 0);
3140 });
3141
3142 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3144 setup();
3145 assert_ok!(Elections::renounce_candidacy(
3146 RuntimeOrigin::signed(3),
3147 Renouncing::RunnerUp
3148 ));
3149 assert_eq!(Elections::remove_and_replace_member(&4, false), Ok(false));
3150
3151 assert_eq!(members_ids(), vec![5]);
3152 assert_eq!(runners_up_ids().len(), 0);
3153 });
3154
3155 ExtBuilder::default().desired_runners_up(1).build_and_execute(|| {
3157 setup();
3158 assert!(matches!(Elections::remove_and_replace_member(&2, false), Err(_)));
3159 });
3160 }
3161
3162 #[test]
3163 fn no_desired_members() {
3164 ExtBuilder::default()
3166 .desired_members(0)
3167 .desired_runners_up(0)
3168 .build_and_execute(|| {
3169 assert_eq!(Candidates::<Test>::get().len(), 0);
3170
3171 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3172 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3173 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3174
3175 assert_eq!(Candidates::<Test>::get().len(), 3);
3176
3177 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3178 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3179 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3180
3181 System::set_block_number(5);
3182 Elections::on_initialize(System::block_number());
3183
3184 assert_eq!(members_ids().len(), 0);
3185 assert_eq!(runners_up_ids().len(), 0);
3186 assert_eq!(all_voters().len(), 3);
3187 assert_eq!(Candidates::<Test>::get().len(), 0);
3188 });
3189
3190 ExtBuilder::default()
3192 .desired_members(0)
3193 .desired_runners_up(2)
3194 .build_and_execute(|| {
3195 assert_eq!(Candidates::<Test>::get().len(), 0);
3196
3197 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3198 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3199 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3200
3201 assert_eq!(Candidates::<Test>::get().len(), 3);
3202
3203 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3204 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3205 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3206
3207 System::set_block_number(5);
3208 Elections::on_initialize(System::block_number());
3209
3210 assert_eq!(members_ids().len(), 0);
3211 assert_eq!(runners_up_ids(), vec![3, 4]);
3212 assert_eq!(all_voters().len(), 3);
3213 assert_eq!(Candidates::<Test>::get().len(), 0);
3214 });
3215
3216 ExtBuilder::default()
3218 .desired_members(2)
3219 .desired_runners_up(0)
3220 .build_and_execute(|| {
3221 assert_eq!(Candidates::<Test>::get().len(), 0);
3222
3223 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3224 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3225 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3226
3227 assert_eq!(Candidates::<Test>::get().len(), 3);
3228
3229 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 40));
3230 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3231 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2], 20));
3232
3233 System::set_block_number(5);
3234 Elections::on_initialize(System::block_number());
3235
3236 assert_eq!(members_ids(), vec![3, 4]);
3237 assert_eq!(runners_up_ids().len(), 0);
3238 assert_eq!(all_voters().len(), 3);
3239 assert_eq!(Candidates::<Test>::get().len(), 0);
3240 });
3241 }
3242
3243 #[test]
3244 fn dupe_vote_is_moot() {
3245 ExtBuilder::default().desired_members(1).build_and_execute(|| {
3246 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3247 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3248 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3249 assert_ok!(submit_candidacy(RuntimeOrigin::signed(2)));
3250 assert_ok!(submit_candidacy(RuntimeOrigin::signed(1)));
3251
3252 assert_ok!(vote(RuntimeOrigin::signed(1), vec![2, 2, 2, 2], 5));
3254 assert_ok!(vote(RuntimeOrigin::signed(2), vec![2, 2, 2, 2], 20));
3255
3256 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 30));
3257
3258 System::set_block_number(5);
3259 Elections::on_initialize(System::block_number());
3260
3261 assert_eq!(members_ids(), vec![3]);
3262 })
3263 }
3264
3265 #[test]
3266 fn remove_defunct_voter_works() {
3267 ExtBuilder::default().build_and_execute(|| {
3268 assert_ok!(submit_candidacy(RuntimeOrigin::signed(5)));
3269 assert_ok!(submit_candidacy(RuntimeOrigin::signed(4)));
3270 assert_ok!(submit_candidacy(RuntimeOrigin::signed(3)));
3271
3272 assert_ok!(vote(RuntimeOrigin::signed(5), vec![5, 4], 5));
3274 assert_ok!(vote(RuntimeOrigin::signed(4), vec![4], 5));
3276 assert_ok!(vote(RuntimeOrigin::signed(3), vec![3], 5));
3278 assert_ok!(vote(RuntimeOrigin::signed(2), vec![3, 4], 5));
3280
3281 assert_ok!(Elections::renounce_candidacy(
3282 RuntimeOrigin::signed(5),
3283 Renouncing::Candidate(3)
3284 ));
3285 assert_ok!(Elections::renounce_candidacy(
3286 RuntimeOrigin::signed(4),
3287 Renouncing::Candidate(2)
3288 ));
3289 assert_ok!(Elections::renounce_candidacy(
3290 RuntimeOrigin::signed(3),
3291 Renouncing::Candidate(1)
3292 ));
3293
3294 assert_ok!(Elections::clean_defunct_voters(RuntimeOrigin::root(), 4, 2));
3295 })
3296 }
3297}