// Copyright 2017-2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! Democratic system: Handles administration of general stakeholder voting.
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(test)]
extern crate substrate_primitives;
#[macro_use]
extern crate parity_codec_derive;
extern crate sr_std as rstd;
#[macro_use]
extern crate srml_support;
extern crate parity_codec as codec;
extern crate sr_io as runtime_io;
extern crate sr_primitives as primitives;
extern crate srml_balances as balances;
extern crate srml_system as system;
use rstd::prelude::*;
use rstd::result;
use primitives::traits::{Zero, As};
use srml_support::{StorageValue, StorageMap, Parameter, Dispatchable, IsSubType};
use srml_support::dispatch::Result;
use system::ensure_signed;
mod vote_threshold;
pub use vote_threshold::{Approved, VoteThreshold};
/// A proposal index.
pub type PropIndex = u32;
/// A referendum index.
pub type ReferendumIndex = u32;
/// A number of lock periods.
pub type LockPeriods = i8;
/// A number of lock periods, plus a vote, one way or the other.
#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Default)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct Vote(i8);
impl Vote {
/// Create a new instance.
pub fn new(aye: bool, multiplier: LockPeriods) -> Self {
let m = multiplier.max(1) - 1;
Vote(if aye {
-1 - m
} else {
m
})
}
/// Is this an aye vote?
pub fn is_aye(self) -> bool {
self.0 < 0
}
/// The strength (measured in lock periods).
pub fn multiplier(self) -> LockPeriods {
1 + if self.0 < 0 { -(self.0 + 1) } else { self.0 }
}
}
pub trait Trait: balances::Trait + Sized {
type Proposal: Parameter + Dispatchable + IsSubType>;
type Event: From> + Into<::Event>;
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin {
fn deposit_event() = default;
/// Propose a sensitive action to be taken.
fn propose(
origin,
proposal: Box,
#[compact] value: T::Balance
) {
let who = ensure_signed(origin)?;
ensure!(value >= Self::minimum_deposit(), "value too low");
>::reserve(&who, value)
.map_err(|_| "proposer's balance too low")?;
let index = Self::public_prop_count();
>::put(index + 1);
>::insert(index, (value, vec![who.clone()]));
let mut props = Self::public_props();
props.push((index, (*proposal).clone(), who));
>::put(props);
}
/// Propose a sensitive action to be taken.
fn second(origin, #[compact] proposal: PropIndex) {
let who = ensure_signed(origin)?;
let mut deposit = Self::deposit_of(proposal)
.ok_or("can only second an existing proposal")?;
>::reserve(&who, deposit.0)
.map_err(|_| "seconder's balance too low")?;
deposit.1.push(who);
>::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.
fn vote(origin, #[compact] ref_index: ReferendumIndex, vote: Vote) {
let who = ensure_signed(origin)?;
ensure!(vote.multiplier() <= Self::max_lock_periods(), "vote has too great a strength");
ensure!(Self::is_active_referendum(ref_index), "vote given for invalid referendum.");
ensure!(!>::total_balance(&who).is_zero(),
"transactor must have balance to signal approval.");
if !>::exists(&(ref_index, who.clone())) {
>::mutate(ref_index, |voters| voters.push(who.clone()));
}
>::insert(&(ref_index, who), vote);
}
/// Start a referendum.
fn start_referendum(proposal: Box, threshold: VoteThreshold, delay: T::BlockNumber) -> Result {
Self::inject_referendum(
>::block_number() + Self::voting_period(),
*proposal,
threshold,
delay,
).map(|_| ())
}
/// Remove a referendum.
fn cancel_referendum(#[compact] ref_index: ReferendumIndex) {
Self::clear_referendum(ref_index);
}
/// Cancel a proposal queued for enactment.
pub fn cancel_queued(#[compact] when: T::BlockNumber, #[compact] which: u32) -> Result {
let which = which as usize;
>::mutate(when, |items| if items.len() > which { items[which] = None });
Ok(())
}
fn on_finalise(n: T::BlockNumber) {
if let Err(e) = Self::end_block(n) {
runtime_io::print(e);
}
}
}
}
/// Info regarding an ongoing referendum.
#[derive(Encode, Decode, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct ReferendumInfo {
/// When voting on this referendum will end.
end: BlockNumber,
/// The proposal being voted on.
proposal: Proposal,
/// The thresholding mechanism to determine whether it passed.
threshold: VoteThreshold,
/// The delay (in blocks) to wait after a successful referendum before deploying.
delay: BlockNumber,
}
impl ReferendumInfo {
/// Create a new instance.
pub fn new(end: BlockNumber, proposal: Proposal, threshold: VoteThreshold, delay: BlockNumber) -> Self {
ReferendumInfo { end, proposal, threshold, delay }
}
}
decl_storage! {
trait Store for Module as Democracy {
/// The number of (public) proposals that have been made so far.
pub PublicPropCount get(public_prop_count) build(|_| 0 as PropIndex) : PropIndex;
/// The public proposals. Unsorted.
pub PublicProps get(public_props): Vec<(PropIndex, T::Proposal, T::AccountId)>;
/// Those who have locked a deposit.
pub DepositOf get(deposit_of): map PropIndex => Option<(T::Balance, Vec)>;
/// How often (in blocks) new public referenda are launched.
pub LaunchPeriod get(launch_period) config(): T::BlockNumber = T::BlockNumber::sa(1000);
/// The minimum amount to be used as a deposit for a public referendum proposal.
pub MinimumDeposit get(minimum_deposit) config(): T::Balance;
/// The delay before enactment for all public referenda.
pub PublicDelay get(public_delay) config(): T::BlockNumber;
/// The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`.
pub MaxLockPeriods get(max_lock_periods) config(): LockPeriods;
/// How often (in blocks) to check for new votes.
pub VotingPeriod get(voting_period) config(): T::BlockNumber = T::BlockNumber::sa(1000);
/// The next free referendum index, aka the number of referendums started so far.
pub ReferendumCount get(referendum_count) build(|_| 0 as ReferendumIndex): ReferendumIndex;
/// The next referendum index that should be tallied.
pub NextTally get(next_tally) build(|_| 0 as ReferendumIndex): ReferendumIndex;
/// Information concerning any given referendum.
pub ReferendumInfoOf get(referendum_info): map ReferendumIndex => Option<(ReferendumInfo)>;
/// Queue of successful referenda to be dispatched.
pub DispatchQueue get(dispatch_queue): map T::BlockNumber => Vec