12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129 |
- //! Council Elections Manager
- //!
- //! # Election Parameters:
- //! We don't currently handle zero periods, zero council term, zero council size and candidacy
- //! limit in any special way. The behaviour in such cases:
- //!
- //! - Setting any period to 0 will mean the election getting stuck in that stage, until force changing
- //! the state.
- //!
- //! - Council Size of 0 - no limit to size of council, all applicants that move beyond
- //! announcing stage would become council members, so effectively the candidacy limit will
- //! be the size of the council, voting and revealing have no impact on final results.
- //!
- //! - If candidacy limit is zero and council size > 0, council_size number of applicants will reach the voting stage.
- //! and become council members, voting will have no impact on final results.
- //!
- //! - If both candidacy limit and council size are zero then all applicant become council members
- //! since no filtering occurs at end of announcing stage.
- //!
- //! We only guard against these edge cases in the [`set_election_parameters`] call.
- //!
- //! [`set_election_parameters`]: struct.Module.html#method.set_election_parameters
- // Clippy linter warning
- #![allow(clippy::type_complexity)]
- // disable it because of possible frontend API break
- // TODO: remove post-Constaninople
- // Clippy linter warning
- #![allow(clippy::redundant_closure_call)] // disable it because of the substrate lib design
- use rstd::prelude::*;
- use srml_support::traits::{Currency, ReservableCurrency};
- use srml_support::{decl_event, decl_module, decl_storage, dispatch::Result, ensure};
- use system::{self, ensure_root, ensure_signed};
- use codec::{Decode, Encode};
- use rstd::collections::btree_map::BTreeMap;
- use rstd::ops::Add;
- #[cfg(feature = "std")]
- use serde::{Deserialize, Serialize};
- use sr_primitives::traits::{Hash, Zero};
- use super::sealed_vote::SealedVote;
- use super::stake::Stake;
- use super::council;
- use crate::election_params::ElectionParameters;
- pub use common::currency::{BalanceOf, GovernanceCurrency};
- pub trait Trait:
- system::Trait + council::Trait + GovernanceCurrency + membership::members::Trait
- {
- type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
- type CouncilElected: CouncilElected<Seats<Self::AccountId, BalanceOf<Self>>, Self::BlockNumber>;
- }
- pub static MSG_CANNOT_CHANGE_PARAMS_DURING_ELECTION: &str = "CannotChangeParamsDuringElection";
- #[derive(Clone, Copy, Encode, Decode)]
- pub enum ElectionStage<BlockNumber> {
- Announcing(BlockNumber),
- Voting(BlockNumber),
- Revealing(BlockNumber),
- }
- #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
- #[derive(Encode, Decode, Default, Clone, PartialEq, Eq)]
- pub struct Seat<AccountId, Balance> {
- pub member: AccountId,
- pub stake: Balance,
- pub backers: Vec<Backer<AccountId, Balance>>,
- }
- impl<AccountId, Balance> Seat<AccountId, Balance>
- where
- Balance: Add<Output = Balance> + Copy,
- {
- pub fn calc_total_stake(&self) -> Balance {
- self.backers
- .iter()
- .fold(self.stake, |acc, backer| acc + backer.stake)
- }
- }
- #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
- #[derive(Encode, Decode, Default, Clone, PartialEq, Eq)]
- pub struct Backer<AccountId, Balance> {
- pub member: AccountId,
- pub stake: Balance,
- }
- pub type Seats<AccountId, Balance> = Vec<Seat<AccountId, Balance>>;
- // Hook for setting a new council when it is elected
- pub trait CouncilElected<Elected, Term> {
- fn council_elected(new_council: Elected, term: Term);
- }
- impl<Elected, Term> CouncilElected<Elected, Term> for () {
- fn council_elected(_new_council: Elected, _term: Term) {}
- }
- impl<Elected, Term, X: CouncilElected<Elected, Term>> CouncilElected<Elected, Term> for (X,) {
- fn council_elected(new_council: Elected, term: Term) {
- X::council_elected(new_council, term);
- }
- }
- // Chain of handlers.
- impl<
- Elected: Clone,
- Term: Clone,
- X: CouncilElected<Elected, Term>,
- Y: CouncilElected<Elected, Term>,
- > CouncilElected<Elected, Term> for (X, Y)
- {
- fn council_elected(new_council: Elected, term: Term) {
- X::council_elected(new_council.clone(), term.clone());
- Y::council_elected(new_council, term);
- }
- }
- #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
- #[derive(Clone, Copy, Encode, Decode, Default)]
- pub struct TransferableStake<Balance> {
- seat: Balance,
- backing: Balance,
- }
- // can we use a type alias to overcome name clashes of public types with other modules?
- pub type ElectionStake<T> = Stake<BalanceOf<T>>;
- decl_storage! {
- trait Store for Module<T: Trait> as CouncilElection {
- // Flag for wether to automatically start an election after a council term ends
- AutoStart get(auto_start) config() : bool = true;
- // Current stage if there is an election running
- Stage get(stage): Option<ElectionStage<T::BlockNumber>>;
- // The election round
- Round get(round): u32;
- ExistingStakeHolders get(existing_stake_holders): Vec<T::AccountId>;
- TransferableStakes get(transferable_stakes): map T::AccountId => TransferableStake<BalanceOf<T>>;
- Applicants get(applicants): Vec<T::AccountId>;
- ApplicantStakes get(applicant_stakes): map T::AccountId => ElectionStake<T>;
- Commitments get(commitments): Vec<T::Hash>;
- // TODO value type of this map looks scary, is there any way to simplify the notation?
- Votes get(votes): map T::Hash => SealedVote<T::AccountId, ElectionStake<T>, T::Hash, T::AccountId>;
- // Current Election Parameters.
- // Should we replace all the individual values with a single ElectionParameters type?
- // Having them individually makes it more flexible to add and remove new parameters in future
- // without dealing with migration issues.
- AnnouncingPeriod get(announcing_period): T::BlockNumber;
- VotingPeriod get(voting_period): T::BlockNumber;
- RevealingPeriod get(revealing_period): T::BlockNumber;
- CouncilSize get(council_size): u32;
- CandidacyLimit get (candidacy_limit): u32;
- MinCouncilStake get(min_council_stake): BalanceOf<T>;
- NewTermDuration get(new_term_duration): T::BlockNumber;
- MinVotingStake get(min_voting_stake): BalanceOf<T>;
- }
- add_extra_genesis {
- config(election_parameters): ElectionParameters<BalanceOf<T>, T::BlockNumber>;
- build(|config: &GenesisConfig<T>| {
- config.election_parameters.ensure_valid().expect("Invalid Election Parameters");
- Module::<T>::set_verified_election_parameters(config.election_parameters);
- });
- }
- }
- // Event for this module.
- decl_event!(
- pub enum Event<T> where
- <T as system::Trait>::BlockNumber,
- <T as system::Trait>::AccountId,
- <T as system::Trait>::Hash {
- /// A new election started
- ElectionStarted(),
- AnnouncingStarted(u32),
- AnnouncingEnded(),
- VotingStarted(),
- VotingEnded(),
- RevealingStarted(),
- RevealingEnded(),
- CouncilElected(BlockNumber),
- Applied(AccountId),
- Voted(AccountId, Hash),
- Revealed(AccountId, Hash, AccountId),
- }
- );
- impl<T: Trait> Module<T> {
- // HELPERS - IMMUTABLES
- fn council_size_usize() -> usize {
- Self::council_size() as usize
- }
- fn candidacy_limit_usize() -> usize {
- Self::candidacy_limit() as usize
- }
- fn current_block_number_plus(length: T::BlockNumber) -> T::BlockNumber {
- <system::Module<T>>::block_number() + length
- }
- fn can_participate(sender: &T::AccountId) -> bool {
- !<T as GovernanceCurrency>::Currency::free_balance(sender).is_zero()
- && <membership::members::Module<T>>::is_member_account(sender)
- }
- // PUBLIC IMMUTABLES
- /// Returns true if an election is running
- pub fn is_election_running() -> bool {
- Self::stage().is_some()
- }
- /// Returns block number at which current stage will end if an election is running.
- pub fn stage_ends_at() -> Option<T::BlockNumber> {
- if let Some(stage) = Self::stage() {
- match stage {
- ElectionStage::Announcing(ends) => Some(ends),
- ElectionStage::Voting(ends) => Some(ends),
- ElectionStage::Revealing(ends) => Some(ends),
- }
- } else {
- None
- }
- }
- // PRIVATE MUTABLES
- /// Starts an election. Will fail if an election is already running
- /// Initializes transferable stakes. Assumes election parameters have already been set.
- fn start_election(current_council: Seats<T::AccountId, BalanceOf<T>>) -> Result {
- ensure!(!Self::is_election_running(), "election already in progress");
- ensure!(
- Self::existing_stake_holders().is_empty(),
- "stake holders must be empty"
- );
- ensure!(Self::applicants().is_empty(), "applicants must be empty");
- ensure!(Self::commitments().is_empty(), "commitments must be empty");
- // Take snapshot of seat and backing stakes of an existing council
- // Its important to note that the election system takes ownership of these stakes, and is responsible
- // to return any unused stake to original owners at the end of the election.
- Self::initialize_transferable_stakes(current_council);
- Self::deposit_event(RawEvent::ElectionStarted());
- Self::move_to_announcing_stage();
- Ok(())
- }
- /// Sets announcing stage. Can be called from any stage and assumes all preparatory work
- /// for entering the stage has been performed.
- /// Bumps the election round.
- fn move_to_announcing_stage() {
- let next_round = Round::mutate(|n| {
- *n += 1;
- *n
- });
- let new_stage_ends_at = Self::current_block_number_plus(Self::announcing_period());
- <Stage<T>>::put(ElectionStage::Announcing(new_stage_ends_at));
- Self::deposit_event(RawEvent::AnnouncingStarted(next_round));
- }
- /// Sets announcing stage. Can be called from any stage and assumes all preparatory work
- /// for entering the stage has been performed.
- fn move_to_voting_stage() {
- let new_stage_ends_at = Self::current_block_number_plus(Self::voting_period());
- <Stage<T>>::put(ElectionStage::Voting(new_stage_ends_at));
- Self::deposit_event(RawEvent::VotingStarted());
- }
- /// Sets announcing stage. Can be called from any stage and assumes all preparatory work
- /// for entering the stage has been performed.
- fn move_to_revealing_stage() {
- let new_stage_ends_at = Self::current_block_number_plus(Self::revealing_period());
- <Stage<T>>::put(ElectionStage::Revealing(new_stage_ends_at));
- Self::deposit_event(RawEvent::RevealingStarted());
- }
- /// Sorts applicants by stake, and returns slice of applicants with least stake. Applicants not
- /// returned in the slice are the top `len` highest staked.
- fn find_least_staked_applicants(
- applicants: &mut Vec<T::AccountId>,
- len: usize,
- ) -> &[T::AccountId] {
- if len >= applicants.len() {
- &[]
- } else {
- #[allow(clippy::redundant_closure)] // disable incorrect Clippy linter warning
- applicants.sort_by_key(|applicant| Self::applicant_stakes(applicant));
- &applicants[0..applicants.len() - len]
- }
- }
- fn on_announcing_ended() {
- let mut applicants = Self::applicants();
- if applicants.len() < Self::council_size_usize() {
- // Not enough applicants announced candidacy
- Self::move_to_announcing_stage();
- } else {
- // upper limit on applicants that will move to voting stage
- let limit = rstd::cmp::max(Self::council_size_usize(), Self::candidacy_limit_usize());
- let applicants_to_drop = Self::find_least_staked_applicants(&mut applicants, limit);
- Self::drop_applicants(applicants_to_drop);
- Self::move_to_voting_stage();
- }
- }
- fn on_voting_ended() {
- Self::move_to_revealing_stage();
- }
- fn on_revealing_ended() {
- // tally the revealed votes
- let mut votes = Vec::new();
- for commitment in Self::commitments().iter() {
- votes.push(Self::votes(commitment));
- }
- let mut new_council = Self::tally_votes(&votes);
- // Note here that applicants with zero votes dont appear in the tally.
- // Is an applicant with some votes but less total stake than another applicant with zero votes
- // more qualified to be on the council?
- // Consider implications - if a council can be formed purely by staking are we fine with that?
- for applicant in Self::applicants().iter() {
- if !new_council.contains_key(applicant) {
- new_council.insert(
- applicant.clone(),
- Seat {
- member: applicant.clone(),
- stake: Self::applicant_stakes(applicant).total(),
- backers: Vec::new(),
- },
- );
- }
- }
- match new_council.len() {
- ncl if ncl == Self::council_size_usize() => {
- // all applicants in the tally will form the new council
- }
- ncl if ncl > Self::council_size_usize() => {
- // we have more than enough applicants to form the new council.
- // select top staked
- Self::filter_top_staked(&mut new_council, Self::council_size_usize());
- }
- _ => {
- // Not enough applicants with votes to form a council.
- // This may happen if we didn't add applicants with zero votes to the tally,
- // or in future if we allow applicants to withdraw candidacy during voting or revealing stages.
- // or council size was increased during voting, revealing stages.
- }
- }
- // unless we want to add more filtering criteria to what is considered a successful election
- // other than just the minimum stake for candidacy, we have a new council!
- Self::teardown_election(
- &votes,
- &new_council,
- true, /* unlock transferable stakes */
- );
- let new_council = new_council.into_iter().map(|(_, seat)| seat).collect();
- T::CouncilElected::council_elected(new_council, Self::new_term_duration());
- Self::deposit_event(RawEvent::CouncilElected(<system::Module<T>>::block_number()));
- }
- fn teardown_election(
- votes: &[SealedVote<T::AccountId, Stake<BalanceOf<T>>, T::Hash, T::AccountId>],
- new_council: &BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>>,
- unlock_ts: bool,
- ) {
- Self::refund_voting_stakes(&votes, &new_council);
- Self::clear_votes();
- Self::drop_unelected_applicants(&new_council);
- Self::clear_applicants();
- if unlock_ts {
- Self::unlock_transferable_stakes();
- }
- Self::clear_transferable_stakes();
- <Stage<T>>::kill();
- }
- fn unlock_transferable_stakes() {
- // move stakes back to account holder's free balance
- for stakeholder in Self::existing_stake_holders().iter() {
- let stake = Self::transferable_stakes(stakeholder);
- if !stake.seat.is_zero() || !stake.backing.is_zero() {
- <T as GovernanceCurrency>::Currency::unreserve(
- stakeholder,
- stake.seat + stake.backing,
- );
- }
- }
- }
- fn clear_transferable_stakes() {
- for stakeholder in Self::existing_stake_holders() {
- <TransferableStakes<T>>::remove(stakeholder);
- }
- <ExistingStakeHolders<T>>::kill();
- }
- fn clear_applicants() {
- for applicant in Self::applicants() {
- <ApplicantStakes<T>>::remove(applicant);
- }
- <Applicants<T>>::kill();
- }
- fn refund_applicant(applicant: &T::AccountId) {
- let stake = <ApplicantStakes<T>>::get(applicant);
- // return new stake to account's free balance
- if !stake.new.is_zero() {
- <T as GovernanceCurrency>::Currency::unreserve(applicant, stake.new);
- }
- // return unused transferable stake
- if !stake.transferred.is_zero() {
- <TransferableStakes<T>>::mutate(applicant, |transferable| {
- (*transferable).seat += stake.transferred
- });
- }
- }
- fn drop_applicants(drop: &[T::AccountId]) {
- let not_dropped: Vec<T::AccountId> = Self::applicants()
- .into_iter()
- .filter(|id| !drop.iter().any(|x| *x == *id))
- .collect();
- for applicant in drop {
- Self::refund_applicant(applicant);
- <ApplicantStakes<T>>::remove(applicant);
- }
- <Applicants<T>>::put(not_dropped);
- }
- fn drop_unelected_applicants(
- new_council: &BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>>,
- ) {
- let applicants_to_drop: Vec<T::AccountId> = Self::applicants()
- .into_iter()
- .filter(|applicant| !new_council.contains_key(&applicant))
- .collect();
- Self::drop_applicants(&applicants_to_drop[..]);
- }
- fn refund_voting_stakes(
- sealed_votes: &[SealedVote<T::AccountId, Stake<BalanceOf<T>>, T::Hash, T::AccountId>],
- new_council: &BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>>,
- ) {
- for sealed_vote in sealed_votes.iter() {
- // Do a refund if commitment was not revealed, or the vote was for applicant that did
- // not get elected to the council
- // TODO critical: shouldn't we slash the stake in such a case? This is the whole idea behid staking on something: people need to decide carefully and be responsible for their bahavior because they can loose their stake
- // See https://github.com/Joystream/substrate-node-joystream/issues/4
- let do_refund = match sealed_vote.get_vote() {
- Some(applicant) => !new_council.contains_key(&applicant),
- None => true,
- };
- if do_refund {
- // return new stake to account's free balance
- let SealedVote { voter, stake, .. } = sealed_vote;
- if !stake.new.is_zero() {
- <T as GovernanceCurrency>::Currency::unreserve(voter, stake.new);
- }
- // return unused transferable stake
- if !stake.transferred.is_zero() {
- <TransferableStakes<T>>::mutate(voter, |transferable| {
- (*transferable).backing += stake.transferred
- });
- }
- }
- }
- }
- fn clear_votes() {
- for commitment in Self::commitments() {
- <Votes<T>>::remove(commitment);
- }
- <Commitments<T>>::kill();
- }
- fn tally_votes(
- sealed_votes: &[SealedVote<T::AccountId, Stake<BalanceOf<T>>, T::Hash, T::AccountId>],
- ) -> BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>> {
- let mut tally: BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>> = BTreeMap::new();
- for sealed_vote in sealed_votes.iter() {
- if let Some(applicant) = sealed_vote.get_vote() {
- if !tally.contains_key(&applicant) {
- // Add new seat
- tally.insert(
- applicant.clone(),
- Seat {
- member: applicant.clone(),
- stake: Self::applicant_stakes(applicant).total(),
- backers: vec![],
- },
- );
- }
- if let Some(seat) = tally.get_mut(&applicant) {
- // Add backer to existing seat
- seat.backers.push(Backer {
- member: sealed_vote.voter.clone(),
- stake: sealed_vote.stake.total(),
- });
- }
- }
- }
- tally
- }
- fn filter_top_staked(
- tally: &mut BTreeMap<T::AccountId, Seat<T::AccountId, BalanceOf<T>>>,
- limit: usize,
- ) {
- if limit >= tally.len() {
- return;
- }
- // use ordering in the applicants vector (not ordering resulting from btreemap iteration)
- let mut seats: Vec<T::AccountId> = Self::applicants()
- .into_iter()
- .filter(|id| tally.contains_key(id))
- .collect();
- // ensure_eq!(seats.len(), tally.len());
- if limit >= seats.len() {
- // Tally is inconsistent with list of applicants!
- return;
- }
- // TODO: order by number of votes, then number of backers
- seats.sort_by_key(|applicant| {
- tally
- .get(&applicant)
- .map_or(Zero::zero(), |seat| seat.calc_total_stake())
- });
- // seats at bottom of list
- let filtered_out_seats = &seats[0..seats.len() - limit];
- for id in filtered_out_seats {
- tally.remove(id);
- }
- }
- /// Checks if the current election stage has ended and calls the stage ended handler
- fn check_if_stage_is_ending(now: T::BlockNumber) {
- if let Some(stage) = Self::stage() {
- match stage {
- ElectionStage::Announcing(ends) => {
- if ends == now {
- Self::deposit_event(RawEvent::AnnouncingEnded());
- Self::on_announcing_ended();
- }
- }
- ElectionStage::Voting(ends) => {
- if ends == now {
- Self::deposit_event(RawEvent::VotingEnded());
- Self::on_voting_ended();
- }
- }
- ElectionStage::Revealing(ends) => {
- if ends == now {
- Self::deposit_event(RawEvent::RevealingEnded());
- Self::on_revealing_ended();
- }
- }
- }
- }
- }
- /// Takes a snapshot of the stakes from the current council
- fn initialize_transferable_stakes(current_council: Seats<T::AccountId, BalanceOf<T>>) {
- let mut stakeholder_accounts: Vec<T::AccountId> = Vec::new();
- for seat in current_council.into_iter() {
- let Seat { member, stake, .. } = seat;
- if <TransferableStakes<T>>::exists(&member) {
- <TransferableStakes<T>>::mutate(&member, |transferbale_stake| {
- *transferbale_stake = TransferableStake {
- seat: transferbale_stake.seat + stake,
- backing: transferbale_stake.backing,
- }
- });
- } else {
- <TransferableStakes<T>>::insert(
- &member,
- TransferableStake {
- seat: stake,
- backing: BalanceOf::<T>::zero(),
- },
- );
- stakeholder_accounts.push(member);
- }
- for backer in seat.backers.into_iter() {
- let Backer { member, stake, .. } = backer;
- if <TransferableStakes<T>>::exists(&member) {
- <TransferableStakes<T>>::mutate(&member, |transferbale_stake| {
- *transferbale_stake = TransferableStake {
- seat: transferbale_stake.seat,
- backing: transferbale_stake.backing + stake,
- }
- });
- } else {
- <TransferableStakes<T>>::insert(
- &member,
- TransferableStake {
- seat: BalanceOf::<T>::zero(),
- backing: stake,
- },
- );
- stakeholder_accounts.push(member);
- }
- }
- }
- <ExistingStakeHolders<T>>::put(stakeholder_accounts);
- }
- fn new_stake_reusing_transferable(
- transferable: &mut BalanceOf<T>,
- new_stake: BalanceOf<T>,
- ) -> Stake<BalanceOf<T>> {
- let transferred = if *transferable >= new_stake {
- new_stake
- } else {
- *transferable
- };
- *transferable -= transferred;
- Stake {
- new: new_stake - transferred,
- transferred,
- }
- }
- fn try_add_applicant(applicant: T::AccountId, stake: BalanceOf<T>) -> Result {
- let mut transferable_stake = <TransferableStakes<T>>::get(&applicant);
- let new_stake = Self::new_stake_reusing_transferable(&mut transferable_stake.seat, stake);
- ensure!(
- <T as GovernanceCurrency>::Currency::can_reserve(&applicant, new_stake.new),
- "not enough free balance to reserve"
- );
- ensure!(
- <T as GovernanceCurrency>::Currency::reserve(&applicant, new_stake.new).is_ok(),
- "failed to reserve applicant stake!"
- );
- let applicant_stake = <ApplicantStakes<T>>::get(&applicant);
- let total_stake = applicant_stake.add(&new_stake);
- if <TransferableStakes<T>>::exists(&applicant) {
- <TransferableStakes<T>>::insert(&applicant, transferable_stake);
- }
- if !<ApplicantStakes<T>>::exists(&applicant) {
- // insert element at the begining, this gives priority to early applicants
- // when ordering applicants by stake if stakes are equal
- <Applicants<T>>::mutate(|applicants| applicants.insert(0, applicant.clone()));
- }
- <ApplicantStakes<T>>::insert(applicant, total_stake);
- Ok(())
- }
- fn try_add_vote(voter: T::AccountId, stake: BalanceOf<T>, commitment: T::Hash) -> Result {
- ensure!(!<Votes<T>>::exists(commitment), "duplicate commitment");
- let mut transferable_stake = <TransferableStakes<T>>::get(&voter);
- let vote_stake =
- Self::new_stake_reusing_transferable(&mut transferable_stake.backing, stake);
- ensure!(
- <T as GovernanceCurrency>::Currency::can_reserve(&voter, vote_stake.new),
- "not enough free balance to reserve"
- );
- ensure!(
- <T as GovernanceCurrency>::Currency::reserve(&voter, vote_stake.new).is_ok(),
- "failed to reserve voting stake!"
- );
- <Commitments<T>>::mutate(|commitments| commitments.push(commitment));
- <Votes<T>>::insert(
- commitment,
- SealedVote::new(voter.clone(), vote_stake, commitment),
- );
- if <TransferableStakes<T>>::exists(&voter) {
- <TransferableStakes<T>>::insert(&voter, transferable_stake);
- }
- Ok(())
- }
- fn try_reveal_vote(
- voter: T::AccountId,
- commitment: T::Hash,
- vote_for: T::AccountId,
- salt: Vec<u8>,
- ) -> Result {
- ensure!(<Votes<T>>::exists(&commitment), "commitment not found");
- let mut sealed_vote = <Votes<T>>::get(&commitment);
- ensure!(sealed_vote.is_not_revealed(), "vote already revealed");
- // only voter can reveal their own votes
- ensure!(sealed_vote.is_owned_by(voter), "only voter can reveal vote");
- ensure!(
- <ApplicantStakes<T>>::exists(&vote_for),
- "vote for non-applicant not allowed"
- );
- let mut salt = salt;
- // Tries to unseal, if salt is invalid will return error
- sealed_vote.unseal(vote_for, &mut salt, <T as system::Trait>::Hashing::hash)?;
- // Update the revealed vote
- <Votes<T>>::insert(commitment, sealed_vote);
- Ok(())
- }
- fn set_verified_election_parameters(params: ElectionParameters<BalanceOf<T>, T::BlockNumber>) {
- <AnnouncingPeriod<T>>::put(params.announcing_period);
- <VotingPeriod<T>>::put(params.voting_period);
- <RevealingPeriod<T>>::put(params.revealing_period);
- <MinCouncilStake<T>>::put(params.min_council_stake);
- <NewTermDuration<T>>::put(params.new_term_duration);
- CouncilSize::put(params.council_size);
- CandidacyLimit::put(params.candidacy_limit);
- <MinVotingStake<T>>::put(params.min_voting_stake);
- }
- }
- decl_module! {
- pub struct Module<T: Trait> for enum Call where origin: T::Origin {
- fn deposit_event() = default;
- // No origin so this is a priviledged call
- fn on_finalize(now: T::BlockNumber) {
- Self::check_if_stage_is_ending(now);
- }
- // Member can apply during announcing stage only. On first call a minimum stake will need to be provided.
- // Member can make subsequent calls during announcing stage to increase their stake.
- fn apply(origin, stake: BalanceOf<T>) {
- let sender = ensure_signed(origin)?;
- ensure!(Self::can_participate(&sender), "Only members can apply to be on council");
- let stage = Self::stage();
- ensure!(Self::stage().is_some(), "election not running");
- let is_announcing = match stage.unwrap() {
- ElectionStage::Announcing(_) => true,
- _ => false
- };
- ensure!(is_announcing, "election not in announcing stage");
- // minimum stake on first attempt to apply
- if !<ApplicantStakes<T>>::exists(&sender) {
- ensure!(stake >= Self::min_council_stake(), "minimum stake must be provided");
- }
- Self::try_add_applicant(sender.clone(), stake)?;
- Self::deposit_event(RawEvent::Applied(sender));
- }
- fn vote(origin, commitment: T::Hash, stake: BalanceOf<T>) {
- let sender = ensure_signed(origin)?;
- ensure!(Self::can_participate(&sender), "Only members can vote for an applicant");
- let stage = Self::stage();
- ensure!(Self::stage().is_some(), "election not running");
- let is_voting = match stage.unwrap() {
- ElectionStage::Voting(_) => true,
- _ => false
- };
- ensure!(is_voting, "election not in voting stage");
- ensure!(stake >= Self::min_voting_stake(), "voting stake too low");
- Self::try_add_vote(sender.clone(), stake, commitment)?;
- Self::deposit_event(RawEvent::Voted(sender, commitment));
- }
- fn reveal(origin, commitment: T::Hash, vote: T::AccountId, salt: Vec<u8>) {
- let sender = ensure_signed(origin)?;
- ensure!(salt.len() <= 32, "salt too large"); // at most 256 bits salt
- let stage = Self::stage();
- ensure!(Self::stage().is_some(), "election not running");
- let is_revealing = match stage.unwrap() {
- ElectionStage::Revealing(_) => true,
- _ => false
- };
- ensure!(is_revealing, "election not in revealing stage");
- Self::try_reveal_vote(sender.clone(), commitment, vote.clone(), salt)?;
- Self::deposit_event(RawEvent::Revealed(sender, commitment, vote));
- }
- fn set_stage_announcing(origin, ends_at: T::BlockNumber) {
- ensure_root(origin)?;
- ensure!(ends_at > <system::Module<T>>::block_number(), "must end at future block number");
- <Stage<T>>::put(ElectionStage::Announcing(ends_at));
- }
- fn set_stage_revealing(origin, ends_at: T::BlockNumber) {
- ensure_root(origin)?;
- ensure!(ends_at > <system::Module<T>>::block_number(), "must end at future block number");
- <Stage<T>>::put(ElectionStage::Revealing(ends_at));
- }
- fn set_stage_voting(origin, ends_at: T::BlockNumber) {
- ensure_root(origin)?;
- ensure!(ends_at > <system::Module<T>>::block_number(), "must end at future block number");
- <Stage<T>>::put(ElectionStage::Voting(ends_at));
- }
- /// Sets new election parameters. Some combination of parameters that are not desirable, so
- /// the parameters are checked for validity.
- /// The call will fail if an election is in progress. If a council is not being elected for some
- /// reaon after multiple rounds, force_stop_election() can be called to stop elections and followed by
- /// set_election_parameters().
- pub fn set_election_parameters(origin, params: ElectionParameters<BalanceOf<T>, T::BlockNumber>) {
- ensure_root(origin)?;
- ensure!(!Self::is_election_running(), MSG_CANNOT_CHANGE_PARAMS_DURING_ELECTION);
- params.ensure_valid()?;
- Self::set_verified_election_parameters(params);
- }
- fn force_stop_election(origin) {
- ensure_root(origin)?;
- ensure!(Self::is_election_running(), "only running election can be stopped");
- let mut votes = Vec::new();
- for commitment in Self::commitments() {
- votes.push(Self::votes(commitment));
- }
- // no council gets elected
- let empty_council = BTreeMap::new();
- Self::teardown_election (
- &votes,
- &empty_council,
- false /* do not unlock transferable stakes */
- );
- }
- fn force_start_election(origin) {
- ensure_root(origin)?;
- Self::start_election(<council::Module<T>>::active_council())?;
- }
- fn set_auto_start (origin, flag: bool) {
- ensure_root(origin)?;
- AutoStart::put(flag);
- }
- }
- }
- impl<T: Trait> council::CouncilTermEnded for Module<T> {
- fn council_term_ended() {
- if Self::auto_start() {
- let _ = Self::start_election(<council::Module<T>>::active_council());
- }
- }
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- use crate::mock::*;
- use codec::Encode;
- use srml_support::*;
- #[test]
- fn election_starts_when_council_term_ends() {
- initial_test_ext().execute_with(|| {
- System::set_block_number(1);
- assert!(Council::is_term_ended());
- assert!(Election::stage().is_none());
- <Election as council::CouncilTermEnded>::council_term_ended();
- assert!(Election::stage().is_some());
- });
- }
- #[test]
- fn new_stake_reusing_transferable_works() {
- {
- let mut transferable = 0;
- let additional = 100;
- let new_stake = Election::new_stake_reusing_transferable(&mut transferable, additional);
- assert_eq!(new_stake.new, 100);
- assert_eq!(new_stake.transferred, 0);
- }
- {
- let mut transferable = 40;
- let additional = 60;
- let new_stake = Election::new_stake_reusing_transferable(&mut transferable, additional);
- assert_eq!(new_stake.new, 20);
- assert_eq!(new_stake.transferred, 40);
- assert_eq!(transferable, 0);
- }
- {
- let mut transferable = 1000;
- let additional = 100;
- let new_stake = Election::new_stake_reusing_transferable(&mut transferable, additional);
- assert_eq!(new_stake.new, 0);
- assert_eq!(new_stake.transferred, 100);
- assert_eq!(transferable, 900);
- }
- }
- #[test]
- fn check_default_params() {
- // TODO missing test implementation?
- }
- #[test]
- fn should_not_start_new_election_if_already_started() {
- initial_test_ext().execute_with(|| {
- assert_ok!(Election::start_election(vec![]));
- assert_err!(
- Election::start_election(vec![]),
- "election already in progress"
- );
- });
- }
- fn assert_announcing_period(expected_period: <Test as system::Trait>::BlockNumber) {
- assert!(
- Election::is_election_running(),
- "Election Stage was not set"
- );
- let election_stage = Election::stage().unwrap();
- match election_stage {
- election::ElectionStage::Announcing(period) => {
- assert_eq!(period, expected_period, "Election period not set correctly")
- }
- _ => assert!(false, "Election Stage was not correctly set to Announcing"),
- }
- }
- #[test]
- fn start_election_should_work() {
- initial_test_ext().execute_with(|| {
- System::set_block_number(1);
- <AnnouncingPeriod<Test>>::put(20);
- let prev_round = Election::round();
- assert_ok!(Election::start_election(vec![]));
- // election round is bumped
- assert_eq!(Election::round(), prev_round + 1);
- // we enter the announcing stage for a specified period
- assert_announcing_period(1 + Election::announcing_period());
- });
- }
- #[test]
- fn init_transferable_stake_should_work() {
- initial_test_ext().execute_with(|| {
- let existing_council = vec![
- Seat {
- member: 1,
- stake: 100,
- backers: vec![
- Backer {
- member: 2,
- stake: 50,
- },
- Backer {
- member: 3,
- stake: 40,
- },
- Backer {
- member: 10,
- stake: 10,
- },
- ],
- },
- Seat {
- member: 2,
- stake: 200,
- backers: vec![
- Backer {
- member: 1,
- stake: 10,
- },
- Backer {
- member: 3,
- stake: 60,
- },
- Backer {
- member: 20,
- stake: 20,
- },
- ],
- },
- Seat {
- member: 3,
- stake: 300,
- backers: vec![
- Backer {
- member: 1,
- stake: 20,
- },
- Backer {
- member: 2,
- stake: 40,
- },
- ],
- },
- ];
- Election::initialize_transferable_stakes(existing_council);
- let mut existing_stake_holders = Election::existing_stake_holders();
- existing_stake_holders.sort();
- assert_eq!(existing_stake_holders, vec![1, 2, 3, 10, 20]);
- assert_eq!(Election::transferable_stakes(&1).seat, 100);
- assert_eq!(Election::transferable_stakes(&1).backing, 30);
- assert_eq!(Election::transferable_stakes(&2).seat, 200);
- assert_eq!(Election::transferable_stakes(&2).backing, 90);
- assert_eq!(Election::transferable_stakes(&3).seat, 300);
- assert_eq!(Election::transferable_stakes(&3).backing, 100);
- assert_eq!(Election::transferable_stakes(&10).seat, 0);
- assert_eq!(Election::transferable_stakes(&10).backing, 10);
- assert_eq!(Election::transferable_stakes(&20).seat, 0);
- assert_eq!(Election::transferable_stakes(&20).backing, 20);
- });
- }
- #[test]
- fn try_add_applicant_should_work() {
- initial_test_ext().execute_with(|| {
- assert!(Election::applicants().len() == 0);
- let applicant = 20 as u64;
- let starting_balance = 1000 as u64;
- let _ = Balances::deposit_creating(&applicant, starting_balance);
- let stake = 100 as u64;
- assert!(Election::try_add_applicant(applicant, stake).is_ok());
- assert_eq!(Election::applicants(), vec![applicant]);
- assert_eq!(Election::applicant_stakes(applicant).new, stake);
- assert_eq!(Election::applicant_stakes(applicant).transferred, 0);
- assert_eq!(Balances::free_balance(&applicant), starting_balance - stake);
- });
- }
- #[test]
- fn increasing_applicant_stake_should_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let starting_stake = 100 as u64;
- <Applicants<Test>>::put(vec![applicant]);
- <ApplicantStakes<Test>>::insert(
- applicant,
- Stake {
- new: starting_stake,
- transferred: 0,
- },
- );
- let additional_stake = 100 as u64;
- let _ = Balances::deposit_creating(&applicant, additional_stake);
- assert!(Election::try_add_applicant(applicant, additional_stake).is_ok());
- assert_eq!(
- Election::applicant_stakes(applicant).new,
- starting_stake + additional_stake
- );
- assert_eq!(Election::applicant_stakes(applicant).transferred, 0)
- });
- }
- #[test]
- fn using_transferable_seat_stake_should_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let _ = Balances::deposit_creating(&applicant, 5000);
- <ExistingStakeHolders<Test>>::put(vec![applicant]);
- save_transferable_stake(
- applicant,
- TransferableStake {
- seat: 1000,
- backing: 0,
- },
- );
- <Applicants<Test>>::put(vec![applicant]);
- let starting_stake = Stake {
- new: 100,
- transferred: 0,
- };
- <ApplicantStakes<Test>>::insert(applicant, starting_stake);
- // transferable stake covers new stake
- assert!(Election::try_add_applicant(applicant, 600).is_ok());
- assert_eq!(
- Election::applicant_stakes(applicant).new,
- starting_stake.new
- );
- assert_eq!(Election::applicant_stakes(applicant).transferred, 600);
- assert_eq!(Election::transferable_stakes(applicant).seat, 400);
- assert_eq!(Balances::free_balance(applicant), 5000);
- // all remaining transferable stake is consumed and free balance covers remaining stake
- assert!(Election::try_add_applicant(applicant, 1000).is_ok());
- assert_eq!(
- Election::applicant_stakes(applicant).new,
- starting_stake.new + 600
- );
- assert_eq!(Election::applicant_stakes(applicant).transferred, 1000);
- assert_eq!(Election::transferable_stakes(applicant).seat, 0);
- assert_eq!(Balances::free_balance(applicant), 4400);
- });
- }
- #[test]
- fn moving_to_voting_without_enough_applicants_should_not_work() {
- initial_test_ext().execute_with(|| {
- System::set_block_number(1);
- <AnnouncingPeriod<Test>>::put(20);
- CouncilSize::put(10);
- Election::move_to_announcing_stage();
- let round = Election::round();
- // add applicants
- <Applicants<Test>>::put(vec![10, 20, 30]);
- let stake = Stake {
- new: 10,
- transferred: 0,
- };
- let applicants = Election::applicants();
- for applicant in applicants.iter() {
- <ApplicantStakes<Test>>::insert(applicant, stake);
- }
- // make sure we are testing the condition that we don't have enough applicants
- assert!(Election::council_size_usize() > applicants.len());
- // try to move to voting stage
- let ann_ends = Election::stage_ends_at().unwrap();
- System::set_block_number(ann_ends);
- Election::on_announcing_ended();
- // A new round should have been started
- assert_eq!(Election::round(), round + 1);
- // A new announcing period started
- assert_announcing_period(ann_ends + Election::announcing_period());
- // applicants list should be unchanged..
- assert_eq!(Election::applicants(), applicants);
- });
- }
- #[test]
- fn top_applicants_move_to_voting_stage() {
- initial_test_ext().execute_with(|| {
- <Applicants<Test>>::put(vec![10, 20, 30, 40]);
- let mut applicants = Election::applicants();
- for (i, applicant) in applicants.iter().enumerate() {
- <ApplicantStakes<Test>>::insert(
- applicant,
- Stake {
- new: (i * 10) as u64,
- transferred: 0,
- },
- );
- }
- let rejected = Election::find_least_staked_applicants(&mut applicants, 3);
- assert_eq!(rejected.to_vec(), vec![10]);
- <Applicants<Test>>::put(vec![40, 30, 20, 10]);
- let mut applicants = Election::applicants();
- for applicant in applicants.iter() {
- <ApplicantStakes<Test>>::insert(
- applicant,
- Stake {
- new: 20,
- transferred: 0,
- },
- );
- }
- // stable sort is preserving order when two elements are equivalent
- let rejected = Election::find_least_staked_applicants(&mut applicants, 3);
- assert_eq!(rejected.to_vec(), vec![40]);
- });
- }
- #[test]
- fn refunding_applicant_stakes_should_work() {
- initial_test_ext().execute_with(|| {
- let _ = Balances::deposit_creating(&1, 1000);
- let _ = Balances::deposit_creating(&2, 7000);
- let _ = Balances::reserve(&2, 5000);
- let _ = Balances::deposit_creating(&3, 8000);
- let _ = Balances::reserve(&3, 5000);
- <Applicants<Test>>::put(vec![1, 2, 3]);
- save_transferable_stake(
- 1,
- TransferableStake {
- seat: 50,
- backing: 0,
- },
- );
- save_transferable_stake(
- 2,
- TransferableStake {
- seat: 0,
- backing: 0,
- },
- );
- save_transferable_stake(
- 3,
- TransferableStake {
- seat: 0,
- backing: 0,
- },
- );
- <ApplicantStakes<Test>>::insert(
- 1,
- Stake {
- new: 100,
- transferred: 200,
- },
- );
- <ApplicantStakes<Test>>::insert(
- 2,
- Stake {
- new: 300,
- transferred: 400,
- },
- );
- <ApplicantStakes<Test>>::insert(
- 3,
- Stake {
- new: 500,
- transferred: 600,
- },
- );
- Election::drop_applicants(&vec![2, 3][..]);
- assert_eq!(Election::applicants(), vec![1]);
- assert_eq!(Election::applicant_stakes(1).new, 100);
- assert_eq!(Election::applicant_stakes(1).transferred, 200);
- assert_eq!(Election::transferable_stakes(1).seat, 50);
- assert_eq!(Balances::free_balance(&1), 1000);
- //assert_eq!(Election::applicant_stakes(2), Default::default());
- assert!(!<ApplicantStakes<Test>>::exists(2));
- assert_eq!(Election::transferable_stakes(2).seat, 400);
- assert_eq!(Balances::free_balance(&2), 2300);
- //assert_eq!(Election::applicant_stakes(3), Default::default());
- assert!(!<ApplicantStakes<Test>>::exists(3));
- assert_eq!(Election::transferable_stakes(3).seat, 600);
- assert_eq!(Balances::free_balance(&3), 3500);
- });
- }
- #[test]
- fn voting_should_work() {
- initial_test_ext().execute_with(|| {
- let _ = Balances::deposit_creating(&20, 1000);
- let payload = vec![10u8];
- let commitment = <Test as system::Trait>::Hashing::hash(&payload[..]);
- assert!(Election::try_add_vote(20, 100, commitment).is_ok());
- assert_eq!(Election::commitments(), vec![commitment]);
- assert_eq!(Election::votes(commitment).voter, 20);
- assert_eq!(Election::votes(commitment).commitment, commitment);
- assert_eq!(
- Election::votes(commitment).stake,
- Stake {
- new: 100,
- transferred: 0,
- }
- );
- assert_eq!(Balances::free_balance(&20), 900);
- });
- }
- fn save_transferable_stake(id: u64, stake: TransferableStake<u64>) {
- <TransferableStakes<Test>>::insert(id, stake);
- }
- #[test]
- fn votes_can_be_covered_by_transferable_stake() {
- initial_test_ext().execute_with(|| {
- let _ = Balances::deposit_creating(&20, 1000);
- save_transferable_stake(
- 20,
- TransferableStake {
- seat: 0,
- backing: 500,
- },
- );
- let payload = vec![10u8];
- let commitment = <Test as system::Trait>::Hashing::hash(&payload[..]);
- assert!(Election::try_add_vote(20, 100, commitment).is_ok());
- assert_eq!(Election::commitments(), vec![commitment]);
- assert_eq!(Election::votes(commitment).voter, 20);
- assert_eq!(Election::votes(commitment).commitment, commitment);
- assert_eq!(
- Election::votes(commitment).stake,
- Stake {
- new: 0,
- transferred: 100,
- }
- );
- assert_eq!(Balances::free_balance(&20), 1000);
- });
- }
- #[test]
- fn voting_without_enough_balance_should_not_work() {
- initial_test_ext().execute_with(|| {
- let _ = Balances::deposit_creating(&20, 100);
- save_transferable_stake(
- 20,
- TransferableStake {
- seat: 0,
- backing: 500,
- },
- );
- let payload = vec![10u8];
- let commitment = <Test as system::Trait>::Hashing::hash(&payload[..]);
- assert!(Election::try_add_vote(20, 1000, commitment).is_err());
- assert_eq!(Election::commitments(), vec![]);
- assert!(!<Votes<Test>>::exists(commitment));
- assert_eq!(Balances::free_balance(&20), 100);
- });
- }
- #[test]
- fn voting_with_existing_commitment_should_not_work() {
- initial_test_ext().execute_with(|| {
- let _ = Balances::deposit_creating(&20, 1000);
- save_transferable_stake(
- 20,
- TransferableStake {
- seat: 0,
- backing: 500,
- },
- );
- let payload = vec![10u8];
- let commitment = <Test as system::Trait>::Hashing::hash(&payload[..]);
- assert!(Election::try_add_vote(20, 100, commitment).is_ok());
- assert_eq!(Election::commitments(), vec![commitment]);
- assert_eq!(Election::votes(commitment).voter, 20);
- assert_eq!(Election::votes(commitment).commitment, commitment);
- assert_eq!(
- Election::votes(commitment).stake,
- Stake {
- new: 0,
- transferred: 100,
- }
- );
- assert_eq!(Balances::free_balance(&20), 1000);
- assert!(Election::try_add_vote(30, 100, commitment).is_err());
- });
- }
- fn make_commitment_for_applicant(
- applicant: <Test as system::Trait>::AccountId,
- salt: &mut Vec<u8>,
- ) -> <Test as system::Trait>::Hash {
- let mut payload = applicant.encode();
- payload.append(salt);
- <Test as system::Trait>::Hashing::hash(&payload[..])
- }
- #[test]
- fn revealing_vote_works() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let salt = vec![128u8];
- let commitment = make_commitment_for_applicant(applicant, &mut salt.clone());
- let voter = 10 as u64;
- <ApplicantStakes<Test>>::insert(
- &applicant,
- Stake {
- new: 0,
- transferred: 0,
- },
- );
- <Votes<Test>>::insert(
- &commitment,
- SealedVote::new(
- voter,
- Stake {
- new: 100,
- transferred: 0,
- },
- commitment,
- ),
- );
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- assert!(Election::try_reveal_vote(voter, commitment, applicant, salt).is_ok());
- assert_eq!(
- <Votes<Test>>::get(commitment).get_vote().unwrap(),
- applicant
- );
- });
- }
- #[test]
- fn revealing_with_bad_salt_should_not_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let salt = vec![128u8];
- let commitment = make_commitment_for_applicant(applicant, &mut salt.clone());
- let voter = 10 as u64;
- <ApplicantStakes<Test>>::insert(
- &applicant,
- Stake {
- new: 0,
- transferred: 0,
- },
- );
- <Votes<Test>>::insert(
- &commitment,
- SealedVote::new(
- voter,
- Stake {
- new: 100,
- transferred: 0,
- },
- commitment,
- ),
- );
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- assert!(Election::try_reveal_vote(voter, commitment, applicant, vec![]).is_err());
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- });
- }
- #[test]
- fn revealing_non_matching_commitment_should_not_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let salt = vec![128u8];
- let commitment = make_commitment_for_applicant(100, &mut salt.clone());
- let voter = 10 as u64;
- <ApplicantStakes<Test>>::insert(
- &applicant,
- Stake {
- new: 0,
- transferred: 0,
- },
- );
- assert!(Election::try_reveal_vote(voter, commitment, applicant, vec![]).is_err());
- });
- }
- #[test]
- fn revealing_for_non_applicant_should_not_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let salt = vec![128u8];
- let commitment = make_commitment_for_applicant(applicant, &mut salt.clone());
- let voter = 10 as u64;
- <Votes<Test>>::insert(
- &commitment,
- SealedVote::new(
- voter,
- Stake {
- new: 100,
- transferred: 0,
- },
- commitment,
- ),
- );
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- assert!(Election::try_reveal_vote(voter, commitment, applicant, vec![]).is_err());
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- });
- }
- #[test]
- fn revealing_by_non_committer_should_not_work() {
- initial_test_ext().execute_with(|| {
- let applicant = 20 as u64;
- let salt = vec![128u8];
- let commitment = make_commitment_for_applicant(applicant, &mut salt.clone());
- let voter = 10 as u64;
- let not_voter = 100 as u64;
- <ApplicantStakes<Test>>::insert(
- &applicant,
- Stake {
- new: 0,
- transferred: 0,
- },
- );
- <Votes<Test>>::insert(
- &commitment,
- SealedVote::new(
- voter,
- Stake {
- new: 100,
- transferred: 0,
- },
- commitment,
- ),
- );
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- assert!(Election::try_reveal_vote(not_voter, commitment, applicant, salt).is_err());
- assert!(<Votes<Test>>::get(commitment).is_not_revealed());
- });
- }
- pub fn mock_votes(
- mock: Vec<(u64, u64, u64, u64)>,
- ) -> Vec<SealedVote<u64, Stake<u64>, primitives::H256, u64>> {
- let commitment = make_commitment_for_applicant(1, &mut vec![0u8]);
- mock.into_iter()
- .map(|(voter, stake_ref, stake_tran, applicant)| {
- SealedVote::new_unsealed(
- voter as u64,
- Stake {
- new: stake_ref,
- transferred: stake_tran,
- },
- commitment,
- applicant as u64,
- )
- })
- .collect()
- }
- #[test]
- fn vote_tallying_should_work() {
- initial_test_ext().execute_with(|| {
- let votes = mock_votes(vec![
- // (voter, stake[new], stake[transferred], applicant)
- (10, 100, 0, 100),
- (10, 150, 0, 100),
- (10, 500, 0, 200),
- (20, 200, 0, 200),
- (30, 300, 0, 300),
- (30, 400, 0, 300),
- ]);
- let tally = Election::tally_votes(&votes);
- assert_eq!(tally.len(), 3);
- assert_eq!(tally.get(&100).unwrap().member, 100);
- assert_eq!(
- tally.get(&100).unwrap().backers,
- vec![
- Backer {
- member: 10 as u64,
- stake: 100 as u64,
- },
- Backer {
- member: 10 as u64,
- stake: 150 as u64,
- },
- ]
- );
- assert_eq!(tally.get(&200).unwrap().member, 200);
- assert_eq!(
- tally.get(&200).unwrap().backers,
- vec![
- Backer {
- member: 10 as u64,
- stake: 500 as u64,
- },
- Backer {
- member: 20 as u64,
- stake: 200 as u64,
- }
- ]
- );
- assert_eq!(tally.get(&300).unwrap().member, 300);
- assert_eq!(
- tally.get(&300).unwrap().backers,
- vec![
- Backer {
- member: 30 as u64,
- stake: 300 as u64,
- },
- Backer {
- member: 30 as u64,
- stake: 400 as u64,
- }
- ]
- );
- });
- }
- #[test]
- fn filter_top_staked_applicants_should_work() {
- initial_test_ext().execute_with(|| {
- // filter_top_staked depends on order of applicants
- <Applicants<Test>>::put(vec![100, 200, 300]);
- {
- let votes = mock_votes(vec![
- // (voter, stake[new], stake[transferred], applicant)
- (10, 100, 0, 100),
- (10, 150, 0, 100),
- (10, 500, 0, 200),
- (20, 200, 0, 200),
- (30, 300, 0, 300),
- (30, 400, 0, 300),
- ]);
- let mut tally = Election::tally_votes(&votes);
- assert_eq!(tally.len(), 3);
- Election::filter_top_staked(&mut tally, 3);
- assert_eq!(tally.len(), 3);
- }
- {
- let votes = mock_votes(vec![
- // (voter, stake[new], stake[transferred], applicant)
- (10, 100, 0, 100),
- (10, 150, 0, 100),
- (10, 500, 0, 200),
- (20, 200, 0, 200),
- (30, 300, 0, 300),
- (30, 400, 0, 300),
- ]);
- let mut tally = Election::tally_votes(&votes);
- assert_eq!(tally.len(), 3);
- Election::filter_top_staked(&mut tally, 2);
- assert_eq!(tally.len(), 2);
- assert!(tally.get(&200).is_some());
- assert!(tally.get(&300).is_some());
- }
- });
- }
- #[test]
- fn drop_unelected_applicants_should_work() {
- initial_test_ext().execute_with(|| {
- <Applicants<Test>>::put(vec![100, 200, 300]);
- let _ = Balances::deposit_creating(&100, 2000);
- let _ = Balances::reserve(&100, 1000);
- <ApplicantStakes<Test>>::insert(
- 100,
- Stake {
- new: 20 as u64,
- transferred: 50 as u64,
- },
- );
- save_transferable_stake(
- 100,
- TransferableStake {
- seat: 100,
- backing: 0,
- },
- );
- let mut new_council: BTreeMap<u64, Seat<u64, u64>> = BTreeMap::new();
- new_council.insert(
- 200 as u64,
- Seat {
- member: 200 as u64,
- stake: 0 as u64,
- backers: vec![],
- },
- );
- new_council.insert(
- 300 as u64,
- Seat {
- member: 300 as u64,
- stake: 0 as u64,
- backers: vec![],
- },
- );
- Election::drop_unelected_applicants(&new_council);
- // applicant dropped
- assert_eq!(Election::applicants(), vec![200, 300]);
- assert!(!<ApplicantStakes<Test>>::exists(100));
- // and refunded
- assert_eq!(Election::transferable_stakes(100).seat, 150);
- assert_eq!(Balances::free_balance(&100), 1020);
- assert_eq!(Balances::reserved_balance(&100), 980);
- });
- }
- #[test]
- fn refunding_voting_stakes_should_work() {
- initial_test_ext().execute_with(|| {
- // voters' balances
- let _ = Balances::deposit_creating(&10, 6000);
- let _ = Balances::reserve(&10, 5000);
- let _ = Balances::deposit_creating(&20, 7000);
- let _ = Balances::reserve(&20, 5000);
- let _ = Balances::deposit_creating(&30, 8000);
- let _ = Balances::reserve(&30, 5000);
- save_transferable_stake(
- 10,
- TransferableStake {
- seat: 0,
- backing: 100,
- },
- );
- save_transferable_stake(
- 20,
- TransferableStake {
- seat: 0,
- backing: 200,
- },
- );
- save_transferable_stake(
- 30,
- TransferableStake {
- seat: 0,
- backing: 300,
- },
- );
- let votes = mock_votes(vec![
- // (voter, stake[new], stake[transferred], applicant)
- (10, 100, 20, 100),
- (20, 200, 40, 100),
- (30, 300, 60, 100),
- (10, 500, 70, 200),
- (20, 600, 80, 200),
- (30, 700, 90, 200),
- (10, 800, 100, 300),
- (20, 900, 120, 300),
- (30, 1000, 140, 300),
- ]);
- let mut new_council: BTreeMap<u64, Seat<u64, u64>> = BTreeMap::new();
- new_council.insert(
- 200 as u64,
- Seat {
- member: 200 as u64,
- stake: 0 as u64,
- backers: vec![],
- },
- );
- new_council.insert(
- 300 as u64,
- Seat {
- member: 300 as u64,
- stake: 0 as u64,
- backers: vec![],
- },
- );
- Election::refund_voting_stakes(&votes, &new_council);
- assert_eq!(Balances::free_balance(&10), 1100);
- assert_eq!(Balances::reserved_balance(&10), 4900);
- assert_eq!(Balances::free_balance(&20), 2200);
- assert_eq!(Balances::reserved_balance(&20), 4800);
- assert_eq!(Balances::free_balance(&30), 3300);
- assert_eq!(Balances::reserved_balance(&30), 4700);
- assert_eq!(Election::transferable_stakes(10).backing, 120);
- assert_eq!(Election::transferable_stakes(20).backing, 240);
- assert_eq!(Election::transferable_stakes(30).backing, 360);
- });
- }
- #[test]
- fn unlock_transferable_stakes_should_work() {
- initial_test_ext().execute_with(|| {
- <ExistingStakeHolders<Test>>::put(vec![10, 20, 30]);
- let _ = Balances::deposit_creating(&10, 6000);
- let _ = Balances::reserve(&10, 5000);
- save_transferable_stake(
- 10,
- TransferableStake {
- seat: 50,
- backing: 100,
- },
- );
- let _ = Balances::deposit_creating(&20, 7000);
- let _ = Balances::reserve(&20, 5000);
- save_transferable_stake(
- 20,
- TransferableStake {
- seat: 60,
- backing: 200,
- },
- );
- let _ = Balances::deposit_creating(&30, 8000);
- let _ = Balances::reserve(&30, 5000);
- save_transferable_stake(
- 30,
- TransferableStake {
- seat: 70,
- backing: 300,
- },
- );
- Election::unlock_transferable_stakes();
- assert_eq!(Balances::free_balance(&10), 1150);
- assert_eq!(Balances::free_balance(&20), 2260);
- assert_eq!(Balances::free_balance(&30), 3370);
- });
- }
- #[test]
- fn council_elected_hook_should_work() {
- initial_test_ext().execute_with(|| {
- let mut new_council: BTreeMap<u64, Seat<u64, u64>> = BTreeMap::new();
- new_council.insert(
- 200 as u64,
- Seat {
- member: 200 as u64,
- stake: 10 as u64,
- backers: vec![],
- },
- );
- new_council.insert(
- 300 as u64,
- Seat {
- member: 300 as u64,
- stake: 20 as u64,
- backers: vec![],
- },
- );
- assert_eq!(Council::active_council().len(), 0);
- let new_council = new_council
- .into_iter()
- .map(|(_, seat)| seat.clone())
- .collect();
- <Test as election::Trait>::CouncilElected::council_elected(new_council, 10);
- assert_eq!(Council::active_council().len(), 2);
- });
- }
- #[test]
- fn simulation() {
- initial_test_ext().execute_with(|| {
- assert_eq!(Council::active_council().len(), 0);
- assert!(Election::stage().is_none());
- CouncilSize::put(10);
- <MinCouncilStake<Test>>::put(50);
- <AnnouncingPeriod<Test>>::put(10);
- <VotingPeriod<Test>>::put(10);
- <RevealingPeriod<Test>>::put(10);
- CandidacyLimit::put(20);
- <NewTermDuration<Test>>::put(100);
- <MinVotingStake<Test>>::put(10);
- for i in 1..30 {
- let _ = Balances::deposit_creating(&(i as u64), 50000);
- }
- System::set_block_number(1);
- assert_ok!(Election::start_election(vec![]));
- for i in 1..20 {
- if i < 21 {
- assert!(Election::apply(Origin::signed(i), 150).is_ok());
- } else {
- assert!(Election::apply(Origin::signed(i + 1000), 150).is_err()); // not enough free balance
- assert!(Election::apply(Origin::signed(i), 20).is_err()); // not enough minimum stake
- }
- }
- let n = 1 + Election::announcing_period();
- System::set_block_number(n);
- let _ = Election::on_finalize(n);
- for i in 1..20 {
- assert!(Election::vote(
- Origin::signed(i),
- make_commitment_for_applicant(i, &mut vec![40u8]),
- 100
- )
- .is_ok());
- assert!(Election::vote(
- Origin::signed(i),
- make_commitment_for_applicant(i, &mut vec![41u8]),
- 100
- )
- .is_ok());
- assert!(Election::vote(
- Origin::signed(i),
- make_commitment_for_applicant(i + 1000, &mut vec![42u8]),
- 100
- )
- .is_ok());
- }
- let n = n + Election::voting_period();
- System::set_block_number(n);
- let _ = Election::on_finalize(n);
- for i in 1..20 {
- assert!(Election::reveal(
- Origin::signed(i),
- make_commitment_for_applicant(i, &mut vec![40u8]),
- i,
- vec![40u8]
- )
- .is_ok());
- //wrong salt
- assert!(Election::reveal(
- Origin::signed(i),
- make_commitment_for_applicant(i, &mut vec![41u8]),
- i,
- vec![]
- )
- .is_err());
- //vote not for valid applicant
- assert!(Election::reveal(
- Origin::signed(i),
- make_commitment_for_applicant(i + 1000, &mut vec![42u8]),
- i + 1000,
- vec![42u8]
- )
- .is_err());
- }
- let n = n + Election::revealing_period();
- System::set_block_number(n);
- let _ = Election::on_finalize(n);
- assert_eq!(
- Council::active_council().len(),
- Election::council_size_usize()
- );
- for (i, seat) in Council::active_council().iter().enumerate() {
- assert_eq!(seat.member, (i + 1) as u64);
- }
- assert!(Election::stage().is_none());
- // When council term ends.. start a new election.
- assert_ok!(Election::start_election(vec![]));
- });
- }
- #[test]
- fn setting_election_parameters() {
- initial_test_ext().execute_with(|| {
- let default_parameters: ElectionParameters<u64, u64> = ElectionParameters::default();
- // default all zeros is invalid
- assert!(default_parameters.ensure_valid().is_err());
- let new_parameters = ElectionParameters {
- announcing_period: 1,
- voting_period: 2,
- revealing_period: 3,
- council_size: 4,
- candidacy_limit: 5,
- min_voting_stake: 6,
- min_council_stake: 7,
- new_term_duration: 8,
- };
- assert_ok!(Election::set_election_parameters(
- Origin::ROOT,
- new_parameters
- ));
- assert_eq!(
- <AnnouncingPeriod<Test>>::get(),
- new_parameters.announcing_period
- );
- assert_eq!(<VotingPeriod<Test>>::get(), new_parameters.voting_period);
- assert_eq!(
- <RevealingPeriod<Test>>::get(),
- new_parameters.revealing_period
- );
- assert_eq!(
- <MinCouncilStake<Test>>::get(),
- new_parameters.min_council_stake
- );
- assert_eq!(
- <NewTermDuration<Test>>::get(),
- new_parameters.new_term_duration
- );
- assert_eq!(CouncilSize::get(), new_parameters.council_size);
- assert_eq!(CandidacyLimit::get(), new_parameters.candidacy_limit);
- assert_eq!(
- <MinVotingStake<Test>>::get(),
- new_parameters.min_voting_stake
- );
- });
- }
- }
|