Simple governance subsytem.

This commit is contained in:
Gav
2018-01-21 22:35:25 +01:00
parent 6303c7900d
commit 2789bc2b8f
15 changed files with 360 additions and 83 deletions
@@ -18,9 +18,9 @@
use primitives::AccountID;
use streamreader::StreamReader;
use runtime::{staking, session, timestamp};
use runtime::{staking, session, timestamp, governance};
/// The functions that a transaction can call (and be dispatched to).
/// Public functions that can be dispatched to.
#[cfg_attr(test, derive(PartialEq, Debug))]
#[derive(Clone, Copy)]
pub enum Function {
@@ -29,6 +29,8 @@ pub enum Function {
StakingTransfer,
SessionSetKey,
TimestampSet,
GovernancePropose,
GovernanceApprove,
}
impl Function {
@@ -40,6 +42,8 @@ impl Function {
x if x == Function::StakingTransfer as u8 => Some(Function::StakingTransfer),
x if x == Function::SessionSetKey as u8 => Some(Function::SessionSetKey),
x if x == Function::TimestampSet as u8 => Some(Function::TimestampSet),
x if x == Function::GovernancePropose as u8 => Some(Function::GovernancePropose),
x if x == Function::GovernanceApprove as u8 => Some(Function::GovernanceApprove),
_ => None,
}
}
@@ -69,6 +73,14 @@ impl Function {
let t = params.read().unwrap();
timestamp::set(t);
}
Function::GovernancePropose => {
let proposal = params.read().unwrap();
governance::propose(transactor, &proposal);
}
Function::GovernanceApprove => {
let era_index = params.read().unwrap();
governance::approve(transactor, era_index);
}
}
}
}
@@ -18,6 +18,7 @@
pub mod primitives;
pub mod function;
pub mod proposal;
pub mod environment;
pub mod storable;
@@ -28,15 +28,25 @@ use std::fmt;
/// The Ed25519 pubkey that identifies an account.
pub type AccountID = [u8; 32];
/// Virtual account ID that represents the idea of a dispatch/statement being signed by everybody
/// (who matters). Essentially this means that a majority of validators have decided it is
/// "correct".
pub const EVERYBODY: AccountID = [255u8; 32];
/// The Ed25519 pub key of an session that belongs to an authority. This is used as what the
/// external environment/consensus algorithm calls an "authority".
pub type SessionKey = AccountID;
/// Indentifier for a chain.
pub type ChainID = u64;
/// Index of a block in the chain.
pub type BlockNumber = u64;
/// Index of a transaction.
pub type TxOrder = u64;
/// A hash of some data.
pub type Hash = [u8; 32];
@@ -76,7 +86,7 @@ impl Slicable for Header {
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
@@ -122,7 +132,7 @@ impl Slicable for Transaction {
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
@@ -200,7 +210,7 @@ impl Slicable for UncheckedTransaction {
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
@@ -237,7 +247,7 @@ impl Slicable for Block {
})
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
@@ -271,7 +281,7 @@ impl<T: NonTrivialSlicable> Slicable for Vec<T> {
Some(r)
}
fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(_fill_slice: &F) -> Option<Self> {
unimplemented!();
}
@@ -0,0 +1,108 @@
// Copyright 2017 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot 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.
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Proposal: This describes a combination of a function ID and data that can be used to call into
//! an internal function.
use runtime_support::size_of;
use slicable::Slicable;
use joiner::Joiner;
use streamreader::StreamReader;
use runtime::staking;
/// Internal functions that can be dispatched to.
#[cfg_attr(test, derive(PartialEq, Debug))]
#[derive(Clone, Copy)]
pub enum InternalFunction {
SystemSetCode,
StakingSetSessionsPerEra,
}
impl InternalFunction {
/// Derive `Some` value from a `u8`, or `None` if it's invalid.
pub fn from_u8(value: u8) -> Option<InternalFunction> {
match value {
x if x == InternalFunction::SystemSetCode as u8 => Some(InternalFunction::SystemSetCode),
x if x == InternalFunction::StakingSetSessionsPerEra as u8 => Some(InternalFunction::StakingSetSessionsPerEra),
_ => None,
}
}
}
/// An internal function.
#[cfg_attr(test, derive(PartialEq, Debug))]
pub struct Proposal {
/// The priviledged function to call.
pub function: InternalFunction,
/// The serialised data to call it with.
pub input_data: Vec<u8>,
}
impl Slicable for Proposal {
fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(fill_slice: &F) -> Option<Self> {
Some(Proposal {
function: InternalFunction::from_u8(Slicable::set_as_slice(fill_slice)?)?,
input_data: Slicable::set_as_slice(&|s, o| fill_slice(s, o + 1))?,
})
}
fn to_vec(&self) -> Vec<u8> {
Vec::new()
.join(&(self.function as u8))
.join(&self.input_data)
}
fn size_of(data: &[u8]) -> Option<usize> {
let first_part = size_of::<u8>();
let second_part = <Vec<u8>>::size_of(&data[first_part..])?;
Some(first_part + second_part)
}
}
impl Proposal {
pub fn enact(&self) {
let mut params = StreamReader::new(&self.input_data);
match self.function {
InternalFunction::SystemSetCode => {
let code = params.read().unwrap();
staking::set_sessions_per_era(code);
}
InternalFunction::StakingSetSessionsPerEra => {
let value = params.read().unwrap();
staking::set_sessions_per_era(value);
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use statichex::StaticHexInto;
#[test]
fn slicing_should_work() {
let p = Proposal {
function: InternalFunction::SystemSetCode,
input_data: b"Hello world".to_vec(),
};
let v = p.to_vec();
assert_eq!(v, "000b00000048656c6c6f20776f726c64".convert::<Vec<u8>>());
let o = Proposal::from_slice(&v).unwrap();
assert_eq!(p, o);
}
}
@@ -36,7 +36,16 @@ macro_rules! impl_sizes {
)* }
}
impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128);
impl StaticHexConversion for Vec<u8> {
fn from_static_hex(hex: &'static str) -> Self {
FromHex::from_hex(hex).unwrap()
}
}
impl_sizes!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 451, 42, 43, 44, 45, 46, 47, 48,
56, 64, 80, 96, 112, 128);
/// Trait to allow converting from itself (only implemented for a static str) into some useful
/// type (which must implement `StaticHexConversion`).
@@ -17,28 +17,37 @@
//! Stuff to do with the runtime's storage.
use slicable::Slicable;
use endiansensitive::EndianSensitive;
use keyedvec::KeyedVec;
use runtime_support::{self, twox_128, Vec};
/// Trait for a value which may be stored in the storage DB.
pub trait Storable {
/// Lookup the value in storage and deserialise, giving a default value if not found.
fn lookup_default(key: &[u8]) -> Self where Self: Sized + Default { Self::lookup(key).unwrap_or_else(Default::default) }
fn lookup_default(key: &[u8]) -> Self where Self: Sized + Default {
Self::lookup(key).unwrap_or_else(Default::default)
}
/// Lookup `Some` value in storage and deserialise; `None` if it's not there.
fn lookup(_key: &[u8]) -> Option<Self> where Self: Sized { unimplemented!() }
fn lookup(_key: &[u8]) -> Option<Self> where Self: Sized {
unimplemented!()
}
/// Place the value in storage under `key`.
fn store(&self, key: &[u8]);
}
// TODO: consider using blake256 to avoid possible eclipse attack.
// TODO: consider using blake256 to avoid possible preimage attack.
/// Remove `key` from storage.
pub fn kill(key: &[u8]) { runtime_support::set_storage(&twox_128(key)[..], b""); }
pub fn kill(key: &[u8]) {
runtime_support::set_storage(&twox_128(key)[..], b"");
}
impl<T: Default + Sized + EndianSensitive> Storable for T {
impl<T: Sized + Slicable> Storable for T {
fn lookup(key: &[u8]) -> Option<Self> {
Slicable::set_as_slice(|out| runtime_support::read_storage(&twox_128(key)[..], out) == out.len())
Slicable::set_as_slice(&|out, offset|
runtime_support::read_storage(&twox_128(key)[..], out, offset) == out.len()
)
}
fn store(&self, key: &[u8]) {
self.as_slice_then(|slice| runtime_support::set_storage(&twox_128(key)[..], slice));