diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 2c38d96c12..f389625eae 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -2295,9 +2295,10 @@ dependencies = [ "srml-aura 2.0.0", "srml-authorship 0.1.0", "srml-balances 2.0.0", + "srml-collective 2.0.0", "srml-contracts 2.0.0", - "srml-council 2.0.0", "srml-democracy 2.0.0", + "srml-elections 2.0.0", "srml-executive 2.0.0", "srml-finality-tracker 2.0.0", "srml-grandpa 2.0.0", @@ -3642,6 +3643,23 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-collective" +version = "2.0.0" +dependencies = [ + "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-balances 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-contracts" version = "2.0.0" @@ -3667,10 +3685,9 @@ dependencies = [ ] [[package]] -name = "srml-council" +name = "srml-democracy" version = "2.0.0" dependencies = [ - "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3678,16 +3695,16 @@ dependencies = [ "sr-primitives 2.0.0", "sr-std 2.0.0", "srml-balances 2.0.0", - "srml-democracy 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", "substrate-primitives 2.0.0", ] [[package]] -name = "srml-democracy" +name = "srml-elections" version = "2.0.0" dependencies = [ + "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 2c2a226ca2..7790c4127a 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -69,8 +69,9 @@ members = [ "srml/aura", "srml/balances", "srml/contracts", - "srml/council", + "srml/collective", "srml/democracy", + "srml/elections", "srml/example", "srml/executive", "srml/finality-tracker", diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index 78f74a6e29..a11acac1d5 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -19,8 +19,8 @@ use primitives::{ed25519, sr25519, Pair, crypto::UncheckedInto}; use node_primitives::{AccountId, AuraId, Balance}; use node_runtime::{ - AuraConfig, BalancesConfig, ContractsConfig, CouncilSeatsConfig, DemocracyConfig, - GrandpaConfig, IndicesConfig, SessionConfig, StakingConfig, SudoConfig, + GrandpaConfig, BalancesConfig, ContractsConfig, ElectionsConfig, DemocracyConfig, CouncilConfig, + AuraConfig, IndicesConfig, SessionConfig, StakingConfig, SudoConfig, TechnicalCommitteeConfig, SystemConfig, TimestampConfig, WASM_BINARY, Perbill, SessionKeys, StakerStatus, DAYS, DOLLARS, MILLICENTS, SECS_PER_BLOCK, }; @@ -130,8 +130,16 @@ fn staging_testnet_config_genesis() -> GenesisConfig { invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), - council_seats: Some(CouncilSeatsConfig { - active_council: vec![], + collective_Instance1: Some(CouncilConfig { + members: vec![], + phantom: Default::default(), + }), + collective_Instance2: Some(TechnicalCommitteeConfig { + members: vec![], + phantom: Default::default(), + }), + elections: Some(ElectionsConfig { + members: vec![], presentation_duration: 1 * DAYS, term_duration: 28 * DAYS, desired_seats: 0, @@ -228,7 +236,7 @@ pub fn testnet_genesis( const ENDOWMENT: Balance = 10_000_000 * DOLLARS; const STASH: Balance = 100 * DOLLARS; - let council_desired_seats = (endowed_accounts.len() / 2 - initial_authorities.len()) as u32; + let desired_seats = (endowed_accounts.len() / 2 - initial_authorities.len()) as u32; GenesisConfig { system: Some(SystemConfig { @@ -257,13 +265,21 @@ pub fn testnet_genesis( invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), - council_seats: Some(CouncilSeatsConfig { - active_council: endowed_accounts.iter() + collective_Instance1: Some(CouncilConfig { + members: vec![], + phantom: Default::default(), + }), + collective_Instance2: Some(TechnicalCommitteeConfig { + members: vec![], + phantom: Default::default(), + }), + elections: Some(ElectionsConfig { + members: endowed_accounts.iter() .filter(|&endowed| initial_authorities.iter().find(|&(_, controller, ..)| controller == endowed).is_none()) .map(|a| (a.clone(), 1000000)).collect(), presentation_duration: 10, term_duration: 1000000, - desired_seats: council_desired_seats, + desired_seats: desired_seats, }), timestamp: Some(TimestampConfig { minimum_period: 2, // 2*2=4 second block time. diff --git a/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index 1d6f1d1794..ae306d33e5 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -343,7 +343,9 @@ mod tests { invulnerables: vec![alice(), bob(), charlie()], }), democracy: Some(Default::default()), - council_seats: Some(Default::default()), + collective_Instance1: Some(Default::default()), + collective_Instance2: Some(Default::default()), + elections: Some(Default::default()), timestamp: Some(Default::default()), contracts: Some(ContractsConfig { current_schedule: Default::default(), diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index 83708be6da..3bf28f7df2 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -20,8 +20,9 @@ aura = { package = "srml-aura", path = "../../srml/aura", default-features = fal authorship = { package = "srml-authorship", path = "../../srml/authorship", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default-features = false } contracts = { package = "srml-contracts", path = "../../srml/contracts", default-features = false } -council = { package = "srml-council", path = "../../srml/council", default-features = false } +collective = { package = "srml-collective", path = "../../srml/collective", default-features = false } democracy = { package = "srml-democracy", path = "../../srml/democracy", default-features = false } +elections = { package = "srml-elections", path = "../../srml/elections", default-features = false } executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } finality-tracker = { package = "srml-finality-tracker", path = "../../srml/finality-tracker", default-features = false } grandpa = { package = "srml-grandpa", path = "../../srml/grandpa", default-features = false } @@ -56,8 +57,9 @@ std = [ "authorship/std", "balances/std", "contracts/std", - "council/std", + "collective/std", "democracy/std", + "elections/std", "executive/std", "finality-tracker/std", "grandpa/std", diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 4382227de4..e97959095c 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -40,9 +40,7 @@ use runtime_primitives::traits::{ BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, Convert, }; use version::RuntimeVersion; -use council::{motions as council_motions, VoteIndex}; -#[cfg(feature = "std")] -use council::seats as council_seats; +use elections::VoteIndex; #[cfg(any(feature = "std", test))] use version::NativeVersion; use substrate_primitives::OpaqueMetadata; @@ -71,8 +69,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 107, - impl_version: 107, + spec_version: 108, + impl_version: 108, apis: RUNTIME_API_VERSIONS, }; @@ -265,14 +263,22 @@ impl democracy::Trait for Runtime { type VotingPeriod = VotingPeriod; type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; - type ExternalOrigin = council_motions::EnsureProportionAtLeast<_1, _2, AccountId>; - type ExternalMajorityOrigin = council_motions::EnsureProportionAtLeast<_2, _3, AccountId>; - type EmergencyOrigin = council_motions::EnsureProportionAtLeast<_1, _1, AccountId>; - type CancellationOrigin = council_motions::EnsureProportionAtLeast<_2, _3, AccountId>; - type VetoOrigin = council_motions::EnsureMember; + type ExternalOrigin = collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilInstance>; + type ExternalMajorityOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; + type ExternalPushOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalInstance>; + type EmergencyOrigin = collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilInstance>; + type CancellationOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; + type VetoOrigin = collective::EnsureMember; type CooloffPeriod = CooloffPeriod; } +type CouncilInstance = collective::Instance1; +impl collective::Trait for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; +} + parameter_types! { pub const CandidacyBond: Balance = 10 * DOLLARS; pub const VotingBond: Balance = 1 * DOLLARS; @@ -281,28 +287,30 @@ parameter_types! { pub const CarryCount: u32 = 6; // one additional vote should go by before an inactive voter can be reaped. pub const InactiveGracePeriod: VoteIndex = 1; - pub const CouncilVotingPeriod: BlockNumber = 2 * DAYS; + pub const ElectionsVotingPeriod: BlockNumber = 2 * DAYS; pub const DecayRatio: u32 = 0; } -impl council::Trait for Runtime { +impl elections::Trait for Runtime { type Event = Event; + type Currency = Balances; type BadPresentation = (); type BadReaper = (); type BadVoterIndex = (); type LoserCandidate = (); - type OnMembersChanged = CouncilMotions; + type ChangeMembers = Council; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; type VotingFee = VotingFee; type PresentSlashPerVoter = PresentSlashPerVoter; type CarryCount = CarryCount; type InactiveGracePeriod = InactiveGracePeriod; - type CouncilVotingPeriod = CouncilVotingPeriod; + type VotingPeriod = ElectionsVotingPeriod; type DecayRatio = DecayRatio; } -impl council::motions::Trait for Runtime { +type TechnicalInstance = collective::Instance2; +impl collective::Trait for Runtime { type Origin = Origin; type Proposal = Call; type Event = Event; @@ -317,8 +325,8 @@ parameter_types! { impl treasury::Trait for Runtime { type Currency = Balances; - type ApproveOrigin = council_motions::EnsureMembers<_4, AccountId>; - type RejectOrigin = council_motions::EnsureMembers<_2, AccountId>; + type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilInstance>; + type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilInstance>; type Event = Event; type MintedForSpending = (); type ProposalRejection = (); @@ -406,9 +414,9 @@ construct_runtime!( Session: session::{Module, Call, Storage, Event, Config}, Staking: staking::{default, OfflineWorker}, Democracy: democracy::{Module, Call, Storage, Config, Event}, - Council: council::{Module, Call, Storage, Event}, - CouncilMotions: council_motions::{Module, Call, Storage, Event, Origin}, - CouncilSeats: council_seats::{Config}, + Council: collective::::{Module, Call, Storage, Origin, Event, Config}, + TechnicalCommittee: collective::::{Module, Call, Storage, Origin, Event, Config}, + Elections: elections::{Module, Call, Storage, Event, Config}, FinalityTracker: finality_tracker::{Module, Call, Inherent}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, Treasury: treasury::{Module, Call, Storage, Event}, diff --git a/substrate/srml/aura/src/mock.rs b/substrate/srml/aura/src/mock.rs index 7482388cd1..fad511baba 100644 --- a/substrate/srml/aura/src/mock.rs +++ b/substrate/srml/aura/src/mock.rs @@ -19,9 +19,8 @@ #![cfg(test)] use primitives::{ - KeyTypeId, traits::IdentityLookup, - testing::{UINT_DUMMY_KEY, Header, UintAuthorityId}, + testing::{Header, UintAuthorityId}, }; use srml_support::{impl_outer_origin, parameter_types}; use runtime_io; diff --git a/substrate/srml/council/Cargo.toml b/substrate/srml/collective/Cargo.toml similarity index 89% rename from substrate/srml/council/Cargo.toml rename to substrate/srml/collective/Cargo.toml index 99fe50cf39..cd0bb17871 100644 --- a/substrate/srml/council/Cargo.toml +++ b/substrate/srml/collective/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "srml-council" +name = "srml-collective" version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" @@ -13,7 +13,6 @@ rstd = { package = "sr-std", path = "../../core/sr-std", default-features = fals runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } srml-support = { path = "../support", default-features = false } -democracy = { package = "srml-democracy", path = "../democracy", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } [dev-dependencies] @@ -31,6 +30,5 @@ std = [ "runtime_io/std", "srml-support/std", "primitives/std", - "democracy/std", "system/std", ] diff --git a/substrate/srml/collective/src/lib.rs b/substrate/srml/collective/src/lib.rs new file mode 100644 index 0000000000..153a5df00a --- /dev/null +++ b/substrate/srml/collective/src/lib.rs @@ -0,0 +1,728 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Collective system: Members of a set of account IDs can make their collective feelings known +//! through dispatched calls from one of two specialised origins. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit="128"] + +use rstd::{prelude::*, result}; +use substrate_primitives::u32_trait::Value as U32; +use primitives::traits::{Hash, EnsureOrigin}; +use srml_support::{ + dispatch::{Dispatchable, Parameter}, codec::{Encode, Decode}, traits::ChangeMembers, + StorageValue, StorageMap, decl_module, decl_event, decl_storage, ensure +}; +use system::{self, ensure_signed, ensure_root}; + +/// Simple index type for proposal counting. +pub type ProposalIndex = u32; + +/// A number of members. +/// +/// This also serves as a number of voting members, and since for motions, each member may +/// vote exactly once, therefore also the number of votes for any given motion. +pub type MemberCount = u32; + +pub trait Trait: system::Trait { + /// The outer origin type. + type Origin: From>; + + /// The outer call dispatch type. + type Proposal: Parameter + Dispatchable>::Origin>; + + /// The outer event type. + type Event: From> + Into<::Event>; +} + +/// Origin for the collective module. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum RawOrigin { + /// It has been condoned by a given number of members of the collective from a given total. + Members(MemberCount, MemberCount), + /// It has been condoned by a single member of the collective. + Member(AccountId), + /// Dummy to manage the fact we have instancing. + _Phantom(rstd::marker::PhantomData), +} + +/// Origin for the collective module. +pub type Origin = RawOrigin<::AccountId, I>; + +#[derive(PartialEq, Eq, Clone, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +/// Info for keeping track of a motion being voted on. +pub struct Votes { + /// The proposal's unique index. + index: ProposalIndex, + /// The number of approval votes that are needed to pass the motion. + threshold: MemberCount, + /// The current set of voters that approved it. + ayes: Vec, + /// The current set of voters that rejected it. + nays: Vec, +} + +decl_storage! { + trait Store for Module, I: Instance=DefaultInstance> as Collective { + /// The hashes of the active proposals. + pub Proposals get(proposals): Vec; + /// Actual proposal for a given hash, if it's current. + pub ProposalOf get(proposal_of): map T::Hash => Option<>::Proposal>; + /// Votes on a given proposal, if it is ongoing. + pub Voting get(voting): map T::Hash => Option>; + /// Proposals so far. + pub ProposalCount get(proposal_count): u32; + /// The current members of the collective. This is stored sorted (just by value). + pub Members get(members) config(): Vec; + } + add_extra_genesis { + config(phantom): rstd::marker::PhantomData; + } +} + +decl_event!( + pub enum Event where + ::Hash, + ::AccountId, + Phantom = rstd::marker::PhantomData + { + /// Dummy to manage the fact we have instancing. + _Phantom(Phantom), + /// A motion (given hash) has been proposed (by given account) with a threshold (given + /// `MemberCount`). + Proposed(AccountId, ProposalIndex, Hash, MemberCount), + /// A motion (given hash) has been voted on by given account, leaving + /// a tally (yes votes and no votes given respectively as `MemberCount`). + Voted(AccountId, Hash, bool, MemberCount, MemberCount), + /// A motion was approved by the required threshold. + Approved(Hash), + /// A motion was not approved by the required threshold. + Disapproved(Hash), + /// A motion was executed; `bool` is true if returned without error. + Executed(Hash, bool), + /// A single member did some action; `bool` is true if returned without error. + MemberExecuted(Hash, bool), + } +); + +decl_module! { + pub struct Module, I: Instance=DefaultInstance> for enum Call where origin: ::Origin { + fn deposit_event() = default; + + /// Set the collective's membership manually to `new_members`. Be nice to the chain and + /// provide it pre-sorted. + /// + /// Requires root origin. + fn set_members(origin, new_members: Vec) { + ensure_root(origin)?; + + // stable sorting since they will generally be provided sorted. + let mut old_members = >::get(); + old_members.sort(); + let mut new_members = new_members; + new_members.sort(); + let mut old_iter = old_members.iter(); + let mut new_iter = new_members.iter(); + let mut incoming = vec![]; + let mut outgoing = vec![]; + let mut old_i = old_iter.next(); + let mut new_i = new_iter.next(); + loop { + match (old_i, new_i) { + (None, None) => break, + (Some(old), Some(new)) if old == new => { + old_i = old_iter.next(); + new_i = new_iter.next(); + } + (Some(old), Some(new)) if old < new => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (Some(old), None) => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (_, Some(new)) => { + incoming.push(new.clone()); + new_i = new_iter.next(); + } + } + } + + Self::change_members(&incoming, &outgoing, &new_members); + } + + /// Dispatch a proposal from a member using the `Member` origin. + /// + /// Origin must be a member of the collective. + fn execute(origin, proposal: Box<>::Proposal>) { + let who = ensure_signed(origin)?; + ensure!(Self::is_member(&who), "proposer not a member"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + let ok = proposal.dispatch(RawOrigin::Member(who).into()).is_ok(); + Self::deposit_event(RawEvent::MemberExecuted(proposal_hash, ok)); + } + + /// # + /// - Bounded storage reads and writes. + /// - Argument `threshold` has bearing on weight. + /// # + fn propose(origin, #[compact] threshold: MemberCount, proposal: Box<>::Proposal>) { + let who = ensure_signed(origin)?; + + ensure!(Self::is_member(&who), "proposer not a member"); + + let proposal_hash = T::Hashing::hash_of(&proposal); + + ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); + + if threshold < 2 { + let seats = Self::members().len() as MemberCount; + let ok = proposal.dispatch(RawOrigin::Members(1, seats).into()).is_ok(); + Self::deposit_event(RawEvent::Executed(proposal_hash, ok)); + } else { + let index = Self::proposal_count(); + >::mutate(|i| *i += 1); + >::mutate(|proposals| proposals.push(proposal_hash)); + >::insert(proposal_hash, *proposal); + let votes = Votes { index, threshold, ayes: vec![who.clone()], nays: vec![] }; + >::insert(proposal_hash, votes); + + Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); + } + } + + /// # + /// - Bounded storage read and writes. + /// - Will be slightly heavier if the proposal is approved / disapproved after the vote. + /// # + fn vote(origin, proposal: T::Hash, #[compact] index: ProposalIndex, approve: bool) { + let who = ensure_signed(origin)?; + + ensure!(Self::is_member(&who), "voter not a member"); + + let mut voting = Self::voting(&proposal).ok_or("proposal must exist")?; + ensure!(voting.index == index, "mismatched index"); + + let position_yes = voting.ayes.iter().position(|a| a == &who); + let position_no = voting.nays.iter().position(|a| a == &who); + + if approve { + if position_yes.is_none() { + voting.ayes.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_no { + voting.nays.swap_remove(pos); + } + } else { + if position_no.is_none() { + voting.nays.push(who.clone()); + } else { + return Err("duplicate vote ignored") + } + if let Some(pos) = position_yes { + voting.ayes.swap_remove(pos); + } + } + + let yes_votes = voting.ayes.len() as MemberCount; + let no_votes = voting.nays.len() as MemberCount; + Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); + + let seats = Self::members().len() as MemberCount; + let approved = yes_votes >= voting.threshold; + let disapproved = seats.saturating_sub(no_votes) < voting.threshold; + if approved || disapproved { + if approved { + Self::deposit_event(RawEvent::Approved(proposal)); + + // execute motion, assuming it exists. + if let Some(p) = >::take(&proposal) { + let origin = RawOrigin::Members(voting.threshold, seats).into(); + let ok = p.dispatch(origin).is_ok(); + Self::deposit_event(RawEvent::Executed(proposal, ok)); + } + } else { + // disapproved + Self::deposit_event(RawEvent::Disapproved(proposal)); + } + + // remove vote + >::remove(&proposal); + >::mutate(|proposals| proposals.retain(|h| h != &proposal)); + } else { + // update voting + >::insert(&proposal, voting); + } + } + } +} + +impl, I: Instance> Module { + pub fn is_member(who: &T::AccountId) -> bool { + Self::members().contains(who) + } +} + +impl, I: Instance> ChangeMembers for Module { + fn change_members(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) { + // remove accounts from all current voting in motions. + let mut old = outgoing.to_vec(); + old.sort_unstable(); + for h in Self::proposals().into_iter() { + >::mutate(h, |v| + if let Some(mut votes) = v.take() { + votes.ayes = votes.ayes.into_iter() + .filter(|i| old.binary_search(i).is_err()) + .collect(); + votes.nays = votes.nays.into_iter() + .filter(|i| old.binary_search(i).is_err()) + .collect(); + *v = Some(votes); + } + ); + } + >::put_ref(new); + } +} + +/// Ensure that the origin `o` represents at least `n` members. Returns `Ok` or an `Err` +/// otherwise. +pub fn ensure_members(o: OuterOrigin, n: MemberCount) + -> result::Result +where + OuterOrigin: Into, OuterOrigin>> +{ + match o.into() { + Ok(RawOrigin::Members(x, _)) if x >= n => Ok(n), + _ => Err("bad origin: expected to be a threshold number of members"), + } +} + +pub struct EnsureMember(rstd::marker::PhantomData<(AccountId, I)>); +impl< + O: Into, O>> + From>, + AccountId, + I, +> EnsureOrigin for EnsureMember { + type Success = AccountId; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Member(id) => Ok(id), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureMembers(rstd::marker::PhantomData<(N, AccountId, I)>); +impl< + O: Into, O>> + From>, + N: U32, + AccountId, + I, +> EnsureOrigin for EnsureMembers { + type Success = (MemberCount, MemberCount); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n >= N::VALUE => Ok((n, m)), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureProportionMoreThan( + rstd::marker::PhantomData<(N, D, AccountId, I)> +); +impl< + O: Into, O>> + From>, + N: U32, + D: U32, + AccountId, + I, +> EnsureOrigin for EnsureProportionMoreThan { + type Success = (); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n * D::VALUE > N::VALUE * m => Ok(()), + r => Err(O::from(r)), + }) + } +} + +pub struct EnsureProportionAtLeast( + rstd::marker::PhantomData<(N, D, AccountId, I)> +); +impl< + O: Into, O>> + From>, + N: U32, + D: U32, + AccountId, + I, +> EnsureOrigin for EnsureProportionAtLeast { + type Success = (); + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Members(n, m) if n * D::VALUE >= N::VALUE * m => Ok(()), + r => Err(O::from(r)), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use srml_support::{Hashable, assert_ok, assert_noop, parameter_types}; + use system::{EventRecord, Phase}; + use hex_literal::hex; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::{ + traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, BuildStorage + }; + use crate as collective; + + parameter_types! { + pub const BlockHashCount: u64 = 250; + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + } + impl Trait for Test { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + } + + pub type Block = primitives::generic::Block; + pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic; + + srml_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Event}, + Collective: collective::::{Module, Call, Event, Origin, Config}, + } + ); + + fn make_ext() -> runtime_io::TestExternalities { + GenesisConfig { + collective_Instance1: Some(collective::GenesisConfig { + members: vec![1, 2, 3], + phantom: Default::default(), + }), + }.build_storage().unwrap().0.into() + } + + #[test] + fn motions_basic_environment_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + assert_eq!(Collective::members(), vec![1, 2, 3]); + assert_eq!(Collective::proposals(), Vec::::new()); + }); + } + + fn make_proposal(value: u64) -> Call { + Call::System(system::Call::remark(value.encode())) + } + + #[test] + fn removal_of_old_voters_votes_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) + ); + Collective::change_members(&[4], &[1], &[2, 3, 4]); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) + ); + + let proposal = make_proposal(69); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(3), hash.clone(), 1, false)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) + ); + Collective::change_members(&[], &[3], &[2, 4]); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) + ); + }); + } + + #[test] + fn removal_of_old_voters_votes_works_with_set_members() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) + ); + assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 3, 4])); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) + ); + + let proposal = make_proposal(69); + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(Collective::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(3), hash.clone(), 1, false)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) + ); + assert_ok!(Collective::set_members(Origin::ROOT, vec![2, 4])); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) + ); + }); + } + + #[test] + fn propose_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_eq!(Collective::proposals(), vec![hash]); + assert_eq!(Collective::proposal_of(&hash), Some(proposal)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 3, ayes: vec![1], nays: vec![] }) + ); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 3, + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_ignoring_non_collective_proposals_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + assert_noop!( + Collective::propose(Origin::signed(42), 3, Box::new(proposal.clone())), + "proposer not a member" + ); + }); + } + + #[test] + fn motions_ignoring_non_collective_votes_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_noop!(Collective::vote(Origin::signed(42), hash.clone(), 0, true), "voter not a member"); + }); + } + + #[test] + fn motions_ignoring_bad_index_collective_vote_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(3); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_noop!(Collective::vote(Origin::signed(2), hash.clone(), 1, true), "mismatched index"); + }); + } + + #[test] + fn motions_revoting_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![] }) + ); + assert_noop!(Collective::vote(Origin::signed(1), hash.clone(), 0, true), "duplicate vote ignored"); + assert_ok!(Collective::vote(Origin::signed(1), hash.clone(), 0, false)); + assert_eq!( + Collective::voting(&hash), + Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1] }) + ); + assert_noop!(Collective::vote(Origin::signed(1), hash.clone(), 0, false), "duplicate vote ignored"); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 2, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 1, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + 0, + 1, + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_disapproval_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, false)); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1( + RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 3, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 2, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + 1, + 1, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Disapproved( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + )), + topics: vec![], + } + ]); + }); + } + + #[test] + fn motions_approval_works() { + with_externalities(&mut make_ext(), || { + System::set_block_number(1); + let proposal = make_proposal(42); + let hash: H256 = proposal.blake2_256().into(); + assert_ok!(Collective::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); + assert_ok!(Collective::vote(Origin::signed(2), hash.clone(), 0, true)); + + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Proposed( + 1, + 0, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + 2, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Voted( + 2, + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + true, + 2, + 0, + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Approved( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + )), + topics: vec![], + }, + EventRecord { + phase: Phase::Finalization, + event: Event::collective_Instance1(RawEvent::Executed( + hex!["10b209e55d0f37cd45574674bba42519a29bf0ccf3c85c3c773fcbacab820bb4"].into(), + false, + )), + topics: vec![], + } + ]); + }); + } +} diff --git a/substrate/srml/council/src/motions.rs b/substrate/srml/council/src/motions.rs deleted file mode 100644 index 83486db430..0000000000 --- a/substrate/srml/council/src/motions.rs +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Council voting system. - -use rstd::{prelude::*, result}; -use substrate_primitives::u32_trait::Value as U32; -use primitives::traits::{Hash, EnsureOrigin}; -use srml_support::{ - dispatch::{Dispatchable, Parameter}, codec::{Encode, Decode}, - StorageValue, StorageMap, decl_module, decl_event, decl_storage, ensure -}; -use super::{Trait as CouncilTrait, Module as Council, OnMembersChanged}; -use system::{self, ensure_signed}; - -/// Simple index type for proposal counting. -pub type ProposalIndex = u32; -/// A number of council members. -/// -/// This also serves as a number of voting members, and since for motions, each council member may -/// vote exactly once, therefore also the number of votes for any given motion. -pub type MemberCount = u32; - -pub trait Trait: CouncilTrait { - /// The outer origin type. - type Origin: From>; - - /// The outer call dispatch type. - type Proposal: Parameter + Dispatchable::Origin>; - - /// The outer event type. - type Event: From> + Into<::Event>; -} - -/// Origin for the council module. -#[derive(PartialEq, Eq, Clone)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum RawOrigin { - /// It has been condoned by a given number of council members from a given total. - Members(MemberCount, MemberCount), - /// It has been condoned by a single council member. - Member(AccountId), -} - -/// Origin for the council module. -pub type Origin = RawOrigin<::AccountId>; - -#[derive(PartialEq, Eq, Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -/// Info for keeping track of a motion being voted on. -pub struct Votes { - /// The proposal's unique index. - index: ProposalIndex, - /// The number of approval votes that are needed to pass the motion. - threshold: MemberCount, - /// The current set of voters that approved it. - ayes: Vec, - /// The current set of voters that rejected it. - nays: Vec, -} - -decl_storage! { - trait Store for Module as CouncilMotions { - /// The hashes of the active proposals. - pub Proposals get(proposals): Vec; - /// Actual proposal for a given hash, if it's current. - pub ProposalOf get(proposal_of): map T::Hash => Option<::Proposal>; - /// Votes on a given proposal, if it is ongoing. - pub Voting get(voting): map T::Hash => Option>; - /// Proposals so far. - pub ProposalCount get(proposal_count): u32; - } -} - -decl_event!( - pub enum Event where ::Hash, ::AccountId { - /// A motion (given hash) has been proposed (by given account) with a threshold (given - /// `MemberCount`). - Proposed(AccountId, ProposalIndex, Hash, MemberCount), - /// A motion (given hash) has been voted on by given account, leaving - /// a tally (yes votes and no votes given respectively as `MemberCount`). - Voted(AccountId, Hash, bool, MemberCount, MemberCount), - /// A motion was approved by the required threshold. - Approved(Hash), - /// A motion was not approved by the required threshold. - Disapproved(Hash), - /// A motion was executed; `bool` is true if returned without error. - Executed(Hash, bool), - /// A single councillor did some action; `bool` is true if returned without error. - MemberExecuted(Hash, bool), - } -); - -decl_module! { - pub struct Module for enum Call where origin: ::Origin { - fn deposit_event() = default; - - /// Dispatch a proposal from a councilor using the `Member` origin. - /// - /// Origin must be a council member. - fn execute(origin, proposal: Box<::Proposal>) { - let who = ensure_signed(origin)?; - ensure!(Self::is_councillor(&who), "proposer not on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - let ok = proposal.dispatch(RawOrigin::Member(who).into()).is_ok(); - Self::deposit_event(RawEvent::MemberExecuted(proposal_hash, ok)); - } - - /// # - /// - Bounded storage reads and writes. - /// - Argument `threshold` has bearing on weight. - /// # - fn propose(origin, #[compact] threshold: MemberCount, proposal: Box<::Proposal>) { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "proposer not on council"); - - let proposal_hash = T::Hashing::hash_of(&proposal); - - ensure!(!>::exists(proposal_hash), "duplicate proposals not allowed"); - - if threshold < 2 { - let seats = >::active_council().len() as MemberCount; - let ok = proposal.dispatch(RawOrigin::Members(1, seats).into()).is_ok(); - Self::deposit_event(RawEvent::Executed(proposal_hash, ok)); - } else { - let index = Self::proposal_count(); - ProposalCount::mutate(|i| *i += 1); - >::mutate(|proposals| proposals.push(proposal_hash)); - >::insert(proposal_hash, *proposal); - let votes = Votes { index, threshold, ayes: vec![who.clone()], nays: vec![] }; - >::insert(proposal_hash, votes); - - Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold)); - } - } - - /// # - /// - Bounded storage read and writes. - /// - Will be slightly heavier if the proposal is approved / disapproved after the vote. - /// # - fn vote(origin, proposal: T::Hash, #[compact] index: ProposalIndex, approve: bool) { - let who = ensure_signed(origin)?; - - ensure!(Self::is_councillor(&who), "voter not on council"); - - let mut voting = Self::voting(&proposal).ok_or("proposal must exist")?; - ensure!(voting.index == index, "mismatched index"); - - let position_yes = voting.ayes.iter().position(|a| a == &who); - let position_no = voting.nays.iter().position(|a| a == &who); - - if approve { - if position_yes.is_none() { - voting.ayes.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_no { - voting.nays.swap_remove(pos); - } - } else { - if position_no.is_none() { - voting.nays.push(who.clone()); - } else { - return Err("duplicate vote ignored") - } - if let Some(pos) = position_yes { - voting.ayes.swap_remove(pos); - } - } - - let yes_votes = voting.ayes.len() as MemberCount; - let no_votes = voting.nays.len() as MemberCount; - Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes)); - - let seats = >::active_council().len() as MemberCount; - let approved = yes_votes >= voting.threshold; - let disapproved = seats.saturating_sub(no_votes) < voting.threshold; - if approved || disapproved { - if approved { - Self::deposit_event(RawEvent::Approved(proposal)); - - // execute motion, assuming it exists. - if let Some(p) = >::take(&proposal) { - let origin = RawOrigin::Members(voting.threshold, seats).into(); - let ok = p.dispatch(origin).is_ok(); - Self::deposit_event(RawEvent::Executed(proposal, ok)); - } - } else { - // disapproved - Self::deposit_event(RawEvent::Disapproved(proposal)); - } - - // remove vote - >::remove(&proposal); - >::mutate(|proposals| proposals.retain(|h| h != &proposal)); - } else { - // update voting - >::insert(&proposal, voting); - } - } - } -} - -impl Module { - pub fn is_councillor(who: &T::AccountId) -> bool { - >::active_council().iter() - .any(|&(ref a, _)| a == who) - } -} - -impl OnMembersChanged for Module { - fn on_members_changed(_new: &[T::AccountId], old: &[T::AccountId]) { - // remove accounts from all current voting in motions. - let mut old = old.to_vec(); - old.sort_unstable(); - for h in Self::proposals().into_iter() { - >::mutate(h, |v| - if let Some(mut votes) = v.take() { - votes.ayes = votes.ayes.into_iter() - .filter(|i| old.binary_search(i).is_err()) - .collect(); - votes.nays = votes.nays.into_iter() - .filter(|i| old.binary_search(i).is_err()) - .collect(); - *v = Some(votes); - } - ); - } - } -} - -/// Ensure that the origin `o` represents at least `n` council members. Returns `Ok` or an `Err` -/// otherwise. -pub fn ensure_council_members(o: OuterOrigin, n: MemberCount) - -> result::Result - where OuterOrigin: Into, OuterOrigin>> -{ - match o.into() { - Ok(RawOrigin::Members(x, _)) if x >= n => Ok(n), - _ => Err("bad origin: expected to be a threshold number of council members"), - } -} - -pub struct EnsureMember(::rstd::marker::PhantomData); -impl< - O: Into, O>> + From>, - AccountId -> EnsureOrigin for EnsureMember { - type Success = AccountId; - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Member(id) => Ok(id), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureMembers(::rstd::marker::PhantomData<(N, AccountId)>); -impl< - O: Into, O>> + From>, - N: U32, - AccountId, -> EnsureOrigin for EnsureMembers { - type Success = (MemberCount, MemberCount); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n >= N::VALUE => Ok((n, m)), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureProportionMoreThan( - ::rstd::marker::PhantomData<(N, D, AccountId)> -); -impl< - O: Into, O>> + From>, - N: U32, - D: U32, - AccountId, -> EnsureOrigin for EnsureProportionMoreThan { - type Success = (); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n * D::VALUE > N::VALUE * m => Ok(()), - r => Err(O::from(r)), - }) - } -} - -pub struct EnsureProportionAtLeast( - ::rstd::marker::PhantomData<(N, D, AccountId)> -); -impl< - O: Into, O>> + From>, - N: U32, - D: U32, - AccountId, -> EnsureOrigin for EnsureProportionAtLeast { - type Success = (); - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Members(n, m) if n * D::VALUE >= N::VALUE * m => Ok(()), - r => Err(O::from(r)), - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use super::RawEvent; - use crate::tests::*; - use crate::tests::{Call, Origin, Event as OuterEvent}; - use primitives::traits::BlakeTwo256; - use srml_support::{Hashable, assert_ok, assert_noop}; - use system::{EventRecord, Phase}; - use hex_literal::hex; - - #[test] - fn motions_basic_environment_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - assert_eq!(Balances::free_balance(&42), 0); - assert_eq!(CouncilMotions::proposals(), Vec::::new()); - }); - } - - fn set_balance_proposal(value: u64) -> Call { - Call::Balances(balances::Call::set_balance(42, value.into(), 0)) - } - - #[test] - fn removal_of_old_voters_votes_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, true)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) - ); - CouncilMotions::on_members_changed(&[], &[1]); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) - ); - - let proposal = set_balance_proposal(69); - let hash = BlakeTwo256::hash_of(&proposal); - assert_ok!(CouncilMotions::propose(Origin::signed(2), 2, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(3), hash.clone(), 1, false)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) - ); - CouncilMotions::on_members_changed(&[], &[3]); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) - ); - }); - } - - #[test] - fn propose_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_eq!(CouncilMotions::proposals(), vec![hash]); - assert_eq!(CouncilMotions::proposal_of(&hash), Some(proposal)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 3, ayes: vec![1], nays: vec![] }) - ); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 3, - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_ignoring_non_council_proposals_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - assert_noop!( - CouncilMotions::propose(Origin::signed(42), 3, Box::new(proposal.clone())), - "proposer not on council" - ); - }); - } - - #[test] - fn motions_ignoring_non_council_votes_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(CouncilMotions::vote(Origin::signed(42), hash.clone(), 0, true), "voter not on council"); - }); - } - - #[test] - fn motions_ignoring_bad_index_council_vote_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(3); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_noop!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 1, true), "mismatched index"); - }); - } - - #[test] - fn motions_revoting_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 2, ayes: vec![1], nays: vec![] }) - ); - assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, true), "duplicate vote ignored"); - assert_ok!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false)); - assert_eq!( - CouncilMotions::voting(&hash), - Some(Votes { index: 0, threshold: 2, ayes: vec![], nays: vec![1] }) - ); - assert_noop!(CouncilMotions::vote(Origin::signed(1), hash.clone(), 0, false), "duplicate vote ignored"); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 2, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 1, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - 0, - 1, - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_disapproval_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 3, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, false)); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions( - RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 3, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 2, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - 1, - 1, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Disapproved( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - )), - topics: vec![], - } - ]); - }); - } - - #[test] - fn motions_approval_works() { - with_externalities(&mut ExtBuilder::default().with_council(true).build(), || { - System::set_block_number(1); - let proposal = set_balance_proposal(42); - let hash: H256 = proposal.blake2_256().into(); - assert_ok!(CouncilMotions::propose(Origin::signed(1), 2, Box::new(proposal.clone()))); - assert_ok!(CouncilMotions::vote(Origin::signed(2), hash.clone(), 0, true)); - - assert_eq!(System::events(), vec![ - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed( - 1, - 0, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - 2, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted( - 2, - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - true, - 2, - 0, - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Approved( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - )), - topics: vec![], - }, - EventRecord { - phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Executed( - hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), - false, - )), - topics: vec![], - } - ]); - }); - } -} diff --git a/substrate/srml/democracy/src/lib.rs b/substrate/srml/democracy/src/lib.rs index 16a6d3b5e2..d13feb4db5 100644 --- a/substrate/srml/democracy/src/lib.rs +++ b/substrate/srml/democracy/src/lib.rs @@ -204,6 +204,11 @@ pub trait Trait: system::Trait + Sized { /// a majority-carries referendum. type ExternalMajorityOrigin: EnsureOrigin; + /// Origin from which the next referendum proposed by the external majority may be immediately + /// tabled to vote asynchronously in a similar manner to the emergency origin. It remains a + /// majority-carries vote. + type ExternalPushOrigin: EnsureOrigin; + /// Origin from which emergency referenda may be scheduled. type EmergencyOrigin: EnsureOrigin; @@ -443,12 +448,7 @@ decl_module! { // resubmission in the case of a mistakenly low `vote_period`; better to just let the // referendum take place with the lowest valid value. let period = voting_period.max(T::EmergencyVotingPeriod::get()); - Self::inject_referendum( - now + period, - *proposal, - threshold, - delay, - ).map(|_| ())?; + Self::inject_referendum(now + period, *proposal, threshold, delay).map(|_| ())?; } /// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same @@ -488,6 +488,31 @@ decl_module! { >::put((*proposal, VoteThreshold::SimpleMajority)); } + /// Schedule the currently externally-proposed majority-carries referendum to be tabled + /// immediately. If there is no externally-proposed referendum currently, or if there is one + /// but it is not a majority-carries referendum then it fails. + /// + /// - `proposal_hash`: The hash of the current external proposal. + /// - `voting_period`: The period that is allowed for voting on this proposal. + /// - `delay`: The number of block after voting has ended in approval and this should be + /// enacted. Increased to `EmergencyVotingPeriod` if too low. + fn external_push(origin, + proposal_hash: T::Hash, + voting_period: T::BlockNumber, + delay: T::BlockNumber + ) { + T::ExternalPushOrigin::ensure_origin(origin)?; + let (proposal, threshold) = >::get().ok_or("no proposal made")?; + ensure!(threshold == VoteThreshold::SimpleMajority, "next external proposal not simple majority"); + ensure!(proposal_hash == T::Hashing::hash_of(&proposal), "invalid hash"); + + >::kill(); + let now = >::block_number(); + // We don't consider it an error if `vote_period` is too low, like `emergency_propose`. + let period = voting_period.max(T::EmergencyVotingPeriod::get()); + Self::inject_referendum(now + period, proposal, threshold, delay).map(|_| ())?; + } + /// Veto and blacklist the external proposal hash. fn veto_external(origin, proposal_hash: T::Hash) { let who = T::VetoOrigin::ensure_origin(origin)?; @@ -734,7 +759,7 @@ impl Module { >::insert(proxy, stash) } - /// Start a referendum. Can be called directly by the council. + /// Start a referendum. pub fn internal_start_referendum( proposal: T::Proposal, threshold: VoteThreshold, @@ -748,7 +773,7 @@ impl Module { ) } - /// Remove a referendum. Can be called directly by the council. + /// Remove a referendum. pub fn internal_cancel_referendum(ref_index: ReferendumIndex) { Self::deposit_event(RawEvent::Cancelled(ref_index)); >::clear_referendum(ref_index); @@ -1038,6 +1063,7 @@ mod tests { type EmergencyOrigin = EnsureSignedBy; type ExternalOrigin = EnsureSignedBy; type ExternalMajorityOrigin = EnsureSignedBy; + type ExternalPushOrigin = EnsureSignedBy; type CancellationOrigin = EnsureSignedBy; type VetoOrigin = EnsureSignedBy; type CooloffPeriod = CooloffPeriod; @@ -1403,6 +1429,46 @@ mod tests { }); } + #[test] + fn external_push_referendum_works() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); + assert_noop!(Democracy::external_push(Origin::signed(5), h, 3, 2), "no proposal made"); + assert_ok!(Democracy::external_propose_majority( + Origin::signed(3), + Box::new(set_balance_proposal(2)) + )); + assert_noop!(Democracy::external_push(Origin::signed(1), h, 3, 2), "Invalid origin"); + assert_ok!(Democracy::external_push(Origin::signed(5), h, 0, 0)); + assert_eq!( + Democracy::referendum_info(0), + Some(ReferendumInfo { + end: 1, + proposal: set_balance_proposal(2), + threshold: VoteThreshold::SimpleMajority, + delay: 0, + }) + ); + }); + } + + #[test] + fn external_push_referendum_fails_when_no_simple_majority() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); + assert_ok!(Democracy::external_propose( + Origin::signed(2), + Box::new(set_balance_proposal(2)) + )); + assert_noop!( + Democracy::external_push(Origin::signed(5), h, 3, 2), + "next external proposal not simple majority" + ); + }); + } + #[test] fn locked_for_should_work() { with_externalities(&mut new_test_ext(), || { diff --git a/substrate/srml/elections/Cargo.toml b/substrate/srml/elections/Cargo.toml new file mode 100644 index 0000000000..cd0a43aeb5 --- /dev/null +++ b/substrate/srml/elections/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "srml-elections" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true } +safe-mix = { version = "1.0", default-features = false} +parity-codec = { version = "4.1.1", default-features = false, features = ["derive"] } +substrate-primitives = { path = "../../core/primitives", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } +primitives = { package = "sr-primitives", path = "../../core/sr-primitives", default-features = false } +srml-support = { path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } + +[dev-dependencies] +hex-literal = "0.2.0" +balances = { package = "srml-balances", path = "../balances" } + +[features] +default = ["std"] +std = [ + "safe-mix/std", + "parity-codec/std", + "substrate-primitives/std", + "rstd/std", + "serde", + "runtime_io/std", + "srml-support/std", + "primitives/std", + "system/std", +] diff --git a/substrate/srml/council/src/seats.rs b/substrate/srml/elections/src/lib.rs similarity index 51% rename from substrate/srml/council/src/seats.rs rename to substrate/srml/elections/src/lib.rs index 892fae4627..598d014a50 100644 --- a/substrate/srml/council/src/seats.rs +++ b/substrate/srml/elections/src/lib.rs @@ -14,7 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Council system: Handles the voting in and maintenance of council members. +//! Election module for stake-weighted membership selection of a collective. +//! +//! The composition of a set of account IDs works according to one or more approval votes +//! weighted by stake. There is a partial carry-over facility to give greater weight to those +//! whose voting is serially unsuccessful. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit="128"] use rstd::prelude::*; use primitives::traits::{Zero, One, StaticLookup, Bounded, Saturating}; @@ -24,19 +31,18 @@ use srml_support::{ dispatch::Result, decl_storage, decl_event, ensure, decl_module, traits::{ Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, - OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, + OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, ChangeMembers } }; -use democracy; use parity_codec::{Encode, Decode}; use system::{self, ensure_signed, ensure_root}; -use super::OnMembersChanged; // no polynomial attacks: // // all unbonded public operations should be constant time. // all other public operations must be linear time in terms of prior public operations and: -// - those "valid" ones that cost nothing be limited to a constant number per single protected operation +// - those "valid" ones that cost nothing be limited to a constant number per single protected +// operation // - the rest costing the same order as the computational complexity // all protected operations must complete in at most O(public operations) // @@ -45,12 +51,14 @@ use super::OnMembersChanged; // any storage requirements should be bonded by the same order as the volume. // public operations: -// - express approvals (you pay in a "voter" bond the first time you do this; O(1); one extra DB entry, one DB change) +// - express approvals (you pay in a "voter" bond the first time you do this; O(1); one extra DB +// entry, one DB change) // - remove active voter (you get your "voter" bond back; O(1); one fewer DB entry, one DB change) -// - remove inactive voter (either you or the target is removed; if the target, you get their "voter" bond back; O(1); one fewer DB entry, one DB change) +// - remove inactive voter (either you or the target is removed; if the target, you get their +// "voter" bond back; O(1); one fewer DB entry, one DB change) // - submit candidacy (you pay a "candidate" bond; O(1); one extra DB entry, two DB changes) -// - present winner/runner-up (you may pay a "presentation" bond of O(voters) if the presentation is invalid; O(voters) compute; ) -// protected operations: +// - present winner/runner-up (you may pay a "presentation" bond of O(voters) if the presentation +// is invalid; O(voters) compute; ) protected operations: // - remove candidacy (remove all votes for a candidate) (one fewer DB entry, two DB changes) // to avoid a potentially problematic case of not-enough approvals prior to voting causing a @@ -58,7 +66,7 @@ use super::OnMembersChanged; // to keep the system as stateless as possible (making it a bit easier to reason about), we just // restrict when votes can begin to blocks that lie on boundaries (`voting_period`). -// for an approval vote of C councillors: +// for an approval vote of C members: // top K runners-up are maintained between votes. all others are discarded. // - candidate removed & bond returned when elected. @@ -69,8 +77,8 @@ use super::OnMembersChanged; // for B blocks following, there's a counting period whereby each of the candidates that believe // they fall in the top K+C voted can present themselves. they get the total stake // recorded (based on the snapshot); an ordered list is maintained (the leaderboard). Noone may -// present themselves that, if elected, would result in being included twice on the council -// (important since existing councillors will have their approval votes as it may be that they +// present themselves that, if elected, would result in being included twice in the collective +// (important since existing members will have their approval votes as it may be that they // don't get removed), nor if existing presenters would mean they're not in the top K+C. // following B blocks, the top C candidates are elected and have their bond returned. the top C @@ -86,7 +94,6 @@ use super::OnMembersChanged; // after each vote as all but K entries are cleared. newly registering candidates must use cleared // entries before they increase the capacity. - /// The activity status of a voter. #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, Default)] #[cfg_attr(feature = "std", derive(Debug))] @@ -94,7 +101,8 @@ pub struct VoterInfo { /// Last VoteIndex in which this voter assigned (or initialized) approvals. last_active: VoteIndex, /// Last VoteIndex in which one of this voter's approvals won. - /// Note that `last_win = N` indicates a last win at index `N-1`, hence `last_win = 0` means no win ever. + /// Note that `last_win = N` indicates a last win at index `N-1`, hence `last_win = 0` means no + /// win ever. last_win: VoteIndex, /// The amount of stored weight as a result of not winning but changing approvals. pot: Balance, @@ -115,13 +123,14 @@ pub enum CellStatus { Hole, } -const COUNCIL_SEATS_ID: LockIdentifier = *b"councils"; +const MODULE_ID: LockIdentifier = *b"py/elect"; pub const VOTER_SET_SIZE: usize = 64; pub const APPROVAL_SET_SIZE: usize = 8; -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type NegativeImbalanceOf = + <::Currency as Currency<::AccountId>>::NegativeImbalance; type SetIndex = u32; pub type VoteIndex = u32; @@ -137,12 +146,17 @@ pub const DEFAULT_VOTING_FEE: u32 = 0; pub const DEFAULT_PRESENT_SLASH_PER_VOTER: u32 = 1; pub const DEFAULT_CARRY_COUNT: u32 = 2; pub const DEFAULT_INACTIVE_GRACE_PERIOD: u32 = 1; -pub const DEFAULT_COUNCIL_VOTING_PERIOD: u32 = 1000; +pub const DEFAULT_VOTING_PERIOD: u32 = 1000; pub const DEFAULT_DECAY_RATIO: u32 = 24; -pub trait Trait: democracy::Trait { +pub trait Trait: system::Trait { type Event: From> + Into<::Event>; + /// The currency that people are electing with. + type Currency: + LockableCurrency + + ReservableCurrency; + /// Handler for the unbalanced reduction when slashing a validator. type BadPresentation: OnUnbalanced>; @@ -154,8 +168,9 @@ pub trait Trait: democracy::Trait { /// Handler for the unbalanced reduction when a candidate has lost (and is not a runner up) type LoserCandidate: OnUnbalanced>; + /// What to do when the members change. - type OnMembersChanged: OnMembersChanged; + type ChangeMembers: ChangeMembers; /// How much should be locked up in order to submit one's candidacy. A reasonable /// default value is 9. @@ -183,16 +198,71 @@ pub trait Trait: democracy::Trait { /// How often (in blocks) to check for new votes. A reasonable default value /// is 1000. - type CouncilVotingPeriod: Get; + type VotingPeriod: Get; /// Decay factor of weight when being accumulated. It should typically be set to - /// __at least__ `council_size -1` to keep the council secure. + /// __at least__ `membership_size -1` to keep the collective secure. /// When set to `N`, it indicates `(1/N)^t` of staked is decayed at weight /// increment step `t`. 0 will result in no weight being added at all (normal /// approval voting). A reasonable default value is 24. type DecayRatio: Get; } +decl_storage! { + trait Store for Module as Council { + // ---- parameters + /// How long to give each top candidate to present themselves after the vote ends. + pub PresentationDuration get(presentation_duration) config(): T::BlockNumber; + /// How long each position is active for. + pub TermDuration get(term_duration) config(): T::BlockNumber; + /// Number of accounts that should constitute the collective. + pub DesiredSeats get(desired_seats) config(): u32; + + // ---- permanent state (always relevant, changes only at the finalization of voting) + /// The current membership. When there's a vote going on, this should still be used for executive + /// matters. The block number (second element in the tuple) is the block that their position is + /// active until (calculated by the sum of the block number when the member was elected + /// and their term duration). + pub Members get(members) config(): Vec<(T::AccountId, T::BlockNumber)>; + /// The total number of vote rounds that have happened or are in progress. + pub VoteCount get(vote_index): VoteIndex; + + // ---- persistent state (always relevant, changes constantly) + /// A list of votes for each voter. The votes are stored as numeric values and parsed in a bit-wise manner. + /// + /// In order to get a human-readable representation (`Vec`), use [`all_approvals_of`]. + /// + /// Furthermore, each vector of scalars is chunked with the cap of `APPROVAL_SET_SIZE`. + pub ApprovalsOf get(approvals_of): map (T::AccountId, SetIndex) => Vec; + /// The vote index and list slot that the candidate `who` was registered or `None` if they are not + /// currently registered. + pub RegisterInfoOf get(candidate_reg_info): map T::AccountId => Option<(VoteIndex, u32)>; + /// Basic information about a voter. + pub VoterInfoOf get(voter_info): map T::AccountId => Option>>; + /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). + pub Voters get(voters): map SetIndex => Vec>; + /// the next free set to store a voter in. This will keep growing. + pub NextVoterSet get(next_nonfull_voter_set): SetIndex = 0; + /// Current number of Voters. + pub VoterCount get(voter_count): SetIndex = 0; + /// The present candidate list. + pub Candidates get(candidates): Vec; // has holes + /// Current number of active candidates + pub CandidateCount get(candidate_count): u32; + + // ---- temporary state (only relevant during finalization/presentation) + /// The accounts holding the seats that will become free on the next tally. + pub NextFinalize get(next_finalize): Option<(T::BlockNumber, u32, Vec)>; + /// Get the leaderboard if we're in the presentation phase. The first element is the weight of each entry; + /// It may be the direct summed approval stakes, or a weighted version of it. + pub Leaderboard get(leaderboard): Option, T::AccountId)> >; // ORDERED low -> high + + /// Who is able to vote for whom. Value is the fund-holding account, key is the + /// vote-transaction-sending account. + pub Proxy get(proxy): map T::AccountId => Option; + } +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { /// How much should be locked up in order to submit one's candidacy. A reasonable @@ -221,10 +291,10 @@ decl_module! { /// How often (in blocks) to check for new votes. A reasonable default value /// is 1000. - const CouncilVotingPeriod: T::BlockNumber = T::CouncilVotingPeriod::get(); + const VotingPeriod: T::BlockNumber = T::VotingPeriod::get(); /// Decay factor of weight when being accumulated. It should typically be set to - /// __at least__ `council_size -1` to keep the council secure. + /// __at least__ `membership_size -1` to keep the collective secure. /// When set to `N`, it indicates `(1/N)^t` of staked is decayed at weight /// increment step `t`. 0 will result in no weight being added at all (normal /// approval voting). A reasonable default value is 24. @@ -267,8 +337,12 @@ decl_module! { /// # /// - Same as `set_approvals` with one additional storage read. /// # - fn proxy_set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { - let who = >::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; + fn proxy_set_approvals(origin, + votes: Vec, + #[compact] index: VoteIndex, + hint: SetIndex + ) -> Result { + let who = Self::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; Self::do_set_approvals(who, votes, index, hint) } @@ -331,7 +405,7 @@ decl_module! { ); T::Currency::remove_lock( - COUNCIL_SEATS_ID, + MODULE_ID, if valid { &who } else { &reporter } ); @@ -368,7 +442,7 @@ decl_module! { Self::remove_voter(&who, index); T::Currency::unreserve(&who, T::VotingBond::get()); - T::Currency::remove_lock(COUNCIL_SEATS_ID, &who); + T::Currency::remove_lock(MODULE_ID, &who); } /// Submit oneself for candidacy. @@ -445,7 +519,7 @@ decl_module! { let mut leaderboard = Self::leaderboard().ok_or("leaderboard must exist while present phase active")?; ensure!(total > leaderboard[0].0, "candidate not worthy of leaderboard"); - if let Some(p) = Self::active_council().iter().position(|&(ref c, _)| c == &candidate) { + if let Some(p) = Self::members().iter().position(|&(ref c, _)| c == &candidate) { ensure!(p < expiring.len(), "candidate must not form a duplicated member if elected"); } @@ -492,19 +566,20 @@ decl_module! { DesiredSeats::put(count); } - /// Remove a particular member from the council. This is effective immediately. + /// Remove a particular member from the set. This is effective immediately. /// /// Note: A tally should happen instantly (if not already in a presentation /// period) to fill the seat if removal means that the desired members are not met. fn remove_member(origin, who: ::Source) { ensure_root(origin)?; let who = T::Lookup::lookup(who)?; - let new_council: Vec<(T::AccountId, T::BlockNumber)> = Self::active_council() + let new_set: Vec<(T::AccountId, T::BlockNumber)> = Self::members() .into_iter() .filter(|i| i.0 != who) .collect(); - >::put(new_council); - T::OnMembersChanged::on_members_changed(&[], &[who]); + >::put(&new_set); + let new_set = new_set.into_iter().map(|x| x.0).collect::>(); + T::ChangeMembers::change_members(&[], &[who], &new_set[..]); } /// Set the presentation duration. If there is currently a vote being presented for, will @@ -530,66 +605,15 @@ decl_module! { } } -decl_storage! { - trait Store for Module as Council { - // ---- parameters - /// How long to give each top candidate to present themselves after the vote ends. - pub PresentationDuration get(presentation_duration) config(): T::BlockNumber; - /// How long each position is active for. - pub TermDuration get(term_duration) config(): T::BlockNumber; - /// Number of accounts that should be sitting on the council. - pub DesiredSeats get(desired_seats) config(): u32; - - // ---- permanent state (always relevant, changes only at the finalization of voting) - /// The current council. When there's a vote going on, this should still be used for executive - /// matters. The block number (second element in the tuple) is the block that their position is - /// active until (calculated by the sum of the block number when the council member was elected - /// and their term duration). - pub ActiveCouncil get(active_council) config(): Vec<(T::AccountId, T::BlockNumber)>; - /// The total number of vote rounds that have happened or are in progress. - pub VoteCount get(vote_index): VoteIndex; - - // ---- persistent state (always relevant, changes constantly) - /// A list of votes for each voter. The votes are stored as numeric values and parsed in a bit-wise manner. - /// - /// In order to get a human-readable representation (`Vec`), use [`all_approvals_of`]. - /// - /// Furthermore, each vector of scalars is chunked with the cap of `APPROVAL_SET_SIZE`. - pub ApprovalsOf get(approvals_of): map (T::AccountId, SetIndex) => Vec; - /// The vote index and list slot that the candidate `who` was registered or `None` if they are not - /// currently registered. - pub RegisterInfoOf get(candidate_reg_info): map T::AccountId => Option<(VoteIndex, u32)>; - /// Basic information about a voter. - pub VoterInfoOf get(voter_info): map T::AccountId => Option>>; - /// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]). - pub Voters get(voters): map SetIndex => Vec>; - /// the next free set to store a voter in. This will keep growing. - pub NextVoterSet get(next_nonfull_voter_set): SetIndex = 0; - /// Current number of Voters. - pub VoterCount get(voter_count): SetIndex = 0; - /// The present candidate list. - pub Candidates get(candidates): Vec; // has holes - /// Current number of active candidates - pub CandidateCount get(candidate_count): u32; - - // ---- temporary state (only relevant during finalization/presentation) - /// The accounts holding the seats that will become free on the next tally. - pub NextFinalize get(next_finalize): Option<(T::BlockNumber, u32, Vec)>; - /// Get the leaderboard if we're in the presentation phase. The first element is the weight of each entry; - /// It may be the direct summed approval stakes, or a weighted version of it. - pub Leaderboard get(leaderboard): Option, T::AccountId)> >; // ORDERED low -> high - } -} - decl_event!( pub enum Event where ::AccountId { /// reaped voter, reaper VoterReaped(AccountId, AccountId), /// slashed reaper BadReaperSlashed(AccountId), - /// A tally (for approval votes of council seat(s)) has started. + /// A tally (for approval votes of seat(s)) has started. TallyStarted(u32), - /// A tally (for approval votes of council seat(s)) has ended (with one or more new members). + /// A tally (for approval votes of seat(s)) has ended (with one or more new members). TallyFinalized(Vec, Vec), } ); @@ -607,9 +631,9 @@ impl Module { >::exists(who) } - /// Iff the councillor `who` still has a seat at blocknumber `n` returns `true`. - pub fn will_still_be_councillor_at(who: &T::AccountId, n: T::BlockNumber) -> bool { - Self::active_council().iter() + /// Iff the member `who` still has a seat at blocknumber `n` returns `true`. + pub fn will_still_be_member_at(who: &T::AccountId, n: T::BlockNumber) -> bool { + Self::members().iter() .find(|&&(ref a, _)| a == who) .map(|&(_, expires)| expires > n) .unwrap_or(false) @@ -617,18 +641,18 @@ impl Module { /// Determine the block that a vote can happen on which is no less than `n`. pub fn next_vote_from(n: T::BlockNumber) -> T::BlockNumber { - let voting_period = T::CouncilVotingPeriod::get(); + let voting_period = T::VotingPeriod::get(); (n + voting_period - One::one()) / voting_period * voting_period } /// The block number on which the tally for the next election will happen. `None` only if the - /// desired seats of the council is zero. + /// desired seats of the set is zero. pub fn next_tally() -> Option { let desired_seats = Self::desired_seats(); if desired_seats == 0 { None } else { - let c = Self::active_council(); + let c = Self::members(); let (next_possible, count, coming) = if let Some((tally_end, comers, leavers)) = Self::next_finalize() { // if there's a tally in progress, then next tally can begin immediately afterwards @@ -639,7 +663,7 @@ impl Module { if count < desired_seats as usize { Some(next_possible) } else { - // next tally begins once enough council members expire to bring members below desired. + // next tally begins once enough members expire to bring members below desired. if desired_seats <= coming { // the entire amount of desired seats is less than those new members - we'll have // to wait until they expire. @@ -654,7 +678,7 @@ impl Module { // Private /// Check there's nothing to do this block fn end_block(block_number: T::BlockNumber) -> Result { - if (block_number % T::CouncilVotingPeriod::get()).is_zero() { + if (block_number % T::VotingPeriod::get()).is_zero() { if let Some(number) = Self::next_tally() { if block_number == number { Self::start_tally(); @@ -749,7 +773,7 @@ impl Module { } T::Currency::set_lock( - COUNCIL_SEATS_ID, + MODULE_ID, &who, locked_balance, T::BlockNumber::max_value(), @@ -772,11 +796,11 @@ impl Module { /// Close the voting, record the number of seats that are actually up for grabs. fn start_tally() { - let active_council = Self::active_council(); + let members = Self::members(); let desired_seats = Self::desired_seats() as usize; let number = >::block_number(); - let expiring = active_council.iter().take_while(|i| i.1 <= number).map(|i| i.0.clone()).collect::>(); - let retaining_seats = active_council.len() - expiring.len(); + let expiring = members.iter().take_while(|i| i.1 <= number).map(|i| i.0.clone()).collect::>(); + let retaining_seats = members.len() - expiring.len(); if retaining_seats < desired_seats { let empty_seats = desired_seats - retaining_seats; >::put((number + Self::presentation_duration(), empty_seats as u32, expiring)); @@ -790,7 +814,7 @@ impl Module { } /// Finalize the vote, removing each of the `removals` and inserting `seats` of the most approved - /// candidates in their place. If the total council members is less than the desired membership + /// candidates in their place. If the total number of members is less than the desired membership /// a new vote is started. /// Clears all presented candidates, returning the bond of the elected ones. fn finalize_tally() -> Result { @@ -821,21 +845,22 @@ impl Module { if let Some(activity) = a { activity.last_win = Self::vote_index() + 1; } })); }); - let active_council = Self::active_council(); - let outgoing: Vec<_> = active_council.iter() + let members = Self::members(); + let outgoing: Vec<_> = members.iter() .take(expiring.len()) .map(|a| a.0.clone()).collect(); - // set the new council. - let mut new_council: Vec<_> = active_council + // set the new membership set. + let mut new_set: Vec<_> = members .into_iter() .skip(expiring.len()) .chain(incoming.iter().cloned().map(|a| (a, new_expiry))) .collect(); - new_council.sort_by_key(|&(_, expiry)| expiry); - >::put(new_council); + new_set.sort_by_key(|&(_, expiry)| expiry); + >::put(&new_set); - T::OnMembersChanged::on_members_changed(&incoming, &outgoing); + let new_set = new_set.into_iter().map(|x| x.0).collect::>(); + T::ChangeMembers::change_members(&incoming, &outgoing, &new_set[..]); // clear all except runners-up from candidate list. let candidates = Self::candidates(); @@ -1072,52 +1097,244 @@ impl Module { #[cfg(test)] mod tests { use super::*; - use crate::tests::*; - use srml_support::{assert_ok, assert_noop, assert_err}; + use std::cell::RefCell; + use srml_support::{assert_ok, assert_err, assert_noop, parameter_types}; + use runtime_io::with_externalities; + use substrate_primitives::{H256, Blake2Hasher}; + use primitives::{ + traits::{BlakeTwo256, IdentityLookup, Block as BlockT}, testing::Header, BuildStorage + }; + use crate as elections; + + parameter_types! { + pub const BlockHashCount: u64 = 250; + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + } + parameter_types! { + pub const ExistentialDeposit: u64 = 0; + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 0; + } + impl balances::Trait for Test { + type Balance = u64; + type OnNewAccount = (); + type OnFreeBalanceZero = (); + type Event = Event; + type TransactionPayment = (); + type TransferPayment = (); + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + } + parameter_types! { + pub const CandidacyBond: u64 = 3; + pub const CarryCount: u32 = 2; + pub const InactiveGracePeriod: u32 = 1; + pub const VotingPeriod: u64 = 4; + } + + thread_local! { + static VOTER_BOND: RefCell = RefCell::new(0); + static VOTING_FEE: RefCell = RefCell::new(0); + static PRESENT_SLASH_PER_VOTER: RefCell = RefCell::new(0); + static DECAY_RATIO: RefCell = RefCell::new(0); + static MEMBERS: RefCell> = RefCell::new(vec![]); + } + + pub struct VotingBond; + impl Get for VotingBond { + fn get() -> u64 { VOTER_BOND.with(|v| *v.borrow()) } + } + + pub struct VotingFee; + impl Get for VotingFee { + fn get() -> u64 { VOTING_FEE.with(|v| *v.borrow()) } + } + + pub struct PresentSlashPerVoter; + impl Get for PresentSlashPerVoter { + fn get() -> u64 { PRESENT_SLASH_PER_VOTER.with(|v| *v.borrow()) } + } + + pub struct DecayRatio; + impl Get for DecayRatio { + fn get() -> u32 { DECAY_RATIO.with(|v| *v.borrow()) } + } + + pub struct TestChangeMembers; + impl ChangeMembers for TestChangeMembers { + fn change_members(incoming: &[u64], outgoing: &[u64], new: &[u64]) { + let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec()); + old_plus_incoming.extend_from_slice(incoming); + old_plus_incoming.sort(); + let mut new_plus_outgoing = new.to_vec(); + new_plus_outgoing.extend_from_slice(outgoing); + new_plus_outgoing.sort(); + assert_eq!(old_plus_incoming, new_plus_outgoing); + + MEMBERS.with(|m| *m.borrow_mut() = new.to_vec()); + } + } + + impl Trait for Test { + type Event = Event; + type Currency = Balances; + type BadPresentation = (); + type BadReaper = (); + type BadVoterIndex = (); + type LoserCandidate = (); + type ChangeMembers = TestChangeMembers; + type CandidacyBond = CandidacyBond; + type VotingBond = VotingBond; + type VotingFee = VotingFee; + type PresentSlashPerVoter = PresentSlashPerVoter; + type CarryCount = CarryCount; + type InactiveGracePeriod = InactiveGracePeriod; + type VotingPeriod = VotingPeriod; + type DecayRatio = DecayRatio; + } + + pub type Block = primitives::generic::Block; + pub type UncheckedExtrinsic = primitives::generic::UncheckedMortalCompactExtrinsic; + + srml_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system::{Module, Call, Event}, + Balances: balances::{Module, Call, Event, Config}, + Elections: elections::{Module, Call, Event, Config}, + } + ); + + pub struct ExtBuilder { + balance_factor: u64, + decay_ratio: u32, + voting_fee: u64, + voter_bond: u64, + bad_presentation_punishment: u64, + } + + impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + decay_ratio: 24, + voting_fee: 0, + voter_bond: 0, + bad_presentation_punishment: 1, + } + } + } + + impl ExtBuilder { + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + pub fn decay_ratio(mut self, ratio: u32) -> Self { + self.decay_ratio = ratio; + self + } + pub fn voting_fee(mut self, fee: u64) -> Self { + self.voting_fee = fee; + self + } + pub fn bad_presentation_punishment(mut self, fee: u64) -> Self { + self.bad_presentation_punishment = fee; + self + } + pub fn voter_bond(mut self, fee: u64) -> Self { + self.voter_bond = fee; + self + } + pub fn build(self) -> runtime_io::TestExternalities { + VOTER_BOND.with(|v| *v.borrow_mut() = self.voter_bond); + VOTING_FEE.with(|v| *v.borrow_mut() = self.voting_fee); + PRESENT_SLASH_PER_VOTER.with(|v| *v.borrow_mut() = self.bad_presentation_punishment); + DECAY_RATIO.with(|v| *v.borrow_mut() = self.decay_ratio); + GenesisConfig { + balances: Some(balances::GenesisConfig::{ + balances: vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor) + ], + vesting: vec![], + }), + elections: Some(elections::GenesisConfig::{ + members: vec![], + desired_seats: 2, + presentation_duration: 2, + term_duration: 5, + }), + }.build_storage().unwrap().0.into() + } + } fn voter_ids() -> Vec { - Council::all_voters().iter().map(|v| v.unwrap_or(0) ).collect::>() + Elections::all_voters().iter().map(|v| v.unwrap_or(0) ).collect::>() } fn vote(i: u64, l: usize) { let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, 0)); } fn vote_at(i: u64, l: usize, index: VoteIndex) { let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, index)); + assert_ok!(Elections::set_approvals(Origin::signed(i), (0..l).map(|_| true).collect::>(), 0, index)); } fn create_candidate(i: u64, index: u32) { let _ = Balances::make_free_balance_be(&i, 20); - assert_ok!(Council::submit_candidacy(Origin::signed(i), index)); + assert_ok!(Elections::submit_candidacy(Origin::signed(i), index)); } fn bond() -> u64 { ::VotingBond::get() } - #[test] fn bool_to_flag_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_eq!(Council::bool_to_flag(vec![]), vec![]); - assert_eq!(Council::bool_to_flag(vec![false]), vec![0]); - assert_eq!(Council::bool_to_flag(vec![true]), vec![1]); - assert_eq!(Council::bool_to_flag(vec![true, true, true, true]), vec![15]); - assert_eq!(Council::bool_to_flag(vec![true, true, true, true, true]), vec![15 + 16]); + assert_eq!(Elections::bool_to_flag(vec![]), vec![]); + assert_eq!(Elections::bool_to_flag(vec![false]), vec![0]); + assert_eq!(Elections::bool_to_flag(vec![true]), vec![1]); + assert_eq!(Elections::bool_to_flag(vec![true, true, true, true]), vec![15]); + assert_eq!(Elections::bool_to_flag(vec![true, true, true, true, true]), vec![15 + 16]); let set_1 = vec![ true, false, false, false, // 0x1 false, true, true, true, // 0xE ]; assert_eq!( - Council::bool_to_flag(set_1.clone()), + Elections::bool_to_flag(set_1.clone()), vec![0x00_00_00_E1_u32] ); assert_eq!( - Council::flag_to_bool(vec![0x00_00_00_E1_u32]), + Elections::flag_to_bool(vec![0x00_00_00_E1_u32]), set_1 ); @@ -1126,11 +1343,11 @@ mod tests { false, true, false, true, // 0xA ]; assert_eq!( - Council::bool_to_flag(set_2.clone()), + Elections::bool_to_flag(set_2.clone()), vec![0x00_00_00_A8_u32] ); assert_eq!( - Council::flag_to_bool(vec![0x00_00_00_A8_u32]), + Elections::flag_to_bool(vec![0x00_00_00_A8_u32]), set_2 ); @@ -1138,7 +1355,7 @@ mod tests { // NOTE: this might be need change based on `APPROVAL_FLAG_LEN`. rhs.extend(vec![0x00_00_00_0F]); assert_eq!( - Council::bool_to_flag((0..100).map(|_| true).collect()), + Elections::bool_to_flag((0..100).map(|_| true).collect()), rhs ) }) @@ -1148,51 +1365,51 @@ mod tests { fn params_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::next_vote_from(1), 4); - assert_eq!(Council::next_vote_from(4), 4); - assert_eq!(Council::next_vote_from(5), 8); - assert_eq!(Council::vote_index(), 0); + assert_eq!(Elections::next_vote_from(1), 4); + assert_eq!(Elections::next_vote_from(4), 4); + assert_eq!(Elections::next_vote_from(5), 8); + assert_eq!(Elections::vote_index(), 0); assert_eq!(::CandidacyBond::get(), 3); assert_eq!(::VotingBond::get(), 0); assert_eq!(::VotingFee::get(), 0); assert_eq!(::PresentSlashPerVoter::get(), 1); - assert_eq!(Council::presentation_duration(), 2); + assert_eq!(Elections::presentation_duration(), 2); assert_eq!(::InactiveGracePeriod::get(), 1); - assert_eq!(::CouncilVotingPeriod::get(), 4); - assert_eq!(Council::term_duration(), 5); - assert_eq!(Council::desired_seats(), 2); + assert_eq!(::VotingPeriod::get(), 4); + assert_eq!(Elections::term_duration(), 5); + assert_eq!(Elections::desired_seats(), 2); assert_eq!(::CarryCount::get(), 2); - assert_eq!(Council::active_council(), vec![]); - assert_eq!(Council::next_tally(), Some(4)); - assert_eq!(Council::presentation_active(), false); - assert_eq!(Council::next_finalize(), None); + assert_eq!(Elections::members(), vec![]); + assert_eq!(Elections::next_tally(), Some(4)); + assert_eq!(Elections::presentation_active(), false); + assert_eq!(Elections::next_finalize(), None); - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Council::is_a_candidate(&1), false); - assert_eq!(Council::candidate_reg_info(1), None); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Elections::is_a_candidate(&1), false); + assert_eq!(Elections::candidate_reg_info(1), None); - assert_eq!(Council::voters(0), Vec::>::new()); - assert_eq!(Council::voter_info(1), None); - assert_eq!(Council::all_approvals_of(&1), vec![]); + assert_eq!(Elections::voters(0), Vec::>::new()); + assert_eq!(Elections::voter_info(1), None); + assert_eq!(Elections::all_approvals_of(&1), vec![]); }); } #[test] fn voter_set_growth_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); // create 65. 64 (set0) + 1 (set1) (1..=63).for_each(|i| vote(i, 2)); - assert_eq!(Council::next_nonfull_voter_set(), 0); + assert_eq!(Elections::next_nonfull_voter_set(), 0); vote(64, 2); - assert_eq!(Council::next_nonfull_voter_set(), 1); + assert_eq!(Elections::next_nonfull_voter_set(), 1); vote(65, 2); - let set1 = Council::voters(0); - let set2 = Council::voters(1); + let set1 = Elections::voters(0); + let set2 = Elections::voters(1); assert_eq!(set1.len(), 64); assert_eq!(set2.len(), 1); @@ -1206,38 +1423,38 @@ mod tests { #[test] fn voter_set_reclaim_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); (1..=129).for_each(|i| vote(i, 2)); - assert_eq!(Council::next_nonfull_voter_set(), 2); + assert_eq!(Elections::next_nonfull_voter_set(), 2); - assert_ok!(Council::retract_voter(Origin::signed(11), 10)); + assert_ok!(Elections::retract_voter(Origin::signed(11), 10)); - assert_ok!(Council::retract_voter(Origin::signed(66), 65)); - assert_ok!(Council::retract_voter(Origin::signed(67), 66)); + assert_ok!(Elections::retract_voter(Origin::signed(66), 65)); + assert_ok!(Elections::retract_voter(Origin::signed(67), 66)); // length does not show it but holes do exist. - assert_eq!(Council::voters(0).len(), 64); - assert_eq!(Council::voters(1).len(), 64); - assert_eq!(Council::voters(2).len(), 1); + assert_eq!(Elections::voters(0).len(), 64); + assert_eq!(Elections::voters(1).len(), 64); + assert_eq!(Elections::voters(2).len(), 1); - assert_eq!(Council::voters(0)[10], None); - assert_eq!(Council::voters(1)[1], None); - assert_eq!(Council::voters(1)[2], None); + assert_eq!(Elections::voters(0)[10], None); + assert_eq!(Elections::voters(1)[1], None); + assert_eq!(Elections::voters(1)[2], None); // Next set with capacity is 2. - assert_eq!(Council::next_nonfull_voter_set(), 2); + assert_eq!(Elections::next_nonfull_voter_set(), 2); // But we can fill a hole. vote_at(130, 2, 10); // Nothing added to set 2. A hole was filled. - assert_eq!(Council::voters(0).len(), 64); - assert_eq!(Council::voters(1).len(), 64); - assert_eq!(Council::voters(2).len(), 1); + assert_eq!(Elections::voters(0).len(), 64); + assert_eq!(Elections::voters(1).len(), 64); + assert_eq!(Elections::voters(2).len(), 1); // and the next two (scheduled) to the second set. - assert_eq!(Council::next_nonfull_voter_set(), 2); + assert_eq!(Elections::next_nonfull_voter_set(), 2); }) } @@ -1249,13 +1466,13 @@ mod tests { (1..=250).for_each(|i| vote(i, i as usize)); // all approvals of should return the exact expected vector. - assert_eq!(Council::all_approvals_of(&180), (0..180).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&180), (0..180).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&32), (0..32).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&8), (0..8).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&64), (0..64).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&65), (0..65).map(|_| true).collect::>()); - assert_eq!(Council::all_approvals_of(&63), (0..63).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&32), (0..32).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&8), (0..8).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&64), (0..64).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&65), (0..65).map(|_| true).collect::>()); + assert_eq!(Elections::all_approvals_of(&63), (0..63).map(|_| true).collect::>()); // NOTE: assuming that APPROVAL_SET_SIZE is more or less small-ish. Might fail otherwise. let full_sets = (180 / APPROVAL_FLAG_LEN) / APPROVAL_SET_SIZE; @@ -1265,16 +1482,16 @@ mod tests { // grab and check the last full set, if it exists. if full_sets > 0 { assert_eq!( - Council::approvals_of((180, (full_sets-1) as SetIndex )), - Council::bool_to_flag((0..APPROVAL_SET_SIZE * APPROVAL_FLAG_LEN).map(|_| true).collect::>()) + Elections::approvals_of((180, (full_sets-1) as SetIndex )), + Elections::bool_to_flag((0..APPROVAL_SET_SIZE * APPROVAL_FLAG_LEN).map(|_| true).collect::>()) ); } // grab and check the last, half-empty, set. if left_over > 0 { assert_eq!( - Council::approvals_of((180, full_sets as SetIndex)), - Council::bool_to_flag((0..left_over * APPROVAL_FLAG_LEN + rem).map(|_| true).collect::>()) + Elections::approvals_of((180, full_sets as SetIndex)), + Elections::bool_to_flag((0..left_over * APPROVAL_FLAG_LEN + rem).map(|_| true).collect::>()) ); } }) @@ -1284,32 +1501,32 @@ mod tests { #[test] fn cell_status_works() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); (1..=63).for_each(|i| vote(i, 2)); - assert_ok!(Council::retract_voter(Origin::signed(11), 10)); - assert_ok!(Council::retract_voter(Origin::signed(21), 20)); + assert_ok!(Elections::retract_voter(Origin::signed(11), 10)); + assert_ok!(Elections::retract_voter(Origin::signed(21), 20)); - assert_eq!(Council::cell_status(0, 10), CellStatus::Hole); - assert_eq!(Council::cell_status(0, 0), CellStatus::Occupied); - assert_eq!(Council::cell_status(0, 20), CellStatus::Hole); - assert_eq!(Council::cell_status(0, 63), CellStatus::Head); - assert_eq!(Council::cell_status(1, 0), CellStatus::Head); - assert_eq!(Council::cell_status(1, 10), CellStatus::Head); + assert_eq!(Elections::cell_status(0, 10), CellStatus::Hole); + assert_eq!(Elections::cell_status(0, 0), CellStatus::Occupied); + assert_eq!(Elections::cell_status(0, 20), CellStatus::Hole); + assert_eq!(Elections::cell_status(0, 63), CellStatus::Head); + assert_eq!(Elections::cell_status(1, 0), CellStatus::Head); + assert_eq!(Elections::cell_status(1, 10), CellStatus::Head); }) } #[test] fn initial_set_approvals_ignores_voter_index() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); // Last argument is essentially irrelevant. You might get or miss a tip. - assert_ok!(Council::set_approvals(Origin::signed(3), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 0, 100)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 0, 100)); // indices are more or less ignored. all is pushed. assert_eq!(voter_ids(), vec![3, 4, 5]); @@ -1319,8 +1536,8 @@ mod tests { #[test] fn bad_approval_index_slashes_voters_and_bond_reduces_stake() { with_externalities(&mut ExtBuilder::default().voting_fee(5).voter_bond(2).build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); (1..=63).for_each(|i| vote(i, 2)); assert_eq!(Balances::free_balance(&1), 20 - 5 - 2); // -5 fee -2 bond @@ -1331,17 +1548,17 @@ mod tests { vote(64, 2); assert_eq!(Balances::free_balance(&64), 20 - 2); // -2 bond assert_eq!( - Council::voter_info(&64).unwrap(), + Elections::voter_info(&64).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 20, pot:0 } ); - assert_eq!(Council::next_nonfull_voter_set(), 1); + assert_eq!(Elections::next_nonfull_voter_set(), 1); // now we charge the next voter. vote(65, 2); assert_eq!(Balances::free_balance(&65), 20 - 5 - 2); assert_eq!( - Council::voter_info(&65).unwrap(), + Elections::voter_info(&65).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 15, pot:0 } ); }); @@ -1350,41 +1567,41 @@ mod tests { #[test] fn subsequent_set_approvals_checks_voter_index() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 0, 100)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 0, 100)); // invalid index - assert_noop!(Council::set_approvals(Origin::signed(4), vec![true], 0, 5), "invalid voter index"); + assert_noop!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 5), "invalid voter index"); // wrong index - assert_noop!(Council::set_approvals(Origin::signed(4), vec![true], 0, 0), "wrong voter index"); + assert_noop!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 0), "wrong voter index"); // correct - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1)); }) } #[test] fn voter_index_does_not_take_holes_into_account() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); // create 65. 64 (set0) + 1 (set1) (1..=65).for_each(|i| vote(i, 2)); // account 65 has global index 65. - assert_eq!(Council::voter_at(64).unwrap(), 65); + assert_eq!(Elections::voter_at(64).unwrap(), 65); - assert_ok!(Council::retract_voter(Origin::signed(1), 0)); - assert_ok!(Council::retract_voter(Origin::signed(2), 1)); + assert_ok!(Elections::retract_voter(Origin::signed(1), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(2), 1)); // still the same. These holes are in some other set. - assert_eq!(Council::voter_at(64).unwrap(), 65); + assert_eq!(Elections::voter_at(64).unwrap(), 65); // proof: can submit a new approval with the old index. - assert_noop!(Council::set_approvals(Origin::signed(65), vec![false, true], 0, 64 - 2), "wrong voter index"); - assert_ok!(Council::set_approvals(Origin::signed(65), vec![false, true], 0, 64)); + assert_noop!(Elections::set_approvals(Origin::signed(65), vec![false, true], 0, 64 - 2), "wrong voter index"); + assert_ok!(Elections::set_approvals(Origin::signed(65), vec![false, true], 0, 64)); }) } @@ -1392,25 +1609,25 @@ mod tests { fn simple_candidate_submission_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_eq!(Council::candidate_reg_info(1), None); - assert_eq!(Council::candidate_reg_info(2), None); - assert_eq!(Council::is_a_candidate(&1), false); - assert_eq!(Council::is_a_candidate(&2), false); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_eq!(Elections::candidate_reg_info(1), None); + assert_eq!(Elections::candidate_reg_info(2), None); + assert_eq!(Elections::is_a_candidate(&1), false); + assert_eq!(Elections::is_a_candidate(&2), false); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_eq!(Council::candidate_reg_info(1), Some((0, 0))); - assert_eq!(Council::candidate_reg_info(2), None); - assert_eq!(Council::is_a_candidate(&1), true); - assert_eq!(Council::is_a_candidate(&2), false); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_eq!(Elections::candidate_reg_info(1), Some((0, 0))); + assert_eq!(Elections::candidate_reg_info(2), None); + assert_eq!(Elections::is_a_candidate(&1), true); + assert_eq!(Elections::is_a_candidate(&2), false); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_eq!(Council::candidates(), vec![1, 2]); - assert_eq!(Council::candidate_reg_info(1), Some((0, 0))); - assert_eq!(Council::candidate_reg_info(2), Some((0, 1))); - assert_eq!(Council::is_a_candidate(&1), true); - assert_eq!(Council::is_a_candidate(&2), true); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_eq!(Elections::candidates(), vec![1, 2]); + assert_eq!(Elections::candidate_reg_info(1), Some((0, 0))); + assert_eq!(Elections::candidate_reg_info(2), Some((0, 1))); + assert_eq!(Elections::is_a_candidate(&1), true); + assert_eq!(Elections::is_a_candidate(&2), true); }); } @@ -1430,13 +1647,13 @@ mod tests { with_externalities(&mut t, || { System::set_block_number(1); - assert_eq!(Council::candidates(), vec![0, 0, 1]); + assert_eq!(Elections::candidates(), vec![0, 0, 1]); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_eq!(Council::candidates(), vec![0, 2, 1]); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_eq!(Elections::candidates(), vec![0, 2, 1]); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_eq!(Council::candidates(), vec![3, 2, 1]); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_eq!(Elections::candidates(), vec![3, 2, 1]); }); } @@ -1446,13 +1663,13 @@ mod tests { with_externalities(&mut t, || { System::set_block_number(1); - assert_eq!(Council::candidates(), vec![0, 0, 1]); + assert_eq!(Elections::candidates(), vec![0, 0, 1]); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_eq!(Council::candidates(), vec![2, 0, 1]); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_eq!(Elections::candidates(), vec![2, 0, 1]); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - assert_eq!(Council::candidates(), vec![2, 3, 1]); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + assert_eq!(Elections::candidates(), vec![2, 3, 1]); }); } @@ -1462,7 +1679,7 @@ mod tests { with_externalities(&mut t, || { System::set_block_number(1); - assert_noop!(Council::submit_candidacy(Origin::signed(4), 3), "invalid candidate slot"); + assert_noop!(Elections::submit_candidacy(Origin::signed(4), 3), "invalid candidate slot"); }); } @@ -1470,8 +1687,8 @@ mod tests { fn bad_candidate_slot_submission_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_noop!(Council::submit_candidacy(Origin::signed(1), 1), "invalid candidate slot"); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_noop!(Elections::submit_candidacy(Origin::signed(1), 1), "invalid candidate slot"); }); } @@ -1479,10 +1696,10 @@ mod tests { fn non_free_candidate_slot_submission_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_noop!(Council::submit_candidacy(Origin::signed(2), 0), "invalid candidate slot"); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_noop!(Elections::submit_candidacy(Origin::signed(2), 0), "invalid candidate slot"); }); } @@ -1490,10 +1707,10 @@ mod tests { fn dupe_candidate_submission_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_eq!(Council::candidates(), vec![1]); - assert_noop!(Council::submit_candidacy(Origin::signed(1), 1), "duplicate candidate submission"); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_eq!(Elections::candidates(), vec![1]); + assert_noop!(Elections::submit_candidacy(Origin::signed(1), 1), "duplicate candidate submission"); }); } @@ -1501,8 +1718,8 @@ mod tests { fn poor_candidate_submission_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); - assert_noop!(Council::submit_candidacy(Origin::signed(7), 0), "candidate has not enough funds"); + assert_eq!(Elections::candidates(), Vec::::new()); + assert_noop!(Elections::submit_candidacy(Origin::signed(7), 0), "candidate has not enough funds"); }); } @@ -1510,11 +1727,11 @@ mod tests { fn balance_should_lock_to_the_maximum() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); + assert_eq!(Elections::candidates(), Vec::::new()); assert_eq!(Balances::free_balance(&2), 20); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); assert_eq!(Balances::free_balance(&2), 20 - bond() ); assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. @@ -1523,12 +1740,12 @@ mod tests { let _ = Balances::deposit_creating(&2, 100); assert_ok!(Balances::reserve(&2, 1)); // locked but now has enough. - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); assert_noop!(Balances::reserve(&2, 1), "account liquidity restrictions prevent withdrawal"); // locked. assert_eq!(Balances::locks(&2).len(), 1); assert_eq!(Balances::locks(&2)[0].amount, 100 + 20); - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); assert_eq!(Balances::locks(&2).len(), 0); assert_eq!(Balances::free_balance(&2), 120 - 1); // 1 ok call to .reserve() happened. @@ -1540,16 +1757,16 @@ mod tests { fn balance_should_lock_on_submit_approvals_unlock_on_retract() { with_externalities(&mut ExtBuilder::default().voter_bond(8).voting_fee(0).build(), || { System::set_block_number(1); - assert_eq!(Council::candidates(), Vec::::new()); + assert_eq!(Elections::candidates(), Vec::::new()); assert_eq!(Balances::free_balance(&2), 20); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); assert_eq!(Balances::free_balance(&2), 12); // 20 - 8 (bond) assert_noop!(Balances::reserve(&2, 10), "account liquidity restrictions prevent withdrawal"); // locked. - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); assert_eq!(Balances::free_balance(&2), 20); assert_ok!(Balances::reserve(&2, 10)); // unlocked. @@ -1560,108 +1777,108 @@ mod tests { fn accumulating_weight_and_decaying_should_work() { with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); + assert!(Elections::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); - assert_eq!(Council::voter_info(6).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 600, pot: 0}); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::voter_info(6).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 600, pot: 0}); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 1, last_active: 0, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); System::set_block_number(12); // retract needed to unlock approval funds => submit candidacy again. - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 1), 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 1), 1), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 19), (5, 19)]); + assert_eq!(Elections::members(), vec![(6, 19), (5, 19)]); assert_eq!( - Council::voter_info(6).unwrap(), + Elections::voter_info(6).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 600, pot:0 } ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 500, pot:0 }); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot:0 }); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 2, last_active: 1, stake: 500, pot:0 }); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot:0 }); System::set_block_number(20); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 2, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 2, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 2, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 2, 1)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(22); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 2), 2), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 2), 2), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 27), (5, 27)]); + assert_eq!(Elections::members(), vec![(6, 27), (5, 27)]); assert_eq!( - Council::voter_info(6).unwrap(), + Elections::voter_info(6).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 600, pot: 0} ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 3, last_active: 2, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); System::set_block_number(28); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 3, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 3, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 3, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 3, 1)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(30); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 3), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 3), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100 + Council::get_offset(100, 3), 3), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93 + 90, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 3), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 3), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100 + Elections::get_offset(100, 3), 3), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100 + 96 + 93 + 90, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 35), (5, 35)]); + assert_eq!(Elections::members(), vec![(6, 35), (5, 35)]); assert_eq!( - Council::voter_info(6).unwrap(), + Elections::voter_info(6).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 600, pot: 0} ); - assert_eq!(Council::voter_info(5).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 500, pot: 0}); - assert_eq!(Council::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); + assert_eq!(Elections::voter_info(5).unwrap(), VoterInfo { last_win: 4, last_active: 3, stake: 500, pot: 0}); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { last_win: 0, last_active: 0, stake: 100, pot: 0}); }) } @@ -1669,61 +1886,61 @@ mod tests { fn winning_resets_accumulated_pot() { with_externalities(&mut ExtBuilder::default().balance_factor(10).build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 3)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 3)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, true, false, false], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true, true], 0, 2)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true, true], 0, 2)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(3), 3, 300, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(2), 2, 300, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(300, 2), (300, 3), (400, 4), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(300, 2), (300, 3), (400, 4), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 11), (4, 11)]); + assert_eq!(Elections::members(), vec![(6, 11), (4, 11)]); System::set_block_number(12); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(4), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, true, false, false], 1, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(4), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false, false], 1, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, true, false, false], 1, 1)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(3), 3, 300 + Council::get_offset(300, 1), 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(2), 2, 300 + Council::get_offset(300, 1), 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(400, 4), (588, 2), (588, 3), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(3), 3, 300 + Elections::get_offset(300, 1), 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(2), 2, 300 + Elections::get_offset(300, 1), 1), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(400, 4), (588, 2), (588, 3), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 19), (3, 19)]); + assert_eq!(Elections::members(), vec![(6, 19), (3, 19)]); System::set_block_number(20); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(22); // 2 will not get re-elected with 300 + 288, instead just 300. // because one of 3's candidates (3) won in previous round // 4 on the other hand will get extra weight since it was unlucky. - assert_eq!(Council::present_winner(Origin::signed(3), 2, 300, 2), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 4, 400 + Council::get_offset(400, 1), 2), Ok(())); - assert_ok!(Council::end_block(System::block_number())); + assert_eq!(Elections::present_winner(Origin::signed(3), 2, 300, 2), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 4, 400 + Elections::get_offset(400, 1), 2), Ok(())); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(4, 27), (2, 27)]); + assert_eq!(Elections::members(), vec![(4, 27), (2, 27)]); }) } @@ -1735,96 +1952,96 @@ mod tests { .balance_factor(10) .build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 2)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 0, 2)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); + assert!(Elections::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); - assert_ok!(Council::end_block(System::block_number())); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 100, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (100, 1), (500, 5), (600, 6)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); System::set_block_number(12); - assert_ok!(Council::retract_voter(Origin::signed(6), 0)); - assert_ok!(Council::retract_voter(Origin::signed(5), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(6), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); + assert_ok!(Elections::retract_voter(Origin::signed(6), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(5), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(6), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true, false, false], 1, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true, false], 1, 1)); // give 1 some new high balance let _ = Balances::make_free_balance_be(&1, 997); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2)); - assert_eq!(Council::voter_info(1).unwrap(), + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![false, false, true], 1, 2)); + assert_eq!(Elections::voter_info(1).unwrap(), VoterInfo { stake: 1000, // 997 + 3 which is candidacy bond. - pot: Council::get_offset(100, 1), + pot: Elections::get_offset(100, 1), last_active: 1, last_win: 1, } ); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(6, 11), (5, 11)]); + assert_eq!(Elections::members(), vec![(6, 11), (5, 11)]); System::set_block_number(14); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(6), 6, 600, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(5), 5, 500, 1), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(1), 1, 1000 + 96 /* pot */, 1), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (500, 5), (600, 6), (1096, 1)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(1, 19), (6, 19)]); + assert_eq!(Elections::members(), vec![(1, 19), (6, 19)]); }) } #[test] fn get_offset_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_eq!(Council::get_offset(100, 0), 0); - assert_eq!(Council::get_offset(100, 1), 96); - assert_eq!(Council::get_offset(100, 2), 96 + 93); - assert_eq!(Council::get_offset(100, 3), 96 + 93 + 90); - assert_eq!(Council::get_offset(100, 4), 96 + 93 + 90 + 87); + assert_eq!(Elections::get_offset(100, 0), 0); + assert_eq!(Elections::get_offset(100, 1), 96); + assert_eq!(Elections::get_offset(100, 2), 96 + 93); + assert_eq!(Elections::get_offset(100, 3), 96 + 93 + 90); + assert_eq!(Elections::get_offset(100, 4), 96 + 93 + 90 + 87); // limit - assert_eq!(Council::get_offset(100, 1000), 100 * 24); + assert_eq!(Elections::get_offset(100, 1000), 100 * 24); - assert_eq!(Council::get_offset(50_000_000_000, 0), 0); - assert_eq!(Council::get_offset(50_000_000_000, 1), 48_000_000_000); - assert_eq!(Council::get_offset(50_000_000_000, 2), 48_000_000_000 + 46_080_000_000); - assert_eq!(Council::get_offset(50_000_000_000, 3), 48_000_000_000 + 46_080_000_000 + 44_236_800_000); + assert_eq!(Elections::get_offset(50_000_000_000, 0), 0); + assert_eq!(Elections::get_offset(50_000_000_000, 1), 48_000_000_000); + assert_eq!(Elections::get_offset(50_000_000_000, 2), 48_000_000_000 + 46_080_000_000); + assert_eq!(Elections::get_offset(50_000_000_000, 3), 48_000_000_000 + 46_080_000_000 + 44_236_800_000); assert_eq!( - Council::get_offset(50_000_000_000, 4), + Elections::get_offset(50_000_000_000, 4), 48_000_000_000 + 46_080_000_000 + 44_236_800_000 + 42_467_328_000 ); // limit - assert_eq!(Council::get_offset(50_000_000_000, 1000), 50_000_000_000 * 24); + assert_eq!(Elections::get_offset(50_000_000_000, 1000), 50_000_000_000 * 24); }) } #[test] fn get_offset_with_zero_decay() { with_externalities(&mut ExtBuilder::default().decay_ratio(0).build(), || { - assert_eq!(Council::get_offset(100, 0), 0); - assert_eq!(Council::get_offset(100, 1), 0); - assert_eq!(Council::get_offset(100, 2), 0); - assert_eq!(Council::get_offset(100, 3), 0); + assert_eq!(Elections::get_offset(100, 0), 0); + assert_eq!(Elections::get_offset(100, 1), 0); + assert_eq!(Elections::get_offset(100, 2), 0); + assert_eq!(Elections::get_offset(100, 3), 0); // limit - assert_eq!(Council::get_offset(100, 1000), 0); + assert_eq!(Elections::get_offset(100, 1000), 0); }) } @@ -1833,25 +2050,25 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 1)); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); assert_eq!(voter_ids(), vec![1, 4]); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0, 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0, 3)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true, true], 0, 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, true], 0, 3)); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); assert_eq!(voter_ids(), vec![1, 4, 2, 3]); }); @@ -1862,29 +2079,29 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); - Democracy::force_proxy(1, 11); - Democracy::force_proxy(2, 12); - Democracy::force_proxy(3, 13); - Democracy::force_proxy(4, 14); - assert_ok!(Council::proxy_set_approvals(Origin::signed(11), vec![true], 0, 0)); - assert_ok!(Council::proxy_set_approvals(Origin::signed(14), vec![true], 0, 1)); + >::insert(11, 1); + >::insert(12, 2); + >::insert(13, 3); + >::insert(14, 4); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(11), vec![true], 0, 0)); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(14), vec![true], 0, 1)); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); assert_eq!(voter_ids(), vec![1, 4]); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::proxy_set_approvals(Origin::signed(12), vec![false, true, true], 0, 2)); - assert_ok!(Council::proxy_set_approvals(Origin::signed(13), vec![false, true, true], 0, 3)); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(12), vec![false, true, true], 0, 2)); + assert_ok!(Elections::proxy_set_approvals(Origin::signed(13), vec![false, true, true], 0, 3)); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&4), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); assert_eq!(voter_ids(), vec![1, 4, 2, 3]); }); @@ -1895,10 +2112,10 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_eq!(Council::candidates().len(), 0); + assert_eq!(Elections::candidates().len(), 0); assert_noop!( - Council::set_approvals(Origin::signed(4), vec![], 0, 0), + Elections::set_approvals(Origin::signed(4), vec![], 0, 0), "amount of candidates to receive approval votes should be non-zero" ); }); @@ -1909,11 +2126,11 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_eq!(Council::candidates().len(), 1); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_eq!(Elections::candidates().len(), 1); assert_noop!( - Council::set_approvals(Origin::signed(4),vec![true, true], 0, 0), + Elections::set_approvals(Origin::signed(4),vec![true, true], 0, 0), "amount of candidate votes cannot exceed amount of candidates" ); }); @@ -1924,17 +2141,17 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true], 0, 0)); - assert_eq!(Council::all_approvals_of(&4), vec![true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true]); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_eq!(Council::candidates().len(), 3); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_eq!(Elections::candidates().len(), 3); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true, false, true], 0, 0)); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); }); } @@ -1943,45 +2160,45 @@ mod tests { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_eq!(Council::candidates().len(), 3); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_eq!(Elections::candidates().len(), 3); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true, true], 0, 1)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, true], 0, 2)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![true, false, true], 0, 3)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true, true], 0, 1)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, true], 0, 2)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![true, false, true], 0, 3)); assert_eq!(voter_ids(), vec![1, 2, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), vec![true]); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); + assert_eq!(Elections::all_approvals_of(&1), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); - assert_ok!(Council::retract_voter(Origin::signed(1), 0)); + assert_ok!(Elections::retract_voter(Origin::signed(1), 0)); assert_eq!(voter_ids(), vec![0, 2, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); - assert_ok!(Council::retract_voter(Origin::signed(2), 1)); + assert_ok!(Elections::retract_voter(Origin::signed(2), 1)); assert_eq!(voter_ids(), vec![0, 0, 3, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), Vec::::new()); - assert_eq!(Council::all_approvals_of(&3), vec![false, true, true]); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&3), vec![false, true, true]); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); - assert_ok!(Council::retract_voter(Origin::signed(3), 2)); + assert_ok!(Elections::retract_voter(Origin::signed(3), 2)); assert_eq!(voter_ids(), vec![0, 0, 0, 4]); - assert_eq!(Council::all_approvals_of(&1), Vec::::new()); - assert_eq!(Council::all_approvals_of(&2), Vec::::new()); - assert_eq!(Council::all_approvals_of(&3), Vec::::new()); - assert_eq!(Council::all_approvals_of(&4), vec![true, false, true]); + assert_eq!(Elections::all_approvals_of(&1), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&2), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&3), Vec::::new()); + assert_eq!(Elections::all_approvals_of(&4), vec![true, false, true]); }); } @@ -1989,11 +2206,11 @@ mod tests { fn invalid_retraction_index_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); assert_eq!(voter_ids(), vec![1, 2]); - assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index mismatch"); + assert_noop!(Elections::retract_voter(Origin::signed(1), 1), "retraction index mismatch"); }); } @@ -2001,9 +2218,9 @@ mod tests { fn overflow_retraction_index_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_noop!(Council::retract_voter(Origin::signed(1), 1), "retraction index invalid"); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_noop!(Elections::retract_voter(Origin::signed(1), 1), "retraction index invalid"); }); } @@ -2011,29 +2228,29 @@ mod tests { fn non_voter_retraction_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(1); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 0)); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); - assert_noop!(Council::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); + assert_noop!(Elections::retract_voter(Origin::signed(2), 0), "cannot retract non-voter"); }); } #[test] fn approval_storage_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![], 0, 0)); - assert_eq!(Council::all_approvals_of(&2), vec![true]); + assert_eq!(Elections::all_approvals_of(&2), vec![true]); // NOTE: these two are stored in mem differently though. - assert_eq!(Council::all_approvals_of(&3), vec![]); - assert_eq!(Council::all_approvals_of(&4), vec![]); + assert_eq!(Elections::all_approvals_of(&3), vec![]); + assert_eq!(Elections::all_approvals_of(&4), vec![]); - assert_eq!(Council::approvals_of((3, 0)), vec![0]); - assert_eq!(Council::approvals_of((4, 0)), vec![]); + assert_eq!(Elections::approvals_of((3, 0)), vec![0]); + assert_eq!(Elections::approvals_of((4, 0)), vec![]); }); } @@ -2041,33 +2258,32 @@ mod tests { fn simple_tally_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); assert_eq!(voter_ids(), vec![2, 5]); - assert_eq!(Council::all_approvals_of(&2), vec![true]); - assert_eq!(Council::all_approvals_of(&5), vec![false, true]); - assert_ok!(Council::end_block(System::block_number())); + assert_eq!(Elections::all_approvals_of(&2), vec![true]); + assert_eq!(Elections::all_approvals_of(&5), vec![false, true]); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_ok!(Council::end_block(System::block_number())); + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); - - assert!(!Council::is_a_candidate(&2)); - assert!(!Council::is_a_candidate(&5)); - assert_eq!(Council::vote_index(), 1); - assert_eq!(Council::voter_info(2), Some(VoterInfo { last_win: 1, last_active: 0, stake: 20, pot: 0 })); - assert_eq!(Council::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); + assert!(!Elections::is_a_candidate(&2)); + assert!(!Elections::is_a_candidate(&5)); + assert_eq!(Elections::vote_index(), 1); + assert_eq!(Elections::voter_info(2), Some(VoterInfo { last_win: 1, last_active: 0, stake: 20, pot: 0 })); + assert_eq!(Elections::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); }); } @@ -2075,27 +2291,27 @@ mod tests { fn seats_should_be_released() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); - assert_eq!(Council::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); - assert_eq!(Council::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); - assert_ok!(Council::end_block(System::block_number())); + assert!(Elections::presentation_active()); + assert_eq!(Elections::present_winner(Origin::signed(4), 2, 20, 0), Ok(())); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Ok(())); + assert_eq!(Elections::leaderboard(), Some(vec![(0, 0), (0, 0), (20, 2), (50, 5)])); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); let mut current = System::block_number(); let free_block; loop { current += 1; System::set_block_number(current); - assert_ok!(Council::end_block(System::block_number())); - if Council::active_council().len() == 0 { + assert_ok!(Elections::end_block(System::block_number())); + if Elections::members().len() == 0 { free_block = current; break; } @@ -2109,13 +2325,13 @@ mod tests { fn presentations_with_zero_staked_deposit_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); assert_noop!( - Council::present_winner(Origin::signed(4), 2, 0, 0), + Elections::present_winner(Origin::signed(4), 2, 0, 0), "stake deposited to present winner and be added to leaderboard should be non-zero" ); }); @@ -2127,19 +2343,19 @@ mod tests { assert!(Balances::can_slash(&4, 10)); System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::present_winner(Origin::signed(4), 5, 50, 0), Err("duplicate presentation")); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_eq!(Elections::present_winner(Origin::signed(4), 5, 50, 0), Err("duplicate presentation")); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::active_council(), vec![(5, 11), (2, 11)]); + assert_eq!(Elections::members(), vec![(5, 11), (2, 11)]); assert_eq!(Balances::total_balance(&4), 38); }); } @@ -2148,31 +2364,31 @@ mod tests { fn retracting_inactive_voter_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert_ok!(Council::reap_inactive_voter(Origin::signed(5), + assert_ok!(Elections::reap_inactive_voter(Origin::signed(5), (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), 2 )); assert_eq!(voter_ids(), vec![0, 5]); - assert_eq!(Council::all_approvals_of(&2).len(), 0); + assert_eq!(Elections::all_approvals_of(&2).len(), 0); assert_eq!(Balances::total_balance(&2), 20); assert_eq!(Balances::total_balance(&5), 50); }); @@ -2182,24 +2398,24 @@ mod tests { fn presenting_for_double_election_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(())); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); // NOTE: This is now mandatory to disable the lock - assert_ok!(Council::retract_voter(Origin::signed(2), 0)); - assert_eq!(Council::submit_candidacy(Origin::signed(2), 0), Ok(())); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); + assert_eq!(Elections::submit_candidacy(Origin::signed(2), 0), Ok(())); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); assert_noop!( - Council::present_winner(Origin::signed(4), 2, 20, 1), + Elections::present_winner(Origin::signed(4), 2, 20, 1), "candidate must not form a duplicated member if elected" ); }); @@ -2209,34 +2425,34 @@ mod tests { fn retracting_inactive_voter_with_other_candidates_in_slots_should_work() { with_externalities(&mut ExtBuilder::default().voter_bond(2).build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(11); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::reap_inactive_voter(Origin::signed(5), + assert_ok!(Elections::reap_inactive_voter(Origin::signed(5), (voter_ids().iter().position(|&i| i == 5).unwrap() as u32).into(), 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), 2 )); assert_eq!(voter_ids(), vec![0, 5]); - assert_eq!(Council::all_approvals_of(&2).len(), 0); + assert_eq!(Elections::all_approvals_of(&2).len(), 0); assert_eq!(Balances::total_balance(&2), 18); assert_eq!(Balances::total_balance(&5), 52); }); @@ -2246,24 +2462,24 @@ mod tests { fn retracting_inactive_voter_with_bad_reporter_index_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), + assert_noop!(Elections::reap_inactive_voter(Origin::signed(2), 42, 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), 2 @@ -2275,24 +2491,24 @@ mod tests { fn retracting_inactive_voter_with_bad_target_index_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert_noop!(Council::reap_inactive_voter(Origin::signed(2), + assert_noop!(Elections::reap_inactive_voter(Origin::signed(2), (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), 2, 42, 2 @@ -2304,38 +2520,38 @@ mod tests { fn attempting_to_retract_active_voter_should_slash_reporter() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 1)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 2)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 3)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, true, false, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 1)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 2)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, true, false, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20 + Council::get_offset(20, 1), 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30 + Council::get_offset(30, 1), 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20 + Elections::get_offset(20, 1), 1)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1), 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert_eq!(Council::vote_index(), 2); + assert_eq!(Elections::vote_index(), 2); assert_eq!(::InactiveGracePeriod::get(), 1); - assert_eq!(::CouncilVotingPeriod::get(), 4); - assert_eq!(Council::voter_info(4), Some(VoterInfo { last_win: 1, last_active: 0, stake: 40, pot: 0 })); + assert_eq!(::VotingPeriod::get(), 4); + assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 1, last_active: 0, stake: 40, pot: 0 })); - assert_ok!(Council::reap_inactive_voter(Origin::signed(4), + assert_ok!(Elections::reap_inactive_voter(Origin::signed(4), (voter_ids().iter().position(|&i| i == 4).unwrap() as u32).into(), 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), @@ -2343,7 +2559,7 @@ mod tests { )); assert_eq!(voter_ids(), vec![2, 3, 0, 5]); - assert_eq!(Council::all_approvals_of(&4).len(), 0); + assert_eq!(Elections::all_approvals_of(&4).len(), 0); assert_eq!(Balances::total_balance(&4), 40); }); } @@ -2352,24 +2568,24 @@ mod tests { fn attempting_to_retract_inactive_voter_by_nonvoter_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![true], 1, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![true], 1, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert_noop!(Council::reap_inactive_voter(Origin::signed(4), + assert_noop!(Elections::reap_inactive_voter(Origin::signed(4), 0, 2, (voter_ids().iter().position(|&i| i == 2).unwrap() as u32).into(), 2 @@ -2381,32 +2597,32 @@ mod tests { fn presenting_loser_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::leaderboard(), Some(vec![ + assert_eq!(Elections::leaderboard(), Some(vec![ (30, 3), (40, 4), (50, 5), (60, 1) ])); - assert_noop!(Council::present_winner(Origin::signed(4), 2, 20, 0), "candidate not worthy of leaderboard"); + assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 0), "candidate not worthy of leaderboard"); }); } @@ -2414,26 +2630,26 @@ mod tests { fn presenting_loser_first_should_not_matter() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 2, 20, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 2, 20, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::leaderboard(), Some(vec![ + assert_eq!(Elections::leaderboard(), Some(vec![ (30, 3), (40, 4), (50, 5), @@ -2446,9 +2662,9 @@ mod tests { fn present_outside_of_presentation_period_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); assert_noop!( - Council::present_winner(Origin::signed(5), 5, 1, 0), + Elections::present_winner(Origin::signed(5), 5, 1, 0), "cannot present outside of presentation period" ); }); @@ -2458,14 +2674,14 @@ mod tests { fn present_with_invalid_vote_index_should_not_work() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_noop!(Council::present_winner(Origin::signed(4), 2, 20, 1), "index not current"); + assert_noop!(Elections::present_winner(Origin::signed(4), 2, 20, 1), "index not current"); }); } @@ -2480,23 +2696,23 @@ mod tests { || { System::set_block_number(4); let _ = Balances::make_free_balance_be(&1, 15); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); // -3 + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); // -3 assert_eq!(Balances::free_balance(&1), 12); - assert_ok!(Council::set_approvals(Origin::signed(1), vec![true], 0, 0)); // -2 -5 - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::set_approvals(Origin::signed(1), vec![true], 0, 0)); // -2 -5 + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); assert_eq!(Balances::free_balance(&1), 5); assert_eq!(Balances::reserved_balance(&1), 5); if p > 5 { - assert_noop!(Council::present_winner( + assert_noop!(Elections::present_winner( Origin::signed(1), 1, 10, 0), "presenter must have sufficient slashable funds" ); } else { - assert_ok!(Council::present_winner(Origin::signed(1), 1, 10, 0)); + assert_ok!(Elections::present_winner(Origin::signed(1), 1, 10, 0)); } }); }; @@ -2508,17 +2724,17 @@ mod tests { fn invalid_present_tally_should_slash() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); assert_eq!(Balances::total_balance(&4), 40); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![true, false], 0, 0)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_err!(Council::present_winner(Origin::signed(4), 2, 80, 0), "incorrect total"); + assert_err!(Elections::present_winner(Origin::signed(4), 2, 80, 0), "incorrect total"); assert_eq!(Balances::total_balance(&4), 38); }); @@ -2528,60 +2744,60 @@ mod tests { fn runners_up_should_be_kept() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert!(!Council::presentation_active()); + assert!(!Elections::presentation_active()); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert!(Council::presentation_active()); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); + assert!(Elections::presentation_active()); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); // leaderboard length is the empty seats plus the carry count (i.e. 5 + 2), where those // to be carried are the lowest and stored in lowest indices - assert_eq!(Council::leaderboard(), Some(vec![ + assert_eq!(Elections::leaderboard(), Some(vec![ (0, 0), (0, 0), (0, 0), (60, 1) ])); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_eq!(Council::leaderboard(), Some(vec![ + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_eq!(Elections::leaderboard(), Some(vec![ (30, 3), (40, 4), (50, 5), (60, 1) ])); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::end_block(System::block_number())); - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(1, 11), (5, 11)]); + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(1, 11), (5, 11)]); - assert!(!Council::is_a_candidate(&1)); - assert!(!Council::is_a_candidate(&5)); - assert!(!Council::is_a_candidate(&2)); - assert!(Council::is_a_candidate(&3)); - assert!(Council::is_a_candidate(&4)); - assert_eq!(Council::vote_index(), 1); - assert_eq!(Council::voter_info(2), Some(VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0 })); - assert_eq!(Council::voter_info(3), Some(VoterInfo { last_win: 0, last_active: 0, stake: 30, pot: 0 })); - assert_eq!(Council::voter_info(4), Some(VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0 })); - assert_eq!(Council::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); - assert_eq!(Council::voter_info(6), Some(VoterInfo { last_win: 1, last_active: 0, stake: 60, pot: 0 })); - assert_eq!(Council::candidate_reg_info(3), Some((0, 2))); - assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); + assert!(!Elections::is_a_candidate(&1)); + assert!(!Elections::is_a_candidate(&5)); + assert!(!Elections::is_a_candidate(&2)); + assert!(Elections::is_a_candidate(&3)); + assert!(Elections::is_a_candidate(&4)); + assert_eq!(Elections::vote_index(), 1); + assert_eq!(Elections::voter_info(2), Some(VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0 })); + assert_eq!(Elections::voter_info(3), Some(VoterInfo { last_win: 0, last_active: 0, stake: 30, pot: 0 })); + assert_eq!(Elections::voter_info(4), Some(VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0 })); + assert_eq!(Elections::voter_info(5), Some(VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0 })); + assert_eq!(Elections::voter_info(6), Some(VoterInfo { last_win: 1, last_active: 0, stake: 60, pot: 0 })); + assert_eq!(Elections::candidate_reg_info(3), Some((0, 2))); + assert_eq!(Elections::candidate_reg_info(4), Some((0, 3))); }); } @@ -2589,54 +2805,54 @@ mod tests { fn second_tally_should_use_runners_up() { with_externalities(&mut ExtBuilder::default().build(), || { System::set_block_number(4); - assert_ok!(Council::submit_candidacy(Origin::signed(1), 0)); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(2), 1)); - assert_ok!(Council::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(3), 2)); - assert_ok!(Council::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(4), 3)); - assert_ok!(Council::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); - assert_ok!(Council::submit_candidacy(Origin::signed(5), 4)); - assert_ok!(Council::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::submit_candidacy(Origin::signed(1), 0)); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(2), 1)); + assert_ok!(Elections::set_approvals(Origin::signed(2), vec![false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(3), 2)); + assert_ok!(Elections::set_approvals(Origin::signed(3), vec![false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(4), 3)); + assert_ok!(Elections::set_approvals(Origin::signed(4), vec![false, false, false, true], 0, 0)); + assert_ok!(Elections::submit_candidacy(Origin::signed(5), 4)); + assert_ok!(Elections::set_approvals(Origin::signed(5), vec![false, false, false, false, true], 0, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(6); - assert_ok!(Council::present_winner(Origin::signed(4), 1, 60, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40, 0)); - assert_ok!(Council::present_winner(Origin::signed(4), 5, 50, 0)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 1, 60, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40, 0)); + assert_ok!(Elections::present_winner(Origin::signed(4), 5, 50, 0)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(8); - assert_ok!(Council::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0)); - assert_ok!(Council::set_desired_seats(Origin::ROOT, 3)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::set_approvals(Origin::signed(6), vec![false, false, true, false], 1, 0)); + assert_ok!(Elections::set_desired_seats(Origin::ROOT, 3)); + assert_ok!(Elections::end_block(System::block_number())); System::set_block_number(10); - assert_ok!(Council::present_winner(Origin::signed(4), 3, 30 + Council::get_offset(30, 1) + 60, 1)); - assert_ok!(Council::present_winner(Origin::signed(4), 4, 40 + Council::get_offset(40, 1), 1)); - assert_ok!(Council::end_block(System::block_number())); + assert_ok!(Elections::present_winner(Origin::signed(4), 3, 30 + Elections::get_offset(30, 1) + 60, 1)); + assert_ok!(Elections::present_winner(Origin::signed(4), 4, 40 + Elections::get_offset(40, 1), 1)); + assert_ok!(Elections::end_block(System::block_number())); - assert!(!Council::presentation_active()); - assert_eq!(Council::active_council(), vec![(1, 11), (5, 11), (3, 15)]); + assert!(!Elections::presentation_active()); + assert_eq!(Elections::members(), vec![(1, 11), (5, 11), (3, 15)]); - assert!(!Council::is_a_candidate(&1)); - assert!(!Council::is_a_candidate(&2)); - assert!(!Council::is_a_candidate(&3)); - assert!(!Council::is_a_candidate(&5)); - assert!(Council::is_a_candidate(&4)); - assert_eq!(Council::vote_index(), 2); - assert_eq!(Council::voter_info(2), Some( VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0})); - assert_eq!(Council::voter_info(3), Some( VoterInfo { last_win: 2, last_active: 0, stake: 30, pot: 0})); - assert_eq!(Council::voter_info(4), Some( VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0})); - assert_eq!(Council::voter_info(5), Some( VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0})); + assert!(!Elections::is_a_candidate(&1)); + assert!(!Elections::is_a_candidate(&2)); + assert!(!Elections::is_a_candidate(&3)); + assert!(!Elections::is_a_candidate(&5)); + assert!(Elections::is_a_candidate(&4)); + assert_eq!(Elections::vote_index(), 2); + assert_eq!(Elections::voter_info(2), Some( VoterInfo { last_win: 0, last_active: 0, stake: 20, pot: 0})); + assert_eq!(Elections::voter_info(3), Some( VoterInfo { last_win: 2, last_active: 0, stake: 30, pot: 0})); + assert_eq!(Elections::voter_info(4), Some( VoterInfo { last_win: 0, last_active: 0, stake: 40, pot: 0})); + assert_eq!(Elections::voter_info(5), Some( VoterInfo { last_win: 1, last_active: 0, stake: 50, pot: 0})); assert_eq!( - Council::voter_info(6), + Elections::voter_info(6), Some(VoterInfo { last_win: 2, last_active: 1, stake: 60, pot: 0}) ); - assert_eq!(Council::candidate_reg_info(4), Some((0, 3))); + assert_eq!(Elections::candidate_reg_info(4), Some((0, 3))); }); } } diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index 71164c0a9c..8f3044daa8 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -648,7 +648,7 @@ decl_module! { fn deposit_event() = default; /// Take the origin account as a stash and lock up `value` of its balance. `controller` will - /// be the account that controls it. + /// be the account that controls it. /// /// The dispatch origin for this call must be _Signed_ by the stash account. /// @@ -693,7 +693,7 @@ decl_module! { } /// Add some extra amount that have appeared in the stash `free_balance` into the balance up - /// for staking. + /// for staking. /// /// Use this if there are additional funds in your stash account that you wish to bond. /// diff --git a/substrate/srml/staking/src/mock.rs b/substrate/srml/staking/src/mock.rs index c86d647646..abad1752a6 100644 --- a/substrate/srml/staking/src/mock.rs +++ b/substrate/srml/staking/src/mock.rs @@ -52,10 +52,9 @@ thread_local! { pub struct TestSessionHandler; impl session::SessionHandler for TestSessionHandler { fn on_new_session(_changed: bool, validators: &[(AccountId, Ks)]) { - SESSION.with(|x| { - let v = validators.iter().map(|(ref a, _)| a).cloned().collect::>(); + SESSION.with(|x| *x.borrow_mut() = (validators.iter().map(|x| x.0.clone()).collect(), HashSet::new()) - }); + ); } fn on_disabled(validator_index: usize) { diff --git a/substrate/srml/support/src/dispatch.rs b/substrate/srml/support/src/dispatch.rs index 8f3a8224db..f990cbd8d5 100644 --- a/substrate/srml/support/src/dispatch.rs +++ b/substrate/srml/support/src/dispatch.rs @@ -870,10 +870,12 @@ macro_rules! decl_module { ) { $( $impl:tt )* } ) => { $(#[doc = $doc_attr])* + #[allow(unreachable_code)] $vis fn $name( $origin: $origin_ty $(, $param: $param_ty )* ) -> $crate::dispatch::Result { { $( $impl )* } + // May be unreachable. Ok(()) } }; @@ -1285,9 +1287,11 @@ macro_rules! impl_outer_dispatch { } $( impl $crate::dispatch::IsSubType<$camelcase, $runtime> for $call_type { + #[allow(unreachable_patterns)] fn is_aux_sub_type(&self) -> Option<&$crate::dispatch::CallableCallFor<$camelcase, $runtime>> { match *self { $call_type::$camelcase(ref r) => Some(r), + // May be unreachable _ => None, } } diff --git a/substrate/srml/support/src/storage/hashed/generator.rs b/substrate/srml/support/src/storage/hashed/generator.rs index 5a8b2f9d8f..cff375e1f6 100644 --- a/substrate/srml/support/src/storage/hashed/generator.rs +++ b/substrate/srml/support/src/storage/hashed/generator.rs @@ -16,7 +16,7 @@ //! Abstract storage to use on HashedStorage trait -use crate::codec; +use crate::codec::{self, Encode}; use crate::rstd::prelude::{Vec, Box}; #[cfg(feature = "std")] use crate::storage::unhashed::generator::UnhashedStorage; @@ -184,6 +184,13 @@ pub trait StorageValue { storage.put(Self::key(), val) } + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + /// Store a value under this key into the provided storage instance. + fn put_ref>(val: &Arg, storage: &mut S) where T: AsRef { + val.using_encoded(|b| storage.put_raw(Self::key(), b)) + } + /// Mutate this value fn mutate R, S: HashedStorage>(f: F, storage: &mut S) -> R; @@ -236,6 +243,17 @@ pub trait StorageMap { storage.put(&Self::key_for(key)[..], val); } + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + /// Store a value under this key into the provided storage instance. + fn insert_ref>( + key: &K, + val: &Arg, + storage: &mut S + ) where V: AsRef { + val.using_encoded(|b| storage.put_raw(&Self::key_for(key)[..], b)) + } + /// Remove the value under a key. fn remove>(key: &K, storage: &mut S) { storage.kill(&Self::key_for(key)[..]); diff --git a/substrate/srml/support/src/storage/mod.rs b/substrate/srml/support/src/storage/mod.rs index a1891dade3..db9e4bc31d 100644 --- a/substrate/srml/support/src/storage/mod.rs +++ b/substrate/srml/support/src/storage/mod.rs @@ -149,6 +149,10 @@ pub trait StorageValue { /// Store a value under this key into the provided storage instance. fn put>(val: Arg); + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + fn put_ref(val: &Arg) where T: AsRef; + /// Mutate the value fn mutate R>(f: F) -> R; @@ -180,6 +184,9 @@ impl StorageValue for U where U: hashed::generator::StorageValue fn put>(val: Arg) { U::put(val.borrow(), &mut RuntimeStorage) } + fn put_ref(val: &Arg) where T: AsRef { + U::put_ref(val, &mut RuntimeStorage) + } fn mutate R>(f: F) -> R { U::mutate(f, &mut RuntimeStorage) } @@ -216,6 +223,10 @@ pub trait StorageMap { /// Store a value to be associated with the given key from the map. fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg); + /// Store a value under this key into the provided storage instance; this can take any reference + /// type that derefs to `T` (and has `Encode` implemented). + fn insert_ref, ValArg: ?Sized + Encode>(key: KeyArg, val: &ValArg) where V: AsRef; + /// Remove the value under a key. fn remove>(key: KeyArg); @@ -249,6 +260,10 @@ impl StorageMap for U where U: hashed::generator::S U::insert(key.borrow(), val.borrow(), &mut RuntimeStorage) } + fn insert_ref, ValArg: ?Sized + Encode>(key: KeyArg, val: &ValArg) where V: AsRef { + U::insert_ref(key.borrow(), val, &mut RuntimeStorage) + } + fn remove>(key: KeyArg) { U::remove(key.borrow(), &mut RuntimeStorage) } diff --git a/substrate/srml/support/src/traits.rs b/substrate/srml/support/src/traits.rs index ae33aeff46..ed76338391 100644 --- a/substrate/srml/support/src/traits.rs +++ b/substrate/srml/support/src/traits.rs @@ -630,3 +630,13 @@ bitmask! { } } +/// Trait for type that can handle incremental changes to a set of account IDs. +pub trait ChangeMembers { + /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The + /// new set is thus given by `_new`. + fn change_members(_incoming: &[AccountId], _outgoing: &[AccountId], _new: &[AccountId]); +} + +impl ChangeMembers for () { + fn change_members(_incoming: &[T], _outgoing: &[T], _new_set: &[T]) {} +}