Migrate pallet-elections to pallet attribute macro (#9088)

* Migrate elections pallet to pallet attribute macro.

* Metadata fix.

* Update frame/elections/src/lib.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
This commit is contained in:
Shaun Wang
2021-06-17 02:17:57 +12:00
committed by GitHub
parent ae5cd339b5
commit b21c49524f
2 changed files with 312 additions and 235 deletions
+310 -233
View File
@@ -22,7 +22,7 @@
//!
//! ---
//!
//! Election module for stake-weighted membership selection of a collective.
//! Election pallet 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
@@ -33,19 +33,20 @@
use sp_std::prelude::*;
use sp_runtime::{
RuntimeDebug, DispatchResult, print,
RuntimeDebug, print,
traits::{Zero, One, StaticLookup, Saturating},
};
use frame_support::{
decl_storage, decl_event, ensure, decl_module, decl_error,
pallet_prelude::*, ensure,
weights::{Weight, DispatchClass},
traits::{
Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, BalanceStatus,
Currency, ExistenceRequirement, LockableCurrency, LockIdentifier, BalanceStatus,
OnUnbalanced, ReservableCurrency, WithdrawReasons, ChangeMembers,
}
};
use codec::{Encode, Decode};
use frame_system::{ensure_signed, ensure_root};
use frame_system::pallet_prelude::*;
pub use pallet::*;
mod mock;
mod tests;
@@ -152,141 +153,250 @@ type ApprovalFlag = u32;
/// Number of approval flags that can fit into [`ApprovalFlag`] type.
const APPROVAL_FLAG_LEN: usize = 32;
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
#[frame_support::pallet]
pub mod pallet {
use super::*;
/// Identifier for the elections pallet's lock
type PalletId: Get<LockIdentifier>;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
/// The currency that people are electing with.
type Currency:
LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>
+ ReservableCurrency<Self::AccountId>;
#[pallet::config]
pub trait Config: frame_system::Config {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Handler for the unbalanced reduction when slashing a validator.
type BadPresentation: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Identifier for the elections pallet's lock
#[pallet::constant]
type PalletId: Get<LockIdentifier>;
/// Handler for the unbalanced reduction when slashing an invalid reaping attempt.
type BadReaper: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// The currency that people are electing with.
type Currency:
LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>
+ ReservableCurrency<Self::AccountId>;
/// Handler for the unbalanced reduction when submitting a bad `voter_index`.
type BadVoterIndex: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Handler for the unbalanced reduction when slashing a validator.
type BadPresentation: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Handler for the unbalanced reduction when a candidate has lost (and is not a runner up)
type LoserCandidate: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Handler for the unbalanced reduction when slashing an invalid reaping attempt.
type BadReaper: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// What to do when the members change.
type ChangeMembers: ChangeMembers<Self::AccountId>;
/// Handler for the unbalanced reduction when submitting a bad `voter_index`.
type BadVoterIndex: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// How much should be locked up in order to submit one's candidacy. A reasonable
/// default value is 9.
type CandidacyBond: Get<BalanceOf<Self>>;
/// Handler for the unbalanced reduction when a candidate has lost (and is not a runner up)
type LoserCandidate: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// How much should be locked up in order to be able to submit votes.
type VotingBond: Get<BalanceOf<Self>>;
/// What to do when the members change.
type ChangeMembers: ChangeMembers<Self::AccountId>;
/// The amount of fee paid upon each vote submission, unless if they submit a
/// _hole_ index and replace it.
type VotingFee: Get<BalanceOf<Self>>;
/// How much should be locked up in order to submit one's candidacy. A reasonable
/// default value is 9.
#[pallet::constant]
type CandidacyBond: Get<BalanceOf<Self>>;
/// Minimum about that can be used as the locked value for voting.
type MinimumVotingLock: Get<BalanceOf<Self>>;
/// How much should be locked up in order to be able to submit votes.
#[pallet::constant]
type VotingBond: Get<BalanceOf<Self>>;
/// The punishment, per voter, if you provide an invalid presentation. A
/// reasonable default value is 1.
type PresentSlashPerVoter: Get<BalanceOf<Self>>;
/// The amount of fee paid upon each vote submission, unless if they submit a
/// _hole_ index and replace it.
#[pallet::constant]
type VotingFee: Get<BalanceOf<Self>>;
/// How many runners-up should have their approvals persist until the next
/// vote. A reasonable default value is 2.
type CarryCount: Get<u32>;
/// Minimum about that can be used as the locked value for voting.
#[pallet::constant]
type MinimumVotingLock: Get<BalanceOf<Self>>;
/// How many vote indices need to go by after a target voter's last vote before
/// they can be reaped if their approvals are moot. A reasonable default value
/// is 1.
type InactiveGracePeriod: Get<VoteIndex>;
/// The punishment, per voter, if you provide an invalid presentation. A
/// reasonable default value is 1.
#[pallet::constant]
type PresentSlashPerVoter: Get<BalanceOf<Self>>;
/// How often (in blocks) to check for new votes. A reasonable default value
/// is 1000.
type VotingPeriod: Get<Self::BlockNumber>;
/// How many runners-up should have their approvals persist until the next
/// vote. A reasonable default value is 2.
#[pallet::constant]
type CarryCount: Get<u32>;
/// Decay factor of weight when being accumulated. It should typically be set to
/// __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<u32>;
}
/// How many vote indices need to go by after a target voter's last vote before
/// they can be reaped if their approvals are moot. A reasonable default value
/// is 1.
#[pallet::constant]
type InactiveGracePeriod: Get<VoteIndex>;
decl_storage! {
trait Store for Module<T: Config> as Elections {
// ---- parameters
/// How often (in blocks) to check for new votes. A reasonable default value
/// is 1000.
#[pallet::constant]
type VotingPeriod: Get<Self::BlockNumber>;
/// How long to give each top candidate to present themselves after the vote ends.
pub PresentationDuration get(fn presentation_duration) config(): T::BlockNumber;
/// How long each position is active for.
pub TermDuration get(fn term_duration) config(): T::BlockNumber;
/// Number of accounts that should constitute the collective.
pub DesiredSeats get(fn 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(fn members) config(): Vec<(T::AccountId, T::BlockNumber)>;
/// The total number of vote rounds that have happened or are in progress.
pub VoteCount get(fn 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<bool>`), use
// [`all_approvals_of`]. Furthermore, each vector of scalars is chunked with the cap of
// `APPROVAL_SET_SIZE`.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash and `SetIndex` is not
/// attacker-controlled.
pub ApprovalsOf get(fn approvals_of):
map hasher(twox_64_concat) (T::AccountId, SetIndex) => Vec<ApprovalFlag>;
/// The vote index and list slot that the candidate `who` was registered or `None` if they
/// are not currently registered.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash.
pub RegisterInfoOf get(fn candidate_reg_info):
map hasher(twox_64_concat) T::AccountId => Option<(VoteIndex, u32)>;
/// Basic information about a voter.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash.
pub VoterInfoOf get(fn voter_info):
map hasher(twox_64_concat) T::AccountId => Option<VoterInfo<BalanceOf<T>>>;
/// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]).
///
/// TWOX-NOTE: OKAY ― `SetIndex` is not user-controlled data.
pub Voters get(fn voters): map hasher(twox_64_concat) SetIndex => Vec<Option<T::AccountId>>;
/// the next free set to store a voter in. This will keep growing.
pub NextVoterSet get(fn next_nonfull_voter_set): SetIndex = 0;
/// Current number of Voters.
pub VoterCount get(fn voter_count): SetIndex = 0;
/// The present candidate list.
pub Candidates get(fn candidates): Vec<T::AccountId>; // has holes
/// Current number of active candidates
pub CandidateCount get(fn 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(fn next_finalize): Option<(T::BlockNumber, u32, Vec<T::AccountId>)>;
/// 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.
/// Sorted from low to high.
pub Leaderboard get(fn leaderboard): Option<Vec<(BalanceOf<T>, T::AccountId)> >;
/// Decay factor of weight when being accumulated. It should typically be set to
/// __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.
#[pallet::constant]
type DecayRatio: Get<u32>;
}
}
decl_error! {
/// Error for the elections module.
pub enum Error for Module<T: Config> {
#[pallet::extra_constants]
impl<T: Config> Pallet<T> {
//TODO: rename to snake case after https://github.com/paritytech/substrate/issues/8826 fixed.
/// The chunk size of the voter vector.
#[allow(non_snake_case)]
fn VOTER_SET_SIZE() -> u32 {
VOTER_SET_SIZE as u32
}
//TODO: rename to snake case after https://github.com/paritytech/substrate/issues/8826 fixed.
/// The chunk size of the approval vector.
#[allow(non_snake_case)]
fn APPROVAL_SET_SIZE() -> u32 {
APPROVAL_SET_SIZE as u32
}
}
// ---- permanent state (always relevant, changes only at the finalization of voting)
/// How long to give each top candidate to present themselves after the vote ends.
#[pallet::storage]
#[pallet::getter(fn presentation_duration)]
pub type PresentationDuration<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
/// How long each position is active for.
#[pallet::storage]
#[pallet::getter(fn term_duration)]
pub type TermDuration<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
/// Number of accounts that should constitute the collective.
#[pallet::storage]
#[pallet::getter(fn desired_seats)]
pub type DesiredSeats<T> = StorageValue<_, u32, ValueQuery>;
// ---- 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).
#[pallet::storage]
#[pallet::getter(fn members)]
pub type Members<T: Config> = StorageValue<_, Vec<(T::AccountId, T::BlockNumber)>, ValueQuery>;
/// The total number of vote rounds that have happened or are in progress.
#[pallet::storage]
#[pallet::getter(fn vote_index)]
pub type VoteCount<T> = StorageValue<_, VoteIndex, ValueQuery>;
// ---- 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<bool>`), use
// [`all_approvals_of`]. Furthermore, each vector of scalars is chunked with the cap of
// `APPROVAL_SET_SIZE`.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash and `SetIndex` is not
/// attacker-controlled.
#[pallet::storage]
#[pallet::getter(fn approvals_of)]
pub type ApprovalsOf<T: Config> = StorageMap<
_,
Twox64Concat, (T::AccountId, SetIndex),
Vec<ApprovalFlag>,
ValueQuery,
>;
/// The vote index and list slot that the candidate `who` was registered or `None` if they
/// are not currently registered.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash.
#[pallet::storage]
#[pallet::getter(fn candidate_reg_info)]
pub type RegisterInfoOf<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, (VoteIndex, u32)>;
/// Basic information about a voter.
///
/// TWOX-NOTE: SAFE as `AccountId` is a crypto hash.
#[pallet::storage]
#[pallet::getter(fn voter_info)]
pub type VoterInfoOf<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, VoterInfo<BalanceOf<T>>>;
/// The present voter list (chunked and capped at [`VOTER_SET_SIZE`]).
///
/// TWOX-NOTE: OKAY ― `SetIndex` is not user-controlled data.
#[pallet::storage]
#[pallet::getter(fn voters)]
pub type Voters<T: Config> = StorageMap<
_,
Twox64Concat, SetIndex,
Vec<Option<T::AccountId>>,
ValueQuery,
>;
/// the next free set to store a voter in. This will keep growing.
#[pallet::storage]
#[pallet::getter(fn next_nonfull_voter_set)]
pub type NextVoterSet<T> = StorageValue<_, SetIndex, ValueQuery>;
/// Current number of Voters.
#[pallet::storage]
#[pallet::getter(fn voter_count)]
pub type VoterCount<T> = StorageValue<_, SetIndex, ValueQuery>;
/// The present candidate list.
#[pallet::storage]
#[pallet::getter(fn candidates)]
pub type Candidates<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>; // has holes
/// Current number of active candidates
#[pallet::storage]
#[pallet::getter(fn candidate_count)]
pub type CandidateCount<T> = StorageValue<_, u32, ValueQuery>;
// ---- temporary state (only relevant during finalization/presentation)
/// The accounts holding the seats that will become free on the next tally.
#[pallet::storage]
#[pallet::getter(fn next_finalize)]
pub type NextFinalize<T: Config> = StorageValue<_, (T::BlockNumber, u32, Vec<T::AccountId>)>;
/// 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.
/// Sorted from low to high.
#[pallet::storage]
#[pallet::getter(fn leaderboard)]
pub type Leaderboard<T: Config> = StorageValue<_, Vec<(BalanceOf<T>, T::AccountId)>>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub presentation_duration: T::BlockNumber,
pub term_duration: T::BlockNumber,
pub desired_seats: u32,
pub members: Vec<(T::AccountId, T::BlockNumber)>,
}
#[cfg(feature = "std")]
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
GenesisConfig {
presentation_duration: Default::default(),
term_duration: Default::default(),
desired_seats: Default::default(),
members: Default::default(),
}
}
}
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
PresentationDuration::<T>::put(self.presentation_duration);
TermDuration::<T>::put(self.term_duration);
DesiredSeats::<T>::put(self.desired_seats);
Members::<T>::put(&self.members);
}
}
#[pallet::error]
pub enum Error<T> {
/// Reporter must be a voter.
NotVoter,
/// Target for inactivity cleanup must be active.
@@ -342,59 +452,35 @@ decl_error! {
/// No approval changes during presentation period.
ApprovalPresentation,
}
}
decl_module! {
pub struct Module<T: Config> for enum Call where origin: T::Origin {
type Error = Error<T>;
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(n: T::BlockNumber) -> Weight {
if let Err(e) = Self::end_block(n) {
print("Guru meditation");
print(e);
}
0
}
}
/// How much should be locked up in order to submit one's candidacy. A reasonable
/// default value is 9.
const CandidacyBond: BalanceOf<T> = T::CandidacyBond::get();
/// How much should be locked up in order to be able to submit votes.
const VotingBond: BalanceOf<T> = T::VotingBond::get();
/// The amount of fee paid upon each vote submission, unless if they submit a
/// _hole_ index and replace it.
const VotingFee: BalanceOf<T> = T::VotingFee::get();
/// The punishment, per voter, if you provide an invalid presentation. A
/// reasonable default value is 1.
const PresentSlashPerVoter: BalanceOf<T> = T::PresentSlashPerVoter::get();
/// How many runners-up should have their approvals persist until the next
/// vote. A reasonable default value is 2.
const CarryCount: u32 = T::CarryCount::get();
/// How many vote indices need to go by after a target voter's last vote before
/// they can be reaped if their approvals are moot. A reasonable default value
/// is 1.
const InactiveGracePeriod: VoteIndex = T::InactiveGracePeriod::get();
/// How often (in blocks) to check for new votes. A reasonable default value
/// is 1000.
const VotingPeriod: T::BlockNumber = T::VotingPeriod::get();
/// Minimum about that can be used as the locked value for voting.
const MinimumVotingLock: BalanceOf<T> = T::MinimumVotingLock::get();
/// Decay factor of weight when being accumulated. It should typically be set to
/// __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.
const DecayRatio: u32 = T::DecayRatio::get();
/// The chunk size of the voter vector.
const VOTER_SET_SIZE: u32 = VOTER_SET_SIZE as u32;
/// The chunk size of the approval vector.
const APPROVAL_SET_SIZE: u32 = APPROVAL_SET_SIZE as u32;
const PalletId: LockIdentifier = T::PalletId::get();
fn deposit_event() = default;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
#[pallet::metadata(T::AccountId = "AccountId", Vec<T::AccountId> = "Vec<AccountId>")]
pub enum Event<T: Config> {
/// Reaped \[voter, reaper\].
VoterReaped(T::AccountId, T::AccountId),
/// Slashed \[reaper\].
BadReaperSlashed(T::AccountId),
/// A tally (for approval votes of \[seats\]) has started.
TallyStarted(u32),
/// A tally (for approval votes of seat(s)) has ended (with one or more new members).
/// \[incoming, outgoing\]
TallyFinalized(Vec<T::AccountId>, Vec<T::AccountId>),
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Set candidate approvals. Approval slots stay valid as long as candidates in those slots
/// are registered.
///
@@ -419,13 +505,13 @@ decl_module! {
/// - Two extra DB entries, one DB change.
/// - Argument `votes` is limited in length to number of candidates.
/// # </weight>
#[weight = 2_500_000_000]
fn set_approvals(
origin,
#[pallet::weight(2_500_000_000)]
pub fn set_approvals(
origin: OriginFor<T>,
votes: Vec<bool>,
#[compact] index: VoteIndex,
#[pallet::compact] index: VoteIndex,
hint: SetIndex,
#[compact] value: BalanceOf<T>,
#[pallet::compact] value: BalanceOf<T>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::do_set_approvals(who, votes, index, hint, value)
@@ -443,14 +529,14 @@ decl_module! {
/// - O(1).
/// - Two fewer DB entries, one DB change.
/// # </weight>
#[weight = 2_500_000_000]
fn reap_inactive_voter(
origin,
#[compact] reporter_index: u32,
#[pallet::weight(2_500_000_000)]
pub fn reap_inactive_voter(
origin: OriginFor<T>,
#[pallet::compact] reporter_index: u32,
who: <T::Lookup as StaticLookup>::Source,
#[compact] who_index: u32,
#[compact] assumed_vote_index: VoteIndex,
) {
#[pallet::compact] who_index: u32,
#[pallet::compact] assumed_vote_index: VoteIndex,
) -> DispatchResult {
let reporter = ensure_signed(origin)?;
let who = T::Lookup::lookup(who)?;
@@ -499,12 +585,13 @@ decl_module! {
// This only fails if `reporter` doesn't exist, which it clearly must do since its
// the origin. Still, it's no more harmful to propagate any error at this point.
T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get(), BalanceStatus::Free)?;
Self::deposit_event(RawEvent::VoterReaped(who, reporter));
Self::deposit_event(Event::<T>::VoterReaped(who, reporter));
} else {
let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0;
T::BadReaper::on_unbalanced(imbalance);
Self::deposit_event(RawEvent::BadReaperSlashed(reporter));
Self::deposit_event(Event::<T>::BadReaperSlashed(reporter));
}
Ok(())
}
/// Remove a voter. All votes are cancelled and the voter deposit is returned.
@@ -517,8 +604,8 @@ decl_module! {
/// - O(1).
/// - Two fewer DB entries, one DB change.
/// # </weight>
#[weight = 1_250_000_000]
fn retract_voter(origin, #[compact] index: u32) {
#[pallet::weight(1_250_000_000)]
pub fn retract_voter(origin: OriginFor<T>, #[pallet::compact] index: u32) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(!Self::presentation_active(), Error::<T>::CannotRetractPresenting);
@@ -530,6 +617,7 @@ decl_module! {
Self::remove_voter(&who, index);
T::Currency::unreserve(&who, T::VotingBond::get());
T::Currency::remove_lock(T::PalletId::get(), &who);
Ok(())
}
/// Submit oneself for candidacy.
@@ -545,8 +633,8 @@ decl_module! {
/// - Independent of input.
/// - Three DB changes.
/// # </weight>
#[weight = 2_500_000_000]
fn submit_candidacy(origin, #[compact] slot: u32) {
#[pallet::weight(2_500_000_000)]
pub fn submit_candidacy(origin: OriginFor<T>, #[pallet::compact] slot: u32) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(!Self::is_a_candidate(&who), Error::<T>::DuplicatedCandidate);
@@ -570,7 +658,8 @@ decl_module! {
candidates[slot] = who;
}
<Candidates<T>>::put(candidates);
CandidateCount::put(count as u32 + 1);
CandidateCount::<T>::put(count as u32 + 1);
Ok(())
}
/// Claim that `candidate` is one of the top `carry_count + desired_seats` candidates. Only
@@ -582,12 +671,12 @@ decl_module! {
/// - O(voters) compute.
/// - One DB change.
/// # </weight>
#[weight = 10_000_000_000]
fn present_winner(
origin,
#[pallet::weight(10_000_000_000)]
pub fn present_winner(
origin: OriginFor<T>,
candidate: <T::Lookup as StaticLookup>::Source,
#[compact] total: BalanceOf<T>,
#[compact] index: VoteIndex,
#[pallet::compact] total: BalanceOf<T>,
#[pallet::compact] index: VoteIndex,
) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(
@@ -656,18 +745,19 @@ decl_module! {
/// Set the desired member count; if lower than the current count, then seats will not be up
/// election when they expire. If more, then a new vote will be started if one is not
/// already in progress.
#[weight = (0, DispatchClass::Operational)]
fn set_desired_seats(origin, #[compact] count: u32) {
#[pallet::weight((0, DispatchClass::Operational))]
pub fn set_desired_seats(origin: OriginFor<T>, #[pallet::compact] count: u32) -> DispatchResult {
ensure_root(origin)?;
DesiredSeats::put(count);
DesiredSeats::<T>::put(count);
Ok(())
}
/// 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.
#[weight = (0, DispatchClass::Operational)]
fn remove_member(origin, who: <T::Lookup as StaticLookup>::Source) {
#[pallet::weight((0, DispatchClass::Operational))]
pub fn remove_member(origin: OriginFor<T>, who: <T::Lookup as StaticLookup>::Source) -> DispatchResult {
ensure_root(origin)?;
let who = T::Lookup::lookup(who)?;
let new_set: Vec<(T::AccountId, T::BlockNumber)> = Self::members()
@@ -677,49 +767,36 @@ decl_module! {
<Members<T>>::put(&new_set);
let new_set = new_set.into_iter().map(|x| x.0).collect::<Vec<_>>();
T::ChangeMembers::change_members(&[], &[who], new_set);
Ok(())
}
/// Set the presentation duration. If there is currently a vote being presented for, will
/// invoke `finalize_vote`.
#[weight = (0, DispatchClass::Operational)]
fn set_presentation_duration(origin, #[compact] count: T::BlockNumber) {
#[pallet::weight((0, DispatchClass::Operational))]
pub fn set_presentation_duration(
origin: OriginFor<T>,
#[pallet::compact] count: T::BlockNumber,
) -> DispatchResult {
ensure_root(origin)?;
<PresentationDuration<T>>::put(count);
Ok(())
}
/// Set the presentation duration. If there is current a vote being presented for, will
/// invoke `finalize_vote`.
#[weight = (0, DispatchClass::Operational)]
fn set_term_duration(origin, #[compact] count: T::BlockNumber) {
#[pallet::weight((0, DispatchClass::Operational))]
pub fn set_term_duration(
origin: OriginFor<T>,
#[pallet::compact] count: T::BlockNumber,
) -> DispatchResult {
ensure_root(origin)?;
<TermDuration<T>>::put(count);
}
fn on_initialize(n: T::BlockNumber) -> Weight {
if let Err(e) = Self::end_block(n) {
print("Guru meditation");
print(e);
}
0
Ok(())
}
}
}
decl_event!(
pub enum Event<T> where <T as frame_system::Config>::AccountId {
/// Reaped \[voter, reaper\].
VoterReaped(AccountId, AccountId),
/// Slashed \[reaper\].
BadReaperSlashed(AccountId),
/// A tally (for approval votes of \[seats\]) has started.
TallyStarted(u32),
/// A tally (for approval votes of seat(s)) has ended (with one or more new members).
/// \[incoming, outgoing\]
TallyFinalized(Vec<AccountId>, Vec<AccountId>),
}
);
impl<T: Config> Module<T> {
impl<T: Config> Pallet<T> {
// exposed immutables.
/// True if we're currently in a presentation period.
@@ -800,7 +877,7 @@ impl<T: Config> Module<T> {
let mut set = Self::voters(set_index);
set[vec_index] = None;
<Voters<T>>::insert(set_index, set);
VoterCount::mutate(|c| *c = *c - 1);
VoterCount::<T>::mutate(|c| *c = *c - 1);
Self::remove_all_approvals_of(voter);
<VoterInfoOf<T>>::remove(voter);
}
@@ -879,14 +956,14 @@ impl<T: Config> Module<T> {
locked_balance -= T::VotingFee::get();
}
if set_len + 1 == VOTER_SET_SIZE {
NextVoterSet::put(next + 1);
NextVoterSet::<T>::put(next + 1);
}
<Voters<T>>::append(next, Some(who.clone()));
}
}
T::Currency::reserve(&who, T::VotingBond::get())?;
VoterCount::mutate(|c| *c = *c + 1);
VoterCount::<T>::mutate(|c| *c = *c + 1);
}
T::Currency::set_lock(
@@ -928,7 +1005,7 @@ impl<T: Config> Module<T> {
let leaderboard_size = empty_seats + T::CarryCount::get() as usize;
<Leaderboard<T>>::put(vec![(BalanceOf::<T>::zero(), T::AccountId::default()); leaderboard_size]);
Self::deposit_event(RawEvent::TallyStarted(empty_seats as u32));
Self::deposit_event(Event::<T>::TallyStarted(empty_seats as u32));
}
}
@@ -1017,11 +1094,11 @@ impl<T: Config> Module<T> {
new_candidates.truncate(last_index + 1);
}
Self::deposit_event(RawEvent::TallyFinalized(incoming, outgoing));
Self::deposit_event(Event::<T>::TallyFinalized(incoming, outgoing));
<Candidates<T>>::put(new_candidates);
CandidateCount::put(count);
VoteCount::put(Self::vote_index() + 1);
CandidateCount::<T>::put(count);
VoteCount::<T>::put(Self::vote_index() + 1);
Ok(())
}
+2 -2
View File
@@ -20,7 +20,7 @@
#![cfg(test)]
use frame_support::{
StorageValue, StorageMap, parameter_types, assert_ok,
parameter_types, assert_ok,
traits::{ChangeMembers, Currency, LockIdentifier},
};
use sp_core::H256;
@@ -266,7 +266,7 @@ pub(crate) fn new_test_ext_with_candidate_holes() -> sp_io::TestExternalities {
let mut t = ExtBuilder::default().build();
t.execute_with(|| {
<elections::Candidates<Test>>::put(vec![0, 0, 1]);
elections::CandidateCount::put(1);
elections::CandidateCount::<Test>::put(1);
<elections::RegisterInfoOf<Test>>::insert(1, (0, 2));
});
t