Files
pezkuwi-subxt/substrate/frame/democracy/src/lib.rs
T
2020-05-22 15:08:51 +02:00

2122 lines
79 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// This file is part of Substrate.
// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Democracy Pallet
//!
//! - [`democracy::Trait`](./trait.Trait.html)
//! - [`Call`](./enum.Call.html)
//!
//! ## Overview
//!
//! The Democracy pallet handles the administration of general stakeholder voting.
//!
//! There are two different queues that a proposal can be added to before it
//! becomes a referendum, 1) the proposal queue consisting of all public proposals
//! and 2) the external queue consisting of a single proposal that originates
//! from one of the _external_ origins (such as a collective group).
//!
//! Every launch period - a length defined in the runtime - the Democracy pallet
//! launches a referendum from a proposal that it takes from either the proposal
//! queue or the external queue in turn. Any token holder in the system can vote
//! on referenda. The voting system
//! uses time-lock voting by allowing the token holder to set their _conviction_
//! behind a vote. The conviction will dictate the length of time the tokens
//! will be locked, as well as the multiplier that scales the vote power.
//!
//! ### Terminology
//!
//! - **Enactment Period:** The minimum period of locking and the period between a proposal being
//! approved and enacted.
//! - **Lock Period:** A period of time after proposal enactment that the tokens of _winning_ voters
//! will be locked.
//! - **Conviction:** An indication of a voter's strength of belief in their vote. An increase
//! of one in conviction indicates that a token holder is willing to lock their tokens for twice
//! as many lock periods after enactment.
//! - **Vote:** A value that can either be in approval ("Aye") or rejection ("Nay")
//! of a particular referendum.
//! - **Proposal:** A submission to the chain that represents an action that a proposer (either an
//! account or an external origin) suggests that the system adopt.
//! - **Referendum:** A proposal that is in the process of being voted on for
//! either acceptance or rejection as a change to the system.
//! - **Proxy:** An account that has full voting power on behalf of a separate "Stash" account
//! that holds the funds.
//! - **Delegation:** The act of granting your voting power to the decisions of another account for
//! up to a certain conviction.
//!
//! ### Adaptive Quorum Biasing
//!
//! A _referendum_ can be either simple majority-carries in which 50%+1 of the
//! votes decide the outcome or _adaptive quorum biased_. Adaptive quorum biasing
//! makes the threshold for passing or rejecting a referendum higher or lower
//! depending on how the referendum was originally proposed. There are two types of
//! adaptive quorum biasing: 1) _positive turnout bias_ makes a referendum
//! require a super-majority to pass that decreases as turnout increases and
//! 2) _negative turnout bias_ makes a referendum require a super-majority to
//! reject that decreases as turnout increases. Another way to think about the
//! quorum biasing is that _positive bias_ referendums will be rejected by
//! default and _negative bias_ referendums get passed by default.
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! #### Public
//!
//! These calls can be made from any externally held account capable of creating
//! a signed extrinsic.
//!
//! Basic actions:
//! - `propose` - Submits a sensitive action, represented as a hash. Requires a deposit.
//! - `second` - Signals agreement with a proposal, moves it higher on the proposal queue, and
//! requires a matching deposit to the original.
//! - `vote` - Votes in a referendum, either the vote is "Aye" to enact the proposal or "Nay" to
//! keep the status quo.
//! - `unvote` - Cancel a previous vote, this must be done by the voter before the vote ends.
//! - `delegate` - Delegates the voting power (tokens * conviction) to another account.
//! - `undelegate` - Stops the delegation of voting power to another account.
//!
//! Administration actions that can be done to any account:
//! - `reap_vote` - Remove some account's expired votes.
//! - `unlock` - Redetermine the account's balance lock, potentially making tokens available.
//!
//! Proxy administration:
//! - `activate_proxy` - Activates a proxy that is already open to the sender.
//! - `close_proxy` - Clears the proxy status, called by the proxy.
//! - `deactivate_proxy` - Deactivates a proxy back to the open status, called by the stash.
//! - `open_proxy` - Opens a proxy account on behalf of the sender.
//!
//! Proxy actions:
//! - `proxy_vote` - Votes in a referendum on behalf of a stash account.
//! - `proxy_unvote` - Cancel a previous vote, done on behalf of the voter by a proxy.
//! - `proxy_delegate` - Delegate voting power, done on behalf of the voter by a proxy.
//! - `proxy_undelegate` - Stop delegating voting power, done on behalf of the voter by a proxy.
//!
//! Preimage actions:
//! - `note_preimage` - Registers the preimage for an upcoming proposal, requires
//! a deposit that is returned once the proposal is enacted.
//! - `note_preimage_operational` - same but provided by `T::OperationalPreimageOrigin`.
//! - `note_imminent_preimage` - Registers the preimage for an upcoming proposal.
//! Does not require a deposit, but the proposal must be in the dispatch queue.
//! - `note_imminent_preimage_operational` - same but provided by `T::OperationalPreimageOrigin`.
//! - `reap_preimage` - Removes the preimage for an expired proposal. Will only
//! work under the condition that it's the same account that noted it and
//! after the voting period, OR it's a different account after the enactment period.
//!
//! #### Cancellation Origin
//!
//! This call can only be made by the `CancellationOrigin`.
//!
//! - `emergency_cancel` - Schedules an emergency cancellation of a referendum.
//! Can only happen once to a specific referendum.
//!
//! #### ExternalOrigin
//!
//! This call can only be made by the `ExternalOrigin`.
//!
//! - `external_propose` - Schedules a proposal to become a referendum once it is is legal
//! for an externally proposed referendum.
//!
//! #### External Majority Origin
//!
//! This call can only be made by the `ExternalMajorityOrigin`.
//!
//! - `external_propose_majority` - Schedules a proposal to become a majority-carries
//! referendum once it is legal for an externally proposed referendum.
//!
//! #### External Default Origin
//!
//! This call can only be made by the `ExternalDefaultOrigin`.
//!
//! - `external_propose_default` - Schedules a proposal to become a negative-turnout-bias
//! referendum once it is legal for an externally proposed referendum.
//!
//! #### Fast Track Origin
//!
//! This call can only be made by the `FastTrackOrigin`.
//!
//! - `fast_track` - Schedules the current externally proposed proposal that
//! is "majority-carries" to become a referendum immediately.
//!
//! #### Veto Origin
//!
//! This call can only be made by the `VetoOrigin`.
//!
//! - `veto_external` - Vetoes and blacklists the external proposal hash.
//!
//! #### Root
//!
//! - `cancel_referendum` - Removes a referendum.
//! - `cancel_queued` - Cancels a proposal that is queued for enactment.
//! - `clear_public_proposal` - Removes all public proposals.
#![recursion_limit="128"]
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::prelude::*;
use sp_runtime::{
DispatchResult, DispatchError, RuntimeDebug,
traits::{Zero, Hash, Dispatchable, Saturating},
};
use codec::{Encode, Decode, Input};
use frame_support::{
decl_module, decl_storage, decl_event, decl_error, ensure, Parameter,
storage::IterableStorageMap,
weights::{Weight, DispatchClass},
traits::{
Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get,
OnUnbalanced, BalanceStatus, schedule::Named as ScheduleNamed, EnsureOrigin
},
dispatch::DispatchResultWithPostInfo,
};
use frame_system::{self as system, ensure_signed, ensure_root};
mod vote_threshold;
mod vote;
mod conviction;
mod types;
pub use vote_threshold::{Approved, VoteThreshold};
pub use vote::{Vote, AccountVote, Voting};
pub use conviction::Conviction;
pub use types::{ReferendumInfo, ReferendumStatus, ProxyState, Tally, UnvoteScope, Delegations};
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
const DEMOCRACY_ID: LockIdentifier = *b"democrac";
/// The maximum number of vetoers on a single proposal used to compute Weight.
///
/// NOTE: This is not enforced by any logic.
pub const MAX_VETOERS: Weight = 100;
/// A proposal index.
pub type PropIndex = u32;
/// A referendum index.
pub type ReferendumIndex = u32;
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
type NegativeImbalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
pub trait Trait: frame_system::Trait + Sized {
type Proposal: Parameter + Dispatchable<Origin=Self::Origin> + From<Call<Self>>;
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
/// Currency type for this module.
type Currency: ReservableCurrency<Self::AccountId>
+ LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>;
/// The minimum period of locking and the period between a proposal being approved and enacted.
///
/// It should generally be a little more than the unstake period to ensure that
/// voting stakers have an opportunity to remove themselves from the system in the case where
/// they are on the losing side of a vote.
type EnactmentPeriod: Get<Self::BlockNumber>;
/// How often (in blocks) new public referenda are launched.
type LaunchPeriod: Get<Self::BlockNumber>;
/// How often (in blocks) to check for new votes.
type VotingPeriod: Get<Self::BlockNumber>;
/// The minimum amount to be used as a deposit for a public referendum proposal.
type MinimumDeposit: Get<BalanceOf<Self>>;
/// Origin from which the next tabled referendum may be forced. This is a normal
/// "super-majority-required" referendum.
type ExternalOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which the next tabled referendum may be forced; this allows for the tabling of
/// a majority-carries referendum.
type ExternalMajorityOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which the next tabled referendum may be forced; this allows for the tabling of
/// a negative-turnout-bias (default-carries) referendum.
type ExternalDefaultOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which the next majority-carries (or more permissive) referendum may be tabled to
/// vote according to the `FastTrackVotingPeriod` asynchronously in a similar manner to the
/// emergency origin. It retains its threshold method.
type FastTrackOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which the next majority-carries (or more permissive) referendum may be tabled to
/// vote immediately and asynchronously in a similar manner to the emergency origin. It retains
/// its threshold method.
type InstantOrigin: EnsureOrigin<Self::Origin>;
/// Indicator for whether an emergency origin is even allowed to happen. Some chains may want
/// to set this permanently to `false`, others may want to condition it on things such as
/// an upgrade having happened recently.
type InstantAllowed: Get<bool>;
/// Minimum voting period allowed for a fast-track referendum.
type FastTrackVotingPeriod: Get<Self::BlockNumber>;
/// Origin from which any referendum may be cancelled in an emergency.
type CancellationOrigin: EnsureOrigin<Self::Origin>;
/// Origin for anyone able to veto proposals.
///
/// # Warning
///
/// The number of Vetoers for a proposal must be small, extrinsics are weighted according to
/// [MAX_VETOERS](./const.MAX_VETOERS.html)
type VetoOrigin: EnsureOrigin<Self::Origin, Success=Self::AccountId>;
/// Period in blocks where an external proposal may not be re-submitted after being vetoed.
type CooloffPeriod: Get<Self::BlockNumber>;
/// The amount of balance that must be deposited per byte of preimage stored.
type PreimageByteDeposit: Get<BalanceOf<Self>>;
/// An origin that can provide a preimage using operational extrinsics.
type OperationalPreimageOrigin: EnsureOrigin<Self::Origin, Success=Self::AccountId>;
/// Handler for the unbalanced reduction when slashing a preimage deposit.
type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// The Scheduler.
type Scheduler: ScheduleNamed<Self::BlockNumber, Self::Proposal>;
/// The maximum number of votes for an account.
///
/// Also used to compute weight, an overly big value can
/// lead to extrinsic with very big weight: see `delegate` for instance.
type MaxVotes: Get<u32>;
}
#[derive(Clone, Encode, Decode, RuntimeDebug)]
pub enum PreimageStatus<AccountId, Balance, BlockNumber> {
/// The preimage is imminently needed at the argument.
Missing(BlockNumber),
/// The preimage is available.
Available {
data: Vec<u8>,
provider: AccountId,
deposit: Balance,
since: BlockNumber,
/// None if it's not imminent.
expiry: Option<BlockNumber>,
},
}
impl<AccountId, Balance, BlockNumber> PreimageStatus<AccountId, Balance, BlockNumber> {
fn to_missing_expiry(self) -> Option<BlockNumber> {
match self {
PreimageStatus::Missing(expiry) => Some(expiry),
_ => None,
}
}
}
// A value placed in storage that represents the current version of the Democracy storage.
// This value is used by the `on_runtime_upgrade` logic to determine whether we run
// storage migration logic.
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)]
enum Releases {
V1,
}
decl_storage! {
trait Store for Module<T: Trait> as Democracy {
// TODO: Refactor public proposal queue into its own pallet.
// https://github.com/paritytech/substrate/issues/5322
/// The number of (public) proposals that have been made so far.
pub PublicPropCount get(fn public_prop_count) build(|_| 0 as PropIndex) : PropIndex;
/// The public proposals. Unsorted. The second item is the proposal's hash.
pub PublicProps get(fn public_props): Vec<(PropIndex, T::Hash, T::AccountId)>;
/// Those who have locked a deposit.
///
/// TWOX-NOTE: Safe, as increasing integer keys are safe.
pub DepositOf get(fn deposit_of):
map hasher(twox_64_concat) PropIndex => Option<(Vec<T::AccountId>, BalanceOf<T>)>;
/// Map of hashes to the proposal preimage, along with who registered it and their deposit.
/// The block number is the block at which it was deposited.
// TODO: Refactor Preimages into its own pallet.
// https://github.com/paritytech/substrate/issues/5322
pub Preimages:
map hasher(identity) T::Hash
=> Option<PreimageStatus<T::AccountId, BalanceOf<T>, T::BlockNumber>>;
/// The next free referendum index, aka the number of referenda started so far.
pub ReferendumCount get(fn referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex;
/// The lowest referendum index representing an unbaked referendum. Equal to
/// `ReferendumCount` if there isn't a unbaked referendum.
pub LowestUnbaked get(fn lowest_unbaked) build(|_| 0 as ReferendumIndex): ReferendumIndex;
/// Information concerning any given referendum.
///
/// TWOX-NOTE: SAFE as indexes are not under an attackers control.
pub ReferendumInfoOf get(fn referendum_info):
map hasher(twox_64_concat) ReferendumIndex
=> Option<ReferendumInfo<T::BlockNumber, T::Hash, BalanceOf<T>>>;
/// All votes for a particular voter. We store the balance for the number of votes that we
/// have recorded. The second item is the total amount of delegations, that will be added.
///
/// TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway.
pub VotingOf: map hasher(twox_64_concat) T::AccountId => Voting<BalanceOf<T>, T::AccountId, T::BlockNumber>;
/// Who is able to vote for whom. Value is the fund-holding account, key is the
/// vote-transaction-sending account.
///
/// TWOX-NOTE: OK ― `AccountId` is a secure hash.
// TODO: Refactor proxy into its own pallet.
// https://github.com/paritytech/substrate/issues/5322
pub Proxy get(fn proxy): map hasher(twox_64_concat) T::AccountId => Option<ProxyState<T::AccountId>>;
/// Accounts for which there are locks in action which may be removed at some point in the
/// future. The value is the block number at which the lock expires and may be removed.
///
/// TWOX-NOTE: OK ― `AccountId` is a secure hash.
pub Locks get(fn locks): map hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;
/// True if the last referendum tabled was submitted externally. False if it was a public
/// proposal.
// TODO: There should be any number of tabling origins, not just public and "external" (council).
// https://github.com/paritytech/substrate/issues/5322
pub LastTabledWasExternal: bool;
/// The referendum to be tabled whenever it would be valid to table an external proposal.
/// This happens when a referendum needs to be tabled and one of two conditions are met:
/// - `LastTabledWasExternal` is `false`; or
/// - `PublicProps` is empty.
pub NextExternal: Option<(T::Hash, VoteThreshold)>;
/// A record of who vetoed what. Maps proposal hash to a possible existent block number
/// (until when it may not be resubmitted) and who vetoed it.
pub Blacklist get(fn blacklist):
map hasher(identity) T::Hash => Option<(T::BlockNumber, Vec<T::AccountId>)>;
/// Record of all proposals that have been subject to emergency cancellation.
pub Cancellations: map hasher(identity) T::Hash => bool;
/// Storage version of the pallet.
///
/// New networks start with last version.
StorageVersion build(|_| Some(Releases::V1)): Option<Releases>;
}
}
decl_event! {
pub enum Event<T> where
Balance = BalanceOf<T>,
<T as frame_system::Trait>::AccountId,
<T as frame_system::Trait>::Hash,
<T as frame_system::Trait>::BlockNumber,
{
/// A motion has been proposed by a public account.
Proposed(PropIndex, Balance),
/// A public proposal has been tabled for referendum vote.
Tabled(PropIndex, Balance, Vec<AccountId>),
/// An external proposal has been tabled.
ExternalTabled,
/// A referendum has begun.
Started(ReferendumIndex, VoteThreshold),
/// A proposal has been approved by referendum.
Passed(ReferendumIndex),
/// A proposal has been rejected by referendum.
NotPassed(ReferendumIndex),
/// A referendum has been cancelled.
Cancelled(ReferendumIndex),
/// A proposal has been enacted.
Executed(ReferendumIndex, bool),
/// An account has delegated their vote to another account.
Delegated(AccountId, AccountId),
/// An account has cancelled a previous delegation operation.
Undelegated(AccountId),
/// An external proposal has been vetoed.
Vetoed(AccountId, Hash, BlockNumber),
/// A proposal's preimage was noted, and the deposit taken.
PreimageNoted(Hash, AccountId, Balance),
/// A proposal preimage was removed and used (the deposit was returned).
PreimageUsed(Hash, AccountId, Balance),
/// A proposal could not be executed because its preimage was invalid.
PreimageInvalid(Hash, ReferendumIndex),
/// A proposal could not be executed because its preimage was missing.
PreimageMissing(Hash, ReferendumIndex),
/// A registered preimage was removed and the deposit collected by the reaper (last item).
PreimageReaped(Hash, AccountId, Balance, AccountId),
/// An account has been unlocked successfully.
Unlocked(AccountId),
}
}
decl_error! {
pub enum Error for Module<T: Trait> {
/// Value too low
ValueLow,
/// Proposal does not exist
ProposalMissing,
/// Not a proxy
NotProxy,
/// Unknown index
BadIndex,
/// Cannot cancel the same proposal twice
AlreadyCanceled,
/// Proposal already made
DuplicateProposal,
/// Proposal still blacklisted
ProposalBlacklisted,
/// Next external proposal not simple majority
NotSimpleMajority,
/// Invalid hash
InvalidHash,
/// No external proposal
NoProposal,
/// Identity may not veto a proposal twice
AlreadyVetoed,
/// Already a proxy
AlreadyProxy,
/// Wrong proxy
WrongProxy,
/// Not delegated
NotDelegated,
/// Preimage already noted
DuplicatePreimage,
/// Not imminent
NotImminent,
/// Too early
TooEarly,
/// Imminent
Imminent,
/// Preimage not found
PreimageMissing,
/// Vote given for invalid referendum
ReferendumInvalid,
/// Invalid preimage
PreimageInvalid,
/// No proposals waiting
NoneWaiting,
/// The target account does not have a lock.
NotLocked,
/// The lock on the account to be unlocked has not yet expired.
NotExpired,
/// A proxy-pairing was attempted to an account that was not open.
NotOpen,
/// A proxy-pairing was attempted to an account that was open to another account.
WrongOpen,
/// A proxy-de-pairing was attempted to an account that was not active.
NotActive,
/// The given account did not vote on the referendum.
NotVoter,
/// The actor has no permission to conduct the action.
NoPermission,
/// The account is already delegating.
AlreadyDelegating,
/// An unexpected integer overflow occurred.
Overflow,
/// An unexpected integer underflow occurred.
Underflow,
/// Too high a balance was provided that the account cannot afford.
InsufficientFunds,
/// The account is not currently delegating.
NotDelegating,
/// The account currently has votes attached to it and the operation cannot succeed until
/// these are removed, either through `unvote` or `reap_vote`.
VotesExist,
/// The instant referendum origin is currently disallowed.
InstantNotAllowed,
/// Delegation to oneself makes no sense.
Nonsense,
/// Invalid upper bound.
WrongUpperBound,
/// Maximum number of votes reached.
MaxVotesReached,
}
}
/// Functions for calcuating the weight of some dispatchables.
mod weight_for {
use frame_support::{traits::Get, weights::Weight};
use super::Trait;
/// Calculate the weight for `delegate`.
/// - Db reads: 2*`VotingOf`, `balances locks`
/// - Db writes: 2*`VotingOf`, `balances locks`
/// - Db reads per votes: `ReferendumInfoOf`
/// - Db writes per votes: `ReferendumInfoOf`
/// - Base Weight: 65.78 + 8.229 * R µs
// NOTE: weight must cover an incorrect voting of origin with 100 votes.
pub fn delegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(3), votes.saturating_add(3))
.saturating_add(66_000_000)
.saturating_add(votes.saturating_mul(8_100_000))
}
/// Calculate the weight for `undelegate`.
/// - Db reads: 2*`VotingOf`
/// - Db writes: 2*`VotingOf`
/// - Db reads per votes: `ReferendumInfoOf`
/// - Db writes per votes: `ReferendumInfoOf`
/// - Base Weight: 33.29 + 8.104 * R µs
pub fn undelegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(2), votes.saturating_add(2))
.saturating_add(33_000_000)
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `proxy_delegate`.
/// same as `delegate with additional:
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `proxy account`
/// - Base Weight: 68.61 + 8.039 * R µs
pub fn proxy_delegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(5), votes.saturating_add(4))
.saturating_add(69_000_000)
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `proxy_undelegate`.
/// same as `undelegate with additional:
/// Db reads: `Proxy`
/// Base Weight: 39 + 7.958 * R µs
pub fn proxy_undelegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(3), votes.saturating_add(2))
.saturating_add(40_000_000)
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `note_preimage`.
/// # <weight>
/// - Complexity: `O(E)` with E size of `encoded_proposal` (protected by a required deposit).
/// - Db reads: `Preimages`
/// - Db writes: `Preimages`
/// - Base Weight: 37.93 + .004 * b µs
/// # </weight>
pub fn note_preimage<T: Trait>(encoded_proposal_len: Weight) -> Weight {
T::DbWeight::get().reads_writes(1, 1)
.saturating_add(38_000_000)
.saturating_add(encoded_proposal_len.saturating_mul(4_000))
}
/// Calculate the weight for `note_imminent_preimage`.
/// # <weight>
/// - Complexity: `O(E)` with E size of `encoded_proposal` (protected by a required deposit).
/// - Db reads: `Preimages`
/// - Db writes: `Preimages`
/// - Base Weight: 28.04 + .003 * b µs
/// # </weight>
pub fn note_imminent_preimage<T: Trait>(encoded_proposal_len: Weight) -> Weight {
T::DbWeight::get().reads_writes(1, 1)
.saturating_add(28_000_000)
.saturating_add(encoded_proposal_len.saturating_mul(3_000))
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
/// The minimum period of locking and the period between a proposal being approved and enacted.
///
/// It should generally be a little more than the unstake period to ensure that
/// voting stakers have an opportunity to remove themselves from the system in the case where
/// they are on the losing side of a vote.
const EnactmentPeriod: T::BlockNumber = T::EnactmentPeriod::get();
/// How often (in blocks) new public referenda are launched.
const LaunchPeriod: T::BlockNumber = T::LaunchPeriod::get();
/// How often (in blocks) to check for new votes.
const VotingPeriod: T::BlockNumber = T::VotingPeriod::get();
/// The minimum amount to be used as a deposit for a public referendum proposal.
const MinimumDeposit: BalanceOf<T> = T::MinimumDeposit::get();
/// Minimum voting period allowed for an emergency referendum.
const FastTrackVotingPeriod: T::BlockNumber = T::FastTrackVotingPeriod::get();
/// Period in blocks where an external proposal may not be re-submitted after being vetoed.
const CooloffPeriod: T::BlockNumber = T::CooloffPeriod::get();
/// The amount of balance that must be deposited per byte of preimage stored.
const PreimageByteDeposit: BalanceOf<T> = T::PreimageByteDeposit::get();
/// The maximum number of votes for an account.
const MaxVotes: u32 = T::MaxVotes::get();
fn deposit_event() = default;
fn on_runtime_upgrade() -> Weight {
if let None = StorageVersion::get() {
StorageVersion::put(Releases::V1);
DepositOf::<T>::translate::<
(BalanceOf<T>, Vec<T::AccountId>), _
>(|_, (balance, accounts)| {
Some((accounts, balance))
});
T::MaximumBlockWeight::get()
} else {
T::DbWeight::get().reads(1)
}
}
/// Propose a sensitive action to be taken.
///
/// The dispatch origin of this call must be _Signed_ and the sender must
/// have funds to cover the deposit.
///
/// - `proposal_hash`: The hash of the proposal preimage.
/// - `value`: The amount of deposit (must be at least `MinimumDeposit`).
///
/// Emits `Proposed`.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `PublicPropCount`, `PublicProps`
/// - Db writes: `PublicPropCount`, `PublicProps`, `DepositOf`
/// -------------------
/// Base Weight: 42.58 + .127 * P µs with `P` the number of proposals `PublicProps`
/// # </weight>
#[weight = 50_000_000 + T::DbWeight::get().reads_writes(2, 3)]
fn propose(origin, proposal_hash: T::Hash, #[compact] value: BalanceOf<T>) {
let who = ensure_signed(origin)?;
ensure!(value >= T::MinimumDeposit::get(), Error::<T>::ValueLow);
T::Currency::reserve(&who, value)?;
let index = Self::public_prop_count();
PublicPropCount::put(index + 1);
<DepositOf<T>>::insert(index, (&[&who][..], value));
<PublicProps<T>>::append((index, proposal_hash, who));
Self::deposit_event(RawEvent::Proposed(index, value));
}
/// Signals agreement with a particular proposal.
///
/// The dispatch origin of this call must be _Signed_ and the sender
/// must have funds to cover the deposit, equal to the original deposit.
///
/// - `proposal`: The index of the proposal to second.
/// - `seconds_upper_bound`: an upper bound on the current number of seconds on this
/// proposal. Extrinsic is weighted according to this value with no refund.
///
/// # <weight>
/// - Complexity: `O(S)` where S is the number of seconds a proposal already has.
/// - Db reads: `DepositOf`
/// - Db writes: `DepositOf`
/// ---------
/// - Base Weight: 22.28 + .229 * S µs
/// # </weight>
#[weight = 23_000_000
.saturating_add(230_000.saturating_mul(Weight::from(*seconds_upper_bound)))
.saturating_add(T::DbWeight::get().reads_writes(1, 1))
]
fn second(origin, #[compact] proposal: PropIndex, #[compact] seconds_upper_bound: u32) {
let who = ensure_signed(origin)?;
let seconds = Self::len_of_deposit_of(proposal)
.ok_or_else(|| Error::<T>::ProposalMissing)?;
ensure!(seconds <= seconds_upper_bound, Error::<T>::WrongUpperBound);
let mut deposit = Self::deposit_of(proposal)
.ok_or(Error::<T>::ProposalMissing)?;
T::Currency::reserve(&who, deposit.1)?;
deposit.0.push(who);
<DepositOf<T>>::insert(proposal, deposit);
}
/// Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;
/// otherwise it is a vote to keep the status quo.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `ref_index`: The index of the referendum to vote for.
/// - `vote`: The vote configuration.
///
/// # <weight>
/// - Complexity: `O(R)` where R is the number of referendums the voter has voted on.
/// weight is charged as if maximum votes.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`, `balances locks`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`, `balances locks`
/// --------------------
/// - Base Weight:
/// - Vote New: 49.24 + .333 * R µs
/// - Vote Existing: 49.94 + .343 * R µs
/// # </weight>
#[weight = 50_000_000 + 350_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(3, 3)]
fn vote(origin,
#[compact] ref_index: ReferendumIndex,
vote: AccountVote<BalanceOf<T>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::try_vote(&who, ref_index, vote)
}
/// Vote in a referendum on behalf of a stash. If `vote.is_aye()`, the vote is to enact
/// the proposal; otherwise it is a vote to keep the status quo.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `ref_index`: The index of the referendum to proxy vote for.
/// - `vote`: The vote configuration.
///
/// # <weight>
/// - Complexity: `O(R)` where R is the number of referendums the proxy has voted on.
/// weight is charged as if maximum votes.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`, `balances locks`, `Proxy`, `proxy account`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`, `balances locks`
/// ------------
/// - Base Weight:
/// - Proxy Vote New: 54.35 + .344 * R µs
/// - Proxy Vote Existing: 54.35 + .35 * R µs
/// # </weight>
#[weight = 55_000_000 + 350_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(5, 3)]
fn proxy_vote(origin,
#[compact] ref_index: ReferendumIndex,
vote: AccountVote<BalanceOf<T>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let voter = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
Self::try_vote(&voter, ref_index, vote)
}
/// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same
/// referendum.
///
/// The dispatch origin of this call must be `CancellationOrigin`.
///
/// -`ref_index`: The index of the referendum to cancel.
///
/// # <weight>
/// - Complexity: `O(1)`.
/// - Db reads: `ReferendumInfoOf`, `Cancellations`
/// - Db writes: `ReferendumInfoOf`, `Cancellations`
/// -------------
/// - Base Weight: 34.25 µs
/// # </weight>
#[weight = (35_000_000 + T::DbWeight::get().reads_writes(2, 2), DispatchClass::Operational)]
fn emergency_cancel(origin, ref_index: ReferendumIndex) {
T::CancellationOrigin::ensure_origin(origin)?;
let status = Self::referendum_status(ref_index)?;
let h = status.proposal_hash;
ensure!(!<Cancellations<T>>::contains_key(h), Error::<T>::AlreadyCanceled);
<Cancellations<T>>::insert(h, true);
Self::internal_cancel_referendum(ref_index);
}
/// Schedule a referendum to be tabled once it is legal to schedule an external
/// referendum.
///
/// The dispatch origin of this call must be `ExternalOrigin`.
///
/// - `proposal_hash`: The preimage hash of the proposal.
///
/// # <weight>
/// - Complexity `O(V)` with V number of vetoers in the blacklist of proposal.
/// Decoding vec of length V. Charged as maximum
/// - Db reads: `NextExternal`, `Blacklist`
/// - Db writes: `NextExternal`
/// - Base Weight: 13.8 + .106 * V µs
/// # </weight>
#[weight = 15_000_000 + 110_000 * MAX_VETOERS + T::DbWeight::get().reads_writes(2, 1)]
fn external_propose(origin, proposal_hash: T::Hash) {
T::ExternalOrigin::ensure_origin(origin)?;
ensure!(!<NextExternal<T>>::exists(), Error::<T>::DuplicateProposal);
if let Some((until, _)) = <Blacklist<T>>::get(proposal_hash) {
ensure!(
<frame_system::Module<T>>::block_number() >= until,
Error::<T>::ProposalBlacklisted,
);
}
<NextExternal<T>>::put((proposal_hash, VoteThreshold::SuperMajorityApprove));
}
/// Schedule a majority-carries referendum to be tabled next once it is legal to schedule
/// an external referendum.
///
/// The dispatch of this call must be `ExternalMajorityOrigin`.
///
/// - `proposal_hash`: The preimage hash of the proposal.
///
/// Unlike `external_propose`, blacklisting has no effect on this and it may replace a
/// pre-scheduled `external_propose` call.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db write: `NextExternal`
/// - Base Weight: 3.065 µs
/// # </weight>
#[weight = 3_100_000 + T::DbWeight::get().writes(1)]
fn external_propose_majority(origin, proposal_hash: T::Hash) {
T::ExternalMajorityOrigin::ensure_origin(origin)?;
<NextExternal<T>>::put((proposal_hash, VoteThreshold::SimpleMajority));
}
/// Schedule a negative-turnout-bias referendum to be tabled next once it is legal to
/// schedule an external referendum.
///
/// The dispatch of this call must be `ExternalDefaultOrigin`.
///
/// - `proposal_hash`: The preimage hash of the proposal.
///
/// Unlike `external_propose`, blacklisting has no effect on this and it may replace a
/// pre-scheduled `external_propose` call.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db write: `NextExternal`
/// - Base Weight: 3.087 µs
/// # </weight>
#[weight = 3_100_000 + T::DbWeight::get().writes(1)]
fn external_propose_default(origin, proposal_hash: T::Hash) {
T::ExternalDefaultOrigin::ensure_origin(origin)?;
<NextExternal<T>>::put((proposal_hash, VoteThreshold::SuperMajorityAgainst));
}
/// 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.
///
/// The dispatch of this call must be `FastTrackOrigin`.
///
/// - `proposal_hash`: The hash of the current external proposal.
/// - `voting_period`: The period that is allowed for voting on this proposal. Increased to
/// `FastTrackVotingPeriod` if too low.
/// - `delay`: The number of block after voting has ended in approval and this should be
/// enacted. This doesn't have a minimum amount.
///
/// Emits `Started`.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `NextExternal`, `ReferendumCount`
/// - Db writes: `NextExternal`, `ReferendumCount`, `ReferendumInfoOf`
/// - Base Weight: 30.1 µs
/// # </weight>
#[weight = 30_000_000 + T::DbWeight::get().reads_writes(2, 3)]
fn fast_track(origin,
proposal_hash: T::Hash,
voting_period: T::BlockNumber,
delay: T::BlockNumber,
) {
// Rather complicated bit of code to ensure that either:
// - `voting_period` is at least `FastTrackVotingPeriod` and `origin` is `FastTrackOrigin`; or
// - `InstantAllowed` is `true` and `origin` is `InstantOrigin`.
let maybe_ensure_instant = if voting_period < T::FastTrackVotingPeriod::get() {
Some(origin)
} else {
if let Err(origin) = T::FastTrackOrigin::try_origin(origin) {
Some(origin)
} else {
None
}
};
if let Some(ensure_instant) = maybe_ensure_instant {
T::InstantOrigin::ensure_origin(ensure_instant)?;
ensure!(T::InstantAllowed::get(), Error::<T>::InstantNotAllowed);
}
let (e_proposal_hash, threshold) = <NextExternal<T>>::get()
.ok_or(Error::<T>::ProposalMissing)?;
ensure!(
threshold != VoteThreshold::SuperMajorityApprove,
Error::<T>::NotSimpleMajority,
);
ensure!(proposal_hash == e_proposal_hash, Error::<T>::InvalidHash);
<NextExternal<T>>::kill();
let now = <frame_system::Module<T>>::block_number();
Self::inject_referendum(now + voting_period, proposal_hash, threshold, delay);
}
/// Veto and blacklist the external proposal hash.
///
/// The dispatch origin of this call must be `VetoOrigin`.
///
/// - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.
///
/// Emits `Vetoed`.
///
/// # <weight>
/// - Complexity: `O(V + log(V))` where V is number of `existing vetoers`
/// Performs a binary search on `existing_vetoers` which should not be very large.
/// - Db reads: `NextExternal`, `Blacklist`
/// - Db writes: `NextExternal`, `Blacklist`
/// - Base Weight: 29.87 + .188 * V µs
/// # </weight>
#[weight = 30_000_000 + 180_000 * MAX_VETOERS + T::DbWeight::get().reads_writes(2, 2)]
fn veto_external(origin, proposal_hash: T::Hash) {
let who = T::VetoOrigin::ensure_origin(origin)?;
if let Some((e_proposal_hash, _)) = <NextExternal<T>>::get() {
ensure!(proposal_hash == e_proposal_hash, Error::<T>::ProposalMissing);
} else {
Err(Error::<T>::NoProposal)?;
}
let mut existing_vetoers = <Blacklist<T>>::get(&proposal_hash)
.map(|pair| pair.1)
.unwrap_or_else(Vec::new);
let insert_position = existing_vetoers.binary_search(&who)
.err().ok_or(Error::<T>::AlreadyVetoed)?;
existing_vetoers.insert(insert_position, who.clone());
let until = <frame_system::Module<T>>::block_number() + T::CooloffPeriod::get();
<Blacklist<T>>::insert(&proposal_hash, (until, existing_vetoers));
Self::deposit_event(RawEvent::Vetoed(who, proposal_hash, until));
<NextExternal<T>>::kill();
}
/// Remove a referendum.
///
/// The dispatch origin of this call must be _Root_.
///
/// - `ref_index`: The index of the referendum to cancel.
///
/// # <weight>
/// - Complexity: `O(1)`.
/// - Db writes: `ReferendumInfoOf`
/// - Base Weight: 21.57 µs
/// # </weight>
#[weight = (22_000_000 + T::DbWeight::get().writes(1), DispatchClass::Operational)]
fn cancel_referendum(origin, #[compact] ref_index: ReferendumIndex) {
ensure_root(origin)?;
Self::internal_cancel_referendum(ref_index);
}
/// Cancel a proposal queued for enactment.
///
/// The dispatch origin of this call must be _Root_.
///
/// - `which`: The index of the referendum to cancel.
///
/// # <weight>
/// - `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`.
/// - Db reads: `scheduler lookup`, scheduler agenda`
/// - Db writes: `scheduler lookup`, scheduler agenda`
/// - Base Weight: 36.78 + 3.277 * D µs
/// # </weight>
#[weight = (68_000_000 + T::DbWeight::get().reads_writes(2, 2), DispatchClass::Operational)]
fn cancel_queued(origin, which: ReferendumIndex) {
ensure_root(origin)?;
T::Scheduler::cancel_named((DEMOCRACY_ID, which).encode())
.map_err(|_| Error::<T>::ProposalMissing)?;
}
/// Weight: see `begin_block`
fn on_initialize(n: T::BlockNumber) -> Weight {
Self::begin_block(n).unwrap_or_else(|e| {
sp_runtime::print(e);
0
})
}
/// Specify a proxy that is already open to us. Called by the stash.
///
/// NOTE: Used to be called `set_proxy`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `proxy`: The account that will be activated as proxy.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`
/// - Db writes: `Proxy`
/// - Base Weight: 7.972 µs
/// # </weight>
#[weight = 8_000_000 + T::DbWeight::get().reads_writes(1, 1)]
fn activate_proxy(origin, proxy: T::AccountId) {
let who = ensure_signed(origin)?;
Proxy::<T>::try_mutate(&proxy, |a| match a.take() {
None => Err(Error::<T>::NotOpen),
Some(ProxyState::Active(_)) => Err(Error::<T>::AlreadyProxy),
Some(ProxyState::Open(x)) if &x == &who => {
*a = Some(ProxyState::Active(who));
Ok(())
}
Some(ProxyState::Open(_)) => Err(Error::<T>::WrongOpen),
})?;
}
/// Clear the proxy. Called by the proxy.
///
/// NOTE: Used to be called `resign_proxy`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`, `sender account`
/// - Db writes: `Proxy`, `sender account`
/// - Base Weight: 15.41 µs
/// # </weight>
#[weight = 16_000_000 + T::DbWeight::get().reads_writes(1, 1)]
fn close_proxy(origin) {
let who = ensure_signed(origin)?;
Proxy::<T>::mutate(&who, |a| {
if a.is_some() {
system::Module::<T>::dec_ref(&who);
}
*a = None;
});
}
/// Deactivate the proxy, but leave open to this account. Called by the stash.
///
/// The proxy must already be active.
///
/// NOTE: Used to be called `remove_proxy`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `proxy`: The account that will be deactivated as proxy.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`
/// - Db writes: `Proxy`
/// - Base Weight: 8.03 µs
/// # </weight>
#[weight = 8_000_000 + T::DbWeight::get().reads_writes(1, 1)]
fn deactivate_proxy(origin, proxy: T::AccountId) {
let who = ensure_signed(origin)?;
Proxy::<T>::try_mutate(&proxy, |a| match a.take() {
None | Some(ProxyState::Open(_)) => Err(Error::<T>::NotActive),
Some(ProxyState::Active(x)) if &x == &who => {
*a = Some(ProxyState::Open(who));
Ok(())
}
Some(ProxyState::Active(_)) => Err(Error::<T>::WrongProxy),
})?;
}
/// Delegate the voting power (with some given conviction) of the sending account.
///
/// The balance delegated is locked for as long as it's delegated, and thereafter for the
/// time appropriate for the conviction's lock period.
///
/// The dispatch origin of this call must be _Signed_, and the signing account must either:
/// - be delegating already; or
/// - have no voting activity (if there is, then it will need to be removed/consolidated
/// through `reap_vote` or `unvote`).
///
/// - `to`: The account whose voting the `target` account's voting power will follow.
/// - `conviction`: The conviction that will be attached to the delegated votes. When the
/// account is undelegated, the funds will be locked for the corresponding period.
/// - `balance`: The amount of the account's balance to be used in delegating. This must
/// not be more than the account's current balance.
///
/// Emits `Delegated`.
///
/// # <weight>
/// - Complexity: `O(R)` where R is the number of referendums the voter delegating to has
/// voted on. Weight is charged as if maximum votes.
/// - Db reads: 2*`VotingOf`, `balances locks`
/// - Db writes: 2*`VotingOf`, `balances locks`
/// - Db reads per votes: `ReferendumInfoOf`
/// - Db writes per votes: `ReferendumInfoOf`
/// - Base Weight: 65.78 + 8.229 * R µs
// NOTE: weight must cover an incorrect voting of origin with 100 votes.
/// # </weight>
#[weight = weight_for::delegate::<T>(T::MaxVotes::get().into())]
pub fn delegate(
origin,
to: T::AccountId,
conviction: Conviction,
balance: BalanceOf<T>
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let votes = Self::try_delegate(who, to, conviction, balance)?;
Ok(Some(weight_for::delegate::<T>(votes.into())).into())
}
/// Undelegate the voting power of the sending account.
///
/// Tokens may be unlocked following once an amount of time consistent with the lock period
/// of the conviction with which the delegation was issued.
///
/// The dispatch origin of this call must be _Signed_ and the signing account must be
/// currently delegating.
///
/// Emits `Undelegated`.
///
/// # <weight>
/// - Complexity: `O(R)` where R is the number of referendums the voter delegating to has
/// voted on. Weight is charged as if maximum votes.
/// - Db reads: 2*`VotingOf`
/// - Db writes: 2*`VotingOf`
/// - Db reads per votes: `ReferendumInfoOf`
/// - Db writes per votes: `ReferendumInfoOf`
/// - Base Weight: 33.29 + 8.104 * R µs
// NOTE: weight must cover an incorrect voting of origin with 100 votes.
/// # </weight>
#[weight = weight_for::undelegate::<T>(T::MaxVotes::get().into())]
fn undelegate(origin) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let votes = Self::try_undelegate(who)?;
Ok(Some(weight_for::undelegate::<T>(votes.into())).into())
}
/// Clears all public proposals.
///
/// The dispatch origin of this call must be _Root_.
///
/// # <weight>
/// - `O(1)`.
/// - Db writes: `PublicProps`
/// - Base Weight: 2.505 µs
/// # </weight>
#[weight = 2_500_000 + T::DbWeight::get().writes(1)]
fn clear_public_proposals(origin) {
ensure_root(origin)?;
<PublicProps<T>>::kill();
}
/// Register the preimage for an upcoming proposal. This doesn't require the proposal to be
/// in the dispatch queue but does require a deposit, returned once enacted.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `encoded_proposal`: The preimage of a proposal.
///
/// Emits `PreimageNoted`.
///
/// # <weight>
/// see `weight_for::note_preimage`
/// # </weight>
#[weight = weight_for::note_preimage::<T>((encoded_proposal.len() as u32).into())]
fn note_preimage(origin, encoded_proposal: Vec<u8>) {
Self::note_preimage_inner(ensure_signed(origin)?, encoded_proposal)?;
}
/// Same as `note_preimage` but origin is `OperationalPreimageOrigin`.
#[weight = (
weight_for::note_preimage::<T>((encoded_proposal.len() as u32).into()),
DispatchClass::Operational,
)]
fn note_preimage_operational(origin, encoded_proposal: Vec<u8>) {
let who = T::OperationalPreimageOrigin::ensure_origin(origin)?;
Self::note_preimage_inner(who, encoded_proposal)?;
}
/// Register the preimage for an upcoming proposal. This requires the proposal to be
/// in the dispatch queue. No deposit is needed.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `encoded_proposal`: The preimage of a proposal.
///
/// Emits `PreimageNoted`.
///
/// # <weight>
/// see `weight_for::note_preimage`
/// # </weight>
#[weight = weight_for::note_imminent_preimage::<T>((encoded_proposal.len() as u32).into())]
fn note_imminent_preimage(origin, encoded_proposal: Vec<u8>) {
Self::note_imminent_preimage_inner(ensure_signed(origin)?, encoded_proposal)?;
}
/// Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`.
#[weight = (
weight_for::note_imminent_preimage::<T>((encoded_proposal.len() as u32).into()),
DispatchClass::Operational,
)]
fn note_imminent_preimage_operational(origin, encoded_proposal: Vec<u8>) {
let who = T::OperationalPreimageOrigin::ensure_origin(origin)?;
Self::note_imminent_preimage_inner(who, encoded_proposal)?;
}
/// Remove an expired proposal preimage and collect the deposit.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `proposal_hash`: The preimage hash of a proposal.
/// - `proposal_length_upper_bound`: an upper bound on length of the proposal.
/// Extrinsic is weighted according to this value with no refund.
///
/// This will only work after `VotingPeriod` blocks from the time that the preimage was
/// noted, if it's the same account doing it. If it's a different account, then it'll only
/// work an additional `EnactmentPeriod` later.
///
/// Emits `PreimageReaped`.
///
/// # <weight>
/// - Complexity: `O(D)` where D is length of proposal.
/// - Db reads: `Preimages`
/// - Db writes: `Preimages`
/// - Base Weight: 39.31 + .003 * b µs
/// # </weight>
#[weight = (39_000_000 + T::DbWeight::get().reads_writes(1, 1))
.saturating_add(3_000.saturating_mul(Weight::from(*proposal_len_upper_bound)))]
fn reap_preimage(origin, proposal_hash: T::Hash, #[compact] proposal_len_upper_bound: u32) {
let who = ensure_signed(origin)?;
ensure!(
Self::pre_image_data_len(proposal_hash)? <= proposal_len_upper_bound,
Error::<T>::WrongUpperBound,
);
let (provider, deposit, since, expiry) = <Preimages<T>>::get(&proposal_hash)
.and_then(|m| match m {
PreimageStatus::Available { provider, deposit, since, expiry, .. }
=> Some((provider, deposit, since, expiry)),
_ => None,
}).ok_or(Error::<T>::PreimageMissing)?;
let now = <frame_system::Module<T>>::block_number();
let (voting, enactment) = (T::VotingPeriod::get(), T::EnactmentPeriod::get());
let additional = if who == provider { Zero::zero() } else { enactment };
ensure!(now >= since + voting + additional, Error::<T>::TooEarly);
ensure!(expiry.map_or(true, |e| now > e), Error::<T>::Imminent);
let _ = T::Currency::repatriate_reserved(&provider, &who, deposit, BalanceStatus::Free);
<Preimages<T>>::remove(&proposal_hash);
Self::deposit_event(RawEvent::PreimageReaped(proposal_hash, provider, deposit, who));
}
/// Unlock tokens that have an expired lock.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `target`: The account to remove the lock on.
///
/// # <weight>
/// - Complexity `O(R)` with R number of vote of target.
/// - Db reads: `VotingOf`, `balances locks`, `target account`
/// - Db writes: `VotingOf`, `balances locks`, `target account`
/// - Base Weight:
/// - Unlock Remove: 42.96 + .048 * R
/// - Unlock Set: 37.63 + .327 * R
/// # </weight>
#[weight = 43_000_000 + 330_000 * Weight::from(T::MaxVotes::get())
+ T::DbWeight::get().reads_writes(3, 3)]
fn unlock(origin, target: T::AccountId) {
ensure_signed(origin)?;
Self::update_lock(&target);
}
/// Become a proxy.
///
/// This must be called prior to a later `activate_proxy`.
///
/// Origin must be a Signed.
///
/// - `target`: The account whose votes will later be proxied.
///
/// `close_proxy` must be called before the account can be destroyed.
///
/// # <weight>
/// - Complexity: O(1)
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `Proxy`, `proxy account`
/// - Base Weight: 14.86 µs
/// # </weight>
#[weight = 15_000_000 + T::DbWeight::get().reads_writes(2, 2)]
fn open_proxy(origin, target: T::AccountId) {
let who = ensure_signed(origin)?;
Proxy::<T>::mutate(&who, |a| {
if a.is_none() {
system::Module::<T>::inc_ref(&who);
}
*a = Some(ProxyState::Open(target));
});
}
/// Remove a vote for a referendum.
///
/// If:
/// - the referendum was cancelled, or
/// - the referendum is ongoing, or
/// - the referendum has ended such that
/// - the vote of the account was in opposition to the result; or
/// - there was no conviction to the account's vote; or
/// - the account made a split vote
/// ...then the vote is removed cleanly and a following call to `unlock` may result in more
/// funds being available.
///
/// If, however, the referendum has ended and:
/// - it finished corresponding to the vote of the account, and
/// - the account made a standard vote with conviction, and
/// - the lock period of the conviction is not over
/// ...then the lock will be aggregated into the overall account's lock, which may involve
/// *overlocking* (where the two locks are combined into a single lock that is the maximum
/// of both the amount locked and the time is it locked for).
///
/// The dispatch origin of this call must be _Signed_, and the signer must have a vote
/// registered for referendum `index`.
///
/// - `index`: The index of referendum of the vote to be removed.
///
/// # <weight>
/// - `O(R + log R)` where R is the number of referenda that `target` has voted on.
/// Weight is calculated for the maximum number of vote.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`
/// - Base Weight: 21.03 + .359 * R
/// # </weight>
#[weight = 21_000_000 + 360_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(2, 2)]
fn remove_vote(origin, index: ReferendumIndex) -> DispatchResult {
let who = ensure_signed(origin)?;
Self::try_remove_vote(&who, index, UnvoteScope::Any)
}
/// Remove a vote for a referendum.
///
/// If the `target` is equal to the signer, then this function is exactly equivalent to
/// `remove_vote`. If not equal to the signer, then the vote must have expired,
/// either because the referendum was cancelled, because the voter lost the referendum or
/// because the conviction period is over.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `target`: The account of the vote to be removed; this account must have voted for
/// referendum `index`.
/// - `index`: The index of referendum of the vote to be removed.
///
/// # <weight>
/// - `O(R + log R)` where R is the number of referenda that `target` has voted on.
/// Weight is calculated for the maximum number of vote.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`
/// - Base Weight: 19.15 + .372 * R
/// # </weight>
#[weight = 19_000_000 + 370_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(2, 2)]
fn remove_other_vote(origin, target: T::AccountId, index: ReferendumIndex) -> DispatchResult {
let who = ensure_signed(origin)?;
let scope = if target == who { UnvoteScope::Any } else { UnvoteScope::OnlyExpired };
Self::try_remove_vote(&target, index, scope)?;
Ok(())
}
/// Delegate the voting power (with some given conviction) of a proxied account.
///
/// The balance delegated is locked for as long as it's delegated, and thereafter for the
/// time appropriate for the conviction's lock period.
///
/// The dispatch origin of this call must be _Signed_, and the signing account must have
/// been set as the proxy account for `target`.
///
/// - `target`: The account whole voting power shall be delegated and whose balance locked.
/// This account must either:
/// - be delegating already; or
/// - have no voting activity (if there is, then it will need to be removed/consolidated
/// through `reap_vote` or `unvote`).
/// - `to`: The account whose voting the `target` account's voting power will follow.
/// - `conviction`: The conviction that will be attached to the delegated votes. When the
/// account is undelegated, the funds will be locked for the corresponding period.
/// - `balance`: The amount of the account's balance to be used in delegating. This must
/// not be more than the account's current balance.
///
/// Emits `Delegated`.
///
/// # <weight>
/// same as `delegate with additional:
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `proxy account`
/// - Base Weight: 68.61 + 8.039 * R µs
/// # </weight>
#[weight = weight_for::proxy_delegate::<T>(T::MaxVotes::get().into())]
pub fn proxy_delegate(origin,
to: T::AccountId,
conviction: Conviction,
balance: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
let votes = Self::try_delegate(target, to, conviction, balance)?;
Ok(Some(weight_for::proxy_delegate::<T>(votes.into())).into())
}
/// Undelegate the voting power of a proxied account.
///
/// Tokens may be unlocked following once an amount of time consistent with the lock period
/// of the conviction with which the delegation was issued.
///
/// The dispatch origin of this call must be _Signed_ and the signing account must be a
/// proxy for some other account which is currently delegating.
///
/// Emits `Undelegated`.
///
/// # <weight>
/// same as `undelegate with additional:
/// Db reads: `Proxy`
/// Base Weight: 39 + 7.958 * R µs
/// # </weight>
#[weight = weight_for::proxy_undelegate::<T>(T::MaxVotes::get().into())]
fn proxy_undelegate(origin) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
let votes = Self::try_undelegate(target)?;
Ok(Some(weight_for::proxy_undelegate::<T>(votes.into())).into())
}
/// Remove a proxied vote for a referendum.
///
/// Exactly equivalent to `remove_vote` except that it operates on the account that the
/// sender is a proxy for.
///
/// The dispatch origin of this call must be _Signed_ and the signing account must be a
/// proxy for some other account which has a registered vote for the referendum of `index`.
///
/// - `index`: The index of referendum of the vote to be removed.
///
/// # <weight>
/// - `O(R + log R)` where R is the number of referenda that `target` has voted on.
/// Weight is calculated for the maximum number of vote.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`, `Proxy`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`
/// - Base Weight: 26.35 + .36 * R µs
/// # </weight>
#[weight = 26_000_000 + 360_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(2, 3)]
fn proxy_remove_vote(origin, index: ReferendumIndex) -> DispatchResult {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
Self::try_remove_vote(&target, index, UnvoteScope::Any)
}
/// Enact a proposal from a referendum. For now we just make the weight be the maximum.
#[weight = T::MaximumBlockWeight::get()]
fn enact_proposal(origin, proposal_hash: T::Hash, index: ReferendumIndex) -> DispatchResult {
ensure_root(origin)?;
Self::do_enact_proposal(proposal_hash, index)
}
}
}
impl<T: Trait> Module<T> {
// exposed immutables.
/// Get the amount locked in support of `proposal`; `None` if proposal isn't a valid proposal
/// index.
pub fn backing_for(proposal: PropIndex) -> Option<BalanceOf<T>> {
Self::deposit_of(proposal).map(|(l, d)| d * (l.len() as u32).into())
}
/// Get all referenda ready for tally at block `n`.
pub fn maturing_referenda_at(
n: T::BlockNumber
) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>)> {
let next = Self::lowest_unbaked();
let last = Self::referendum_count();
Self::maturing_referenda_at_inner(n, next..last)
}
fn maturing_referenda_at_inner(
n: T::BlockNumber,
range: core::ops::Range<PropIndex>,
) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>)> {
range.into_iter()
.map(|i| (i, Self::referendum_info(i)))
.filter_map(|(i, maybe_info)| match maybe_info {
Some(ReferendumInfo::Ongoing(status)) => Some((i, status)),
_ => None,
})
.filter(|(_, status)| status.end == n)
.collect()
}
// Exposed mutables.
#[cfg(feature = "std")]
pub fn force_proxy(stash: T::AccountId, proxy: T::AccountId) {
Proxy::<T>::mutate(&proxy, |o| {
if o.is_none() {
system::Module::<T>::inc_ref(&proxy);
}
*o = Some(ProxyState::Active(stash))
})
}
/// Start a referendum.
pub fn internal_start_referendum(
proposal_hash: T::Hash,
threshold: VoteThreshold,
delay: T::BlockNumber
) -> ReferendumIndex {
<Module<T>>::inject_referendum(
<frame_system::Module<T>>::block_number() + T::VotingPeriod::get(),
proposal_hash,
threshold,
delay
)
}
/// Remove a referendum.
pub fn internal_cancel_referendum(ref_index: ReferendumIndex) {
Self::deposit_event(RawEvent::Cancelled(ref_index));
ReferendumInfoOf::<T>::remove(ref_index);
}
// private.
/// Ok if the given referendum is active, Err otherwise
fn ensure_ongoing(r: ReferendumInfo<T::BlockNumber, T::Hash, BalanceOf<T>>)
-> Result<ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>, DispatchError>
{
match r {
ReferendumInfo::Ongoing(s) => Ok(s),
_ => Err(Error::<T>::ReferendumInvalid.into()),
}
}
fn referendum_status(ref_index: ReferendumIndex)
-> Result<ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>, DispatchError>
{
let info = ReferendumInfoOf::<T>::get(ref_index)
.ok_or(Error::<T>::ReferendumInvalid)?;
Self::ensure_ongoing(info)
}
/// Actually enact a vote, if legit.
fn try_vote(who: &T::AccountId, ref_index: ReferendumIndex, vote: AccountVote<BalanceOf<T>>) -> DispatchResult {
let mut status = Self::referendum_status(ref_index)?;
ensure!(vote.balance() <= T::Currency::free_balance(who), Error::<T>::InsufficientFunds);
VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
if let Voting::Direct { ref mut votes, delegations, .. } = voting {
match votes.binary_search_by_key(&ref_index, |i| i.0) {
Ok(i) => {
// Shouldn't be possible to fail, but we handle it gracefully.
status.tally.remove(votes[i].1).ok_or(Error::<T>::Underflow)?;
if let Some(approve) = votes[i].1.as_standard() {
status.tally.reduce(approve, *delegations);
}
votes[i].1 = vote;
}
Err(i) => {
ensure!(votes.len() as u32 <= T::MaxVotes::get(), Error::<T>::MaxVotesReached);
votes.insert(i, (ref_index, vote));
}
}
// Shouldn't be possible to fail, but we handle it gracefully.
status.tally.add(vote).ok_or(Error::<T>::Overflow)?;
if let Some(approve) = vote.as_standard() {
status.tally.increase(approve, *delegations);
}
Ok(())
} else {
Err(Error::<T>::AlreadyDelegating.into())
}
})?;
// Extend the lock to `balance` (rather than setting it) since we don't know what other
// votes are in place.
T::Currency::extend_lock(
DEMOCRACY_ID,
who,
vote.balance(),
WithdrawReason::Transfer.into()
);
ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
Ok(())
}
/// Remove the account's vote for the given referendum if possible. This is possible when:
/// - The referendum has not finished.
/// - The referendum has finished and the voter lost their direction.
/// - The referendum has finished and the voter's lock period is up.
///
/// This will generally be combined with a call to `unlock`.
fn try_remove_vote(who: &T::AccountId, ref_index: ReferendumIndex, scope: UnvoteScope) -> DispatchResult {
let info = ReferendumInfoOf::<T>::get(ref_index);
VotingOf::<T>::try_mutate(who, |voting| -> DispatchResult {
if let Voting::Direct { ref mut votes, delegations, ref mut prior } = voting {
let i = votes.binary_search_by_key(&ref_index, |i| i.0).map_err(|_| Error::<T>::NotVoter)?;
match info {
Some(ReferendumInfo::Ongoing(mut status)) => {
ensure!(matches!(scope, UnvoteScope::Any), Error::<T>::NoPermission);
// Shouldn't be possible to fail, but we handle it gracefully.
status.tally.remove(votes[i].1).ok_or(Error::<T>::Underflow)?;
if let Some(approve) = votes[i].1.as_standard() {
status.tally.reduce(approve, *delegations);
}
ReferendumInfoOf::<T>::insert(ref_index, ReferendumInfo::Ongoing(status));
}
Some(ReferendumInfo::Finished{end, approved}) =>
if let Some((lock_periods, balance)) = votes[i].1.locked_if(approved) {
let unlock_at = end + T::EnactmentPeriod::get() * lock_periods.into();
let now = system::Module::<T>::block_number();
if now < unlock_at {
ensure!(matches!(scope, UnvoteScope::Any), Error::<T>::NoPermission);
prior.accumulate(unlock_at, balance)
}
},
None => {} // Referendum was cancelled.
}
votes.remove(i);
}
Ok(())
})?;
Ok(())
}
/// Return the number of votes for `who`
fn increase_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
VotingOf::<T>::mutate(who, |voting| match voting {
Voting::Delegating { delegations, .. } => {
// We don't support second level delegating, so we don't need to do anything more.
*delegations = delegations.saturating_add(amount);
1
},
Voting::Direct { votes, delegations, .. } => {
*delegations = delegations.saturating_add(amount);
for &(ref_index, account_vote) in votes.iter() {
if let AccountVote::Standard { vote, .. } = account_vote {
ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info|
if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
status.tally.increase(vote.aye, amount);
}
);
}
}
votes.len() as u32
}
})
}
/// Return the number of votes for `who`
fn reduce_upstream_delegation(who: &T::AccountId, amount: Delegations<BalanceOf<T>>) -> u32 {
VotingOf::<T>::mutate(who, |voting| match voting {
Voting::Delegating { delegations, .. } => {
// We don't support second level delegating, so we don't need to do anything more.
*delegations = delegations.saturating_sub(amount);
1
}
Voting::Direct { votes, delegations, .. } => {
*delegations = delegations.saturating_sub(amount);
for &(ref_index, account_vote) in votes.iter() {
if let AccountVote::Standard { vote, .. } = account_vote {
ReferendumInfoOf::<T>::mutate(ref_index, |maybe_info|
if let Some(ReferendumInfo::Ongoing(ref mut status)) = maybe_info {
status.tally.reduce(vote.aye, amount);
}
);
}
}
votes.len() as u32
}
})
}
/// Attempt to delegate `balance` times `conviction` of voting power from `who` to `target`.
///
/// Return the upstream number of votes.
fn try_delegate(
who: T::AccountId,
target: T::AccountId,
conviction: Conviction,
balance: BalanceOf<T>,
) -> Result<u32, DispatchError> {
ensure!(who != target, Error::<T>::Nonsense);
ensure!(balance <= T::Currency::free_balance(&who), Error::<T>::InsufficientFunds);
let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
let mut old = Voting::Delegating {
balance,
target: target.clone(),
conviction,
delegations: Default::default(),
prior: Default::default(),
};
sp_std::mem::swap(&mut old, voting);
match old {
Voting::Delegating { balance, target, conviction, delegations, prior, .. } => {
// remove any delegation votes to our current target.
Self::reduce_upstream_delegation(&target, conviction.votes(balance));
voting.set_common(delegations, prior);
}
Voting::Direct { votes, delegations, prior } => {
// here we just ensure that we're currently idling with no votes recorded.
ensure!(votes.is_empty(), Error::<T>::VotesExist);
voting.set_common(delegations, prior);
}
}
let votes = Self::increase_upstream_delegation(&target, conviction.votes(balance));
// Extend the lock to `balance` (rather than setting it) since we don't know what other
// votes are in place.
T::Currency::extend_lock(
DEMOCRACY_ID,
&who,
balance,
WithdrawReason::Transfer.into()
);
Ok(votes)
})?;
Self::deposit_event(Event::<T>::Delegated(who, target));
Ok(votes)
}
/// Attempt to end the current delegation.
///
/// Return the number of votes of upstream.
fn try_undelegate(who: T::AccountId) -> Result<u32, DispatchError> {
let votes = VotingOf::<T>::try_mutate(&who, |voting| -> Result<u32, DispatchError> {
let mut old = Voting::default();
sp_std::mem::swap(&mut old, voting);
match old {
Voting::Delegating {
balance,
target,
conviction,
delegations,
mut prior,
} => {
// remove any delegation votes to our current target.
let votes = Self::reduce_upstream_delegation(&target, conviction.votes(balance));
let now = system::Module::<T>::block_number();
let lock_periods = conviction.lock_periods().into();
prior.accumulate(now + T::EnactmentPeriod::get() * lock_periods, balance);
voting.set_common(delegations, prior);
Ok(votes)
}
Voting::Direct { .. } => {
Err(Error::<T>::NotDelegating.into())
}
}
})?;
Self::deposit_event(Event::<T>::Undelegated(who));
Ok(votes)
}
/// Rejig the lock on an account. It will never get more stringent (since that would indicate
/// a security hole) but may be reduced from what they are currently.
fn update_lock(who: &T::AccountId) {
let lock_needed = VotingOf::<T>::mutate(who, |voting| {
voting.rejig(system::Module::<T>::block_number());
voting.locked_balance()
});
if lock_needed.is_zero() {
T::Currency::remove_lock(DEMOCRACY_ID, who);
} else {
T::Currency::set_lock(DEMOCRACY_ID, who, lock_needed, WithdrawReason::Transfer.into());
}
}
/// Start a referendum
fn inject_referendum(
end: T::BlockNumber,
proposal_hash: T::Hash,
threshold: VoteThreshold,
delay: T::BlockNumber,
) -> ReferendumIndex {
let ref_index = Self::referendum_count();
ReferendumCount::put(ref_index + 1);
let status = ReferendumStatus { end, proposal_hash, threshold, delay, tally: Default::default() };
let item = ReferendumInfo::Ongoing(status);
<ReferendumInfoOf<T>>::insert(ref_index, item);
Self::deposit_event(RawEvent::Started(ref_index, threshold));
ref_index
}
/// Table the next waiting proposal for a vote.
fn launch_next(now: T::BlockNumber) -> DispatchResult {
if LastTabledWasExternal::take() {
Self::launch_public(now).or_else(|_| Self::launch_external(now))
} else {
Self::launch_external(now).or_else(|_| Self::launch_public(now))
}.map_err(|_| Error::<T>::NoneWaiting.into())
}
/// Table the waiting external proposal for a vote, if there is one.
fn launch_external(now: T::BlockNumber) -> DispatchResult {
if let Some((proposal, threshold)) = <NextExternal<T>>::take() {
LastTabledWasExternal::put(true);
Self::deposit_event(RawEvent::ExternalTabled);
Self::inject_referendum(
now + T::VotingPeriod::get(),
proposal,
threshold,
T::EnactmentPeriod::get(),
);
Ok(())
} else {
Err(Error::<T>::NoneWaiting)?
}
}
/// Table the waiting public proposal with the highest backing for a vote.
fn launch_public(now: T::BlockNumber) -> DispatchResult {
let mut public_props = Self::public_props();
if let Some((winner_index, _)) = public_props.iter()
.enumerate()
.max_by_key(|x| Self::backing_for((x.1).0).unwrap_or_else(Zero::zero)
/* ^^ defensive only: All current public proposals have an amount locked*/)
{
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
<PublicProps<T>>::put(public_props);
if let Some((depositors, deposit)) = <DepositOf<T>>::take(prop_index) {
// refund depositors
for d in &depositors {
T::Currency::unreserve(d, deposit);
}
Self::deposit_event(RawEvent::Tabled(prop_index, deposit, depositors));
Self::inject_referendum(
now + T::VotingPeriod::get(),
proposal,
VoteThreshold::SuperMajorityApprove,
T::EnactmentPeriod::get(),
);
}
Ok(())
} else {
Err(Error::<T>::NoneWaiting)?
}
}
fn do_enact_proposal(proposal_hash: T::Hash, index: ReferendumIndex) -> DispatchResult {
let preimage = <Preimages<T>>::take(&proposal_hash);
if let Some(PreimageStatus::Available { data, provider, deposit, .. }) = preimage {
if let Ok(proposal) = T::Proposal::decode(&mut &data[..]) {
let _ = T::Currency::unreserve(&provider, deposit);
Self::deposit_event(RawEvent::PreimageUsed(proposal_hash, provider, deposit));
let ok = proposal.dispatch(frame_system::RawOrigin::Root.into()).is_ok();
Self::deposit_event(RawEvent::Executed(index, ok));
Ok(())
} else {
T::Slash::on_unbalanced(T::Currency::slash_reserved(&provider, deposit).0);
Self::deposit_event(RawEvent::PreimageInvalid(proposal_hash, index));
Err(Error::<T>::PreimageInvalid.into())
}
} else {
Self::deposit_event(RawEvent::PreimageMissing(proposal_hash, index));
Err(Error::<T>::PreimageMissing.into())
}
}
fn bake_referendum(
now: T::BlockNumber,
index: ReferendumIndex,
status: ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>,
) -> Result<bool, DispatchError> {
let total_issuance = T::Currency::total_issuance();
let approved = status.threshold.approved(status.tally, total_issuance);
if approved {
Self::deposit_event(RawEvent::Passed(index));
if status.delay.is_zero() {
let _ = Self::do_enact_proposal(status.proposal_hash, index);
} else {
let when = now + status.delay;
// Note that we need the preimage now.
Preimages::<T>::mutate_exists(&status.proposal_hash, |maybe_pre| match *maybe_pre {
Some(PreimageStatus::Available { ref mut expiry, .. }) => *expiry = Some(when),
ref mut a => *a = Some(PreimageStatus::Missing(when)),
});
if T::Scheduler::schedule_named(
(DEMOCRACY_ID, index).encode(),
when,
None,
63,
Call::enact_proposal(status.proposal_hash, index).into(),
).is_err() {
frame_support::print("LOGIC ERROR: bake_referendum/schedule_named failed");
}
}
} else {
Self::deposit_event(RawEvent::NotPassed(index));
}
Ok(approved)
}
/// Current era is ending; we should finish up any proposals.
///
///
/// # <weight>
/// If a referendum is launched or maturing take full block weight. Otherwise:
/// - Complexity: `O(R)` where `R` is the number of unbaked referenda.
/// - Db reads: `LastTabledWasExternal`, `NextExternal`, `PublicProps`, `account`,
/// `ReferendumCount`, `LowestUnbaked`
/// - Db writes: `PublicProps`, `account`, `ReferendumCount`, `DepositOf`, `ReferendumInfoOf`
/// - Db reads per R: `DepositOf`, `ReferendumInfoOf`
/// - Base Weight: 58.58 + 10.9 * R µs
/// # </weight>
fn begin_block(now: T::BlockNumber) -> Result<Weight, DispatchError> {
let mut weight = 60_000_000 + T::DbWeight::get().reads_writes(6, 5);
// pick out another public referendum if it's time.
if (now % T::LaunchPeriod::get()).is_zero() {
// Errors come from the queue being empty. we don't really care about that, and even if
// we did, there is nothing we can do here.
let _ = Self::launch_next(now);
weight = T::MaximumBlockWeight::get();
}
// tally up votes for any expiring referenda.
let next = Self::lowest_unbaked();
let last = Self::referendum_count();
let r = Weight::from(last.saturating_sub(next));
weight += 11_000_000 * r + T::DbWeight::get().reads(2 * r);
for (index, info) in Self::maturing_referenda_at_inner(now, next..last).into_iter() {
let approved = Self::bake_referendum(now, index, info)?;
ReferendumInfoOf::<T>::insert(index, ReferendumInfo::Finished { end: now, approved });
weight = T::MaximumBlockWeight::get();
}
Ok(weight)
}
/// Reads the length of account in DepositOf without getting the complete value in the runtime.
///
/// Return 0 if no deposit for this proposal.
fn len_of_deposit_of(proposal: PropIndex) -> Option<u32> {
// DepositOf first tuple element is a vec, decoding its len is equivalent to decode a
// `Compact<u32>`.
decode_compact_u32_at(&<DepositOf<T>>::hashed_key_for(proposal))
}
/// Check that pre image exists and its value is variant `PreimageStatus::Missing`.
///
/// This check is done without getting the complete value in the runtime to avoid copying a big
/// value in the runtime.
fn check_pre_image_is_missing(proposal_hash: T::Hash) -> DispatchResult {
// To decode the enum variant we only need the first byte.
let mut buf = [0u8; 1];
let key = <Preimages<T>>::hashed_key_for(proposal_hash);
let bytes = match sp_io::storage::read(&key, &mut buf, 0) {
Some(bytes) => bytes,
None => return Err(Error::<T>::NotImminent.into()),
};
// The value may be smaller that 1 byte.
let mut input = &buf[0..buf.len().min(bytes as usize)];
match input.read_byte() {
Ok(0) => Ok(()), // PreimageStatus::Missing is variant 0
Ok(1) => Err(Error::<T>::DuplicatePreimage.into()),
_ => {
sp_runtime::print("Failed to decode `PreimageStatus` variant");
Err(Error::<T>::NotImminent.into())
}
}
}
/// Check that pre image exists, its value is variant `PreimageStatus::Available` and decode
/// the length of `data: Vec<u8>` fields.
///
/// This check is done without getting the complete value in the runtime to avoid copying a big
/// value in the runtime.
///
/// If the pre image is missing variant or doesn't exist then the error `PreimageMissing` is
/// returned.
fn pre_image_data_len(proposal_hash: T::Hash) -> Result<u32, DispatchError> {
// To decode the `data` field of Available variant we need:
// * one byte for the variant
// * at most 5 bytes to decode a `Compact<u32>`
let mut buf = [0u8; 6];
let key = <Preimages<T>>::hashed_key_for(proposal_hash);
let bytes = match sp_io::storage::read(&key, &mut buf, 0) {
Some(bytes) => bytes,
None => return Err(Error::<T>::PreimageMissing.into()),
};
// The value may be smaller that 6 bytes.
let mut input = &buf[0..buf.len().min(bytes as usize)];
match input.read_byte() {
Ok(1) => (), // Check that input exists and is second variant.
Ok(0) => return Err(Error::<T>::PreimageMissing.into()),
_ => {
sp_runtime::print("Failed to decode `PreimageStatus` variant");
return Err(Error::<T>::PreimageMissing.into());
}
}
// Decode the length of the vector.
let len = codec::Compact::<u32>::decode(&mut input).map_err(|_| {
sp_runtime::print("Failed to decode `PreimageStatus` variant");
DispatchError::from(Error::<T>::PreimageMissing)
})?.0;
Ok(len)
}
// See `note_preimage`
fn note_preimage_inner(who: T::AccountId, encoded_proposal: Vec<u8>) -> DispatchResult {
let proposal_hash = T::Hashing::hash(&encoded_proposal[..]);
ensure!(!<Preimages<T>>::contains_key(&proposal_hash), Error::<T>::DuplicatePreimage);
let deposit = <BalanceOf<T>>::from(encoded_proposal.len() as u32)
.saturating_mul(T::PreimageByteDeposit::get());
T::Currency::reserve(&who, deposit)?;
let now = <frame_system::Module<T>>::block_number();
let a = PreimageStatus::Available {
data: encoded_proposal,
provider: who.clone(),
deposit,
since: now,
expiry: None,
};
<Preimages<T>>::insert(proposal_hash, a);
Self::deposit_event(RawEvent::PreimageNoted(proposal_hash, who, deposit));
Ok(())
}
// See `note_imminent_preimage`
fn note_imminent_preimage_inner(who: T::AccountId, encoded_proposal: Vec<u8>) -> DispatchResult {
let proposal_hash = T::Hashing::hash(&encoded_proposal[..]);
Self::check_pre_image_is_missing(proposal_hash)?;
let status = Preimages::<T>::get(&proposal_hash).ok_or(Error::<T>::NotImminent)?;
let expiry = status.to_missing_expiry().ok_or(Error::<T>::DuplicatePreimage)?;
let now = <frame_system::Module<T>>::block_number();
let free = <BalanceOf<T>>::zero();
let a = PreimageStatus::Available {
data: encoded_proposal,
provider: who.clone(),
deposit: Zero::zero(),
since: now,
expiry: Some(expiry),
};
<Preimages<T>>::insert(proposal_hash, a);
Self::deposit_event(RawEvent::PreimageNoted(proposal_hash, who, free));
Ok(())
}
}
/// Decode `Compact<u32>` from the trie at given key.
fn decode_compact_u32_at(key: &[u8]) -> Option<u32> {
// `Compact<u32>` takes at most 5 bytes.
let mut buf = [0u8; 5];
let bytes = match sp_io::storage::read(&key, &mut buf, 0) {
Some(bytes) => bytes,
None => return None,
};
// The value may be smaller than 5 bytes.
let mut input = &buf[0..buf.len().min(bytes as usize)];
match codec::Compact::<u32>::decode(&mut input) {
Ok(c) => Some(c.0),
Err(_) => {
sp_runtime::print("Failed to decode compact u32 at:");
sp_runtime::print(key);
None
}
}
}