diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs index f6c41ff805..43a636f489 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs @@ -16,6 +16,13 @@ //! Governance system: Handles administration and dispatch of sensitive operations including //! setting new code, minting new tokens and changing parameters. +//! +//! For now this is limited to a simple qualified majority vote (whose parameter is retrieved from +//! storage) between validators. A single vote may be proposed per era, and at most one approval +//! vote may be cast by each validator. The tally is maintained +//! +//! +//! use runtime_support::Vec; use keyedvec::KeyedVec; @@ -34,27 +41,29 @@ pub fn propose(transactor: &AccountID, proposal: &Proposal) { approve(transactor, staking::current_era()); } -pub fn approve(transactor: &AccountID, era_index: BlockNumber) { +pub fn approve(validator: &AccountID, era_index: BlockNumber) { if era_index != staking::current_era() { panic!("approval vote applied on non-current era.") } if Proposal::lookup(b"gov:pro").is_none() { panic!("there must be a proposal in order to approve."); } - let key = transactor.to_keyed_vec(b"gov:app:"); + if session::validators().into_iter().position(|v| &v == validator).is_none() { + panic!("transactor must be a validator to approve."); + } + let key = validator.to_keyed_vec(b"gov:app:"); if bool::lookup(&key).is_some() { panic!("transactor may not approve a proposal twice in one era."); } true.store(&key); - (approval_count() + 1).store(b"gov:app"); +} + +pub fn set_approval_ppm_required(ppm: u32) { + ppm.store(b"gov:apr"); } // INSPECTION API -pub fn approval_count() -> u32 { - Storable::lookup_default(b"gov:app") -} - pub fn approval_ppm_required() -> u32 { Storable::lookup(b"gov:apr").unwrap_or(1000) } @@ -69,12 +78,11 @@ pub fn approvals_required() -> u32 { pub fn end_of_an_era() { // tally up votes for the current proposal, if any. enact if there are sufficient approvals. if let Some(proposal) = Proposal::lookup(b"gov:pro") { - let enact = approval_count() >= approvals_required(); - - // clear proposal - reset_proposal(); - - if enact { + kill(b"gov:pro"); + let approved: u32 = session::validators().into_iter() + .map(|v| bool::take(&v.to_keyed_vec(b"gov:app:")).map(|_| 1).unwrap_or(0)) + .sum(); + if approved >= approvals_required() { proposal.enact(); } } @@ -82,14 +90,6 @@ pub fn end_of_an_era() { // PRIVATE API -fn reset_proposal() { - session::validators().into_iter().for_each(|v| { - kill(&v.to_keyed_vec(b"gov:app:")); - }); - kill(b"gov:pro"); - kill(b"gov:app"); -} - #[cfg(test)] mod tests { // TODO diff --git a/substrate/wasm-runtime/polkadot/src/support/proposal.rs b/substrate/wasm-runtime/polkadot/src/support/proposal.rs index bffd7aa76c..963a01b749 100644 --- a/substrate/wasm-runtime/polkadot/src/support/proposal.rs +++ b/substrate/wasm-runtime/polkadot/src/support/proposal.rs @@ -21,7 +21,7 @@ use runtime_support::size_of; use slicable::Slicable; use joiner::Joiner; use streamreader::StreamReader; -use runtime::staking; +use runtime::{system, governance, staking}; /// Internal functions that can be dispatched to. #[cfg_attr(test, derive(PartialEq, Debug))] @@ -29,6 +29,7 @@ use runtime::staking; pub enum InternalFunction { SystemSetCode, StakingSetSessionsPerEra, + GovernanceSetApprovalPpmRequired, } impl InternalFunction { @@ -37,6 +38,7 @@ impl InternalFunction { match value { x if x == InternalFunction::SystemSetCode as u8 => Some(InternalFunction::SystemSetCode), x if x == InternalFunction::StakingSetSessionsPerEra as u8 => Some(InternalFunction::StakingSetSessionsPerEra), + x if x == InternalFunction::GovernanceSetApprovalPpmRequired as u8 => Some(InternalFunction::GovernanceSetApprovalPpmRequired), _ => None, } } @@ -77,13 +79,17 @@ impl Proposal { let mut params = StreamReader::new(&self.input_data); match self.function { InternalFunction::SystemSetCode => { - let code = params.read().unwrap(); - staking::set_sessions_per_era(code); + let code: Vec = params.read().unwrap(); + system::set_code(&code); } InternalFunction::StakingSetSessionsPerEra => { let value = params.read().unwrap(); staking::set_sessions_per_era(value); } + InternalFunction::GovernanceSetApprovalPpmRequired => { + let value = params.read().unwrap(); + governance::set_approval_ppm_required(value); + } } } } diff --git a/substrate/wasm-runtime/polkadot/src/support/storable.rs b/substrate/wasm-runtime/polkadot/src/support/storable.rs index 5cfa154937..61d9f080d9 100644 --- a/substrate/wasm-runtime/polkadot/src/support/storable.rs +++ b/substrate/wasm-runtime/polkadot/src/support/storable.rs @@ -32,6 +32,16 @@ pub trait Storable { unimplemented!() } + /// Retrives and returns the serialised value of a key from storage, removing it immediately. + fn take(key: &[u8]) -> Option where Self: Sized { + if let Some(value) = Self::lookup(key) { + kill(key); + Some(value) + } else { + None + } + } + /// Place the value in storage under `key`. fn store(&self, key: &[u8]); }