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