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