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