mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 09:21:05 +00:00
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:
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user