From 2789bc2b8fb5b22b6cfc8ed96c99290eb2337fc5 Mon Sep 17 00:00:00 2001 From: Gav <gavin@parity.io> Date: Sun, 21 Jan 2018 22:35:25 +0100 Subject: [PATCH] Simple governance subsytem. --- substrate/executor/src/wasm_executor.rs | 5 +- substrate/native-runtime/support/src/lib.rs | 13 ++- .../polkadot/src/codec/endiansensitive.rs | 5 + .../polkadot/src/codec/slicable.rs | 24 ++-- substrate/wasm-runtime/polkadot/src/lib.rs | 2 +- .../polkadot/src/runtime/governance.rs | 97 ++++++++++++++++ .../wasm-runtime/polkadot/src/runtime/mod.rs | 4 +- .../polkadot/src/runtime/staking.rs | 108 ++++++++++-------- .../polkadot/src/support/function.rs | 16 ++- .../wasm-runtime/polkadot/src/support/mod.rs | 1 + .../polkadot/src/support/primitives.rs | 20 +++- .../polkadot/src/support/proposal.rs | 108 ++++++++++++++++++ .../polkadot/src/support/statichex.rs | 11 +- .../polkadot/src/support/storable.rs | 23 ++-- substrate/wasm-runtime/support/src/lib.rs | 6 +- 15 files changed, 360 insertions(+), 83 deletions(-) create mode 100644 substrate/wasm-runtime/polkadot/src/runtime/governance.rs create mode 100644 substrate/wasm-runtime/polkadot/src/support/proposal.rs diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index 7fc9a741f83..d0bc9081360 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -128,11 +128,12 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.write_primitive(written_out, written); offset as u32 }, - ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32 => { + ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32 => { if let Ok(key) = this.memory.get(key_data, key_len as usize) { if let Ok(value) = this.ext.storage(&key) { + let value = &value[value_offset as usize..]; let written = ::std::cmp::min(value_len as usize, value.len()); - let _ = this.memory.set(value_data, &value[0..written]); + let _ = this.memory.set(value_data, &value[..written]); written as u32 } else { 0 } } else { 0 } diff --git a/substrate/native-runtime/support/src/lib.rs b/substrate/native-runtime/support/src/lib.rs index 348c0b17a9c..7adf9a904b8 100644 --- a/substrate/native-runtime/support/src/lib.rs +++ b/substrate/native-runtime/support/src/lib.rs @@ -47,24 +47,29 @@ environmental!(ext : Externalities<Error=NoError> + 'static); /// Get `key` from storage and return a `Vec`, empty if there's a problem. pub fn storage(key: &[u8]) -> Vec<u8> { ext::with(|ext| ext.storage(key).ok().map(|s| s.to_vec())) - .unwrap_or(None) - .unwrap_or_else(|| vec![]) + .expect("read_storage cannot be called outside of an Externalities-provided environment.") + .unwrap_or_else(Vec::new) } /// Get `key` from storage, placing the value into `value_out` (as much as possible) and return /// the number of bytes that the key in storage was. -pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { +pub fn read_storage(key: &[u8], value_out: &mut [u8], value_offset: usize) -> usize { ext::with(|ext| { if let Ok(value) = ext.storage(key) { + let value = &value[value_offset..]; let written = ::std::cmp::min(value.len(), value_out.len()); value_out[0..written].copy_from_slice(&value[0..written]); value.len() } else { + // no-entry is treated as an empty vector of bytes. + // TODO: consider allowing empty-vector to exist separately to no-entry (i.e. return + // Option<usize>) 0 } - }).unwrap_or(0) + }).expect("read_storage cannot be called outside of an Externalities-provided environment.") } +/// Set the storage to some particular key. pub fn set_storage(key: &[u8], value: &[u8]) { ext::with(|ext| ext.set_storage(key.to_vec(), value.to_vec()) diff --git a/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs b/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs index a1576284228..734067d2342 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/endiansensitive.rs @@ -44,6 +44,11 @@ macro_rules! impl_non_endians { )* } } +// TODO: this is fine as long as bool is one byte. it'll break if llvm tries to use more. happily, +// this isn't an issue for the forseeable future. if it ever happens, then it should be implemented +// as endian sensitive. +impl EndianSensitive for bool {} + impl_endians!(u16, u32, u64, usize, i16, i32, i64, isize); impl_non_endians!(u8, i8, [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], diff --git a/substrate/wasm-runtime/polkadot/src/codec/slicable.rs b/substrate/wasm-runtime/polkadot/src/codec/slicable.rs index 5ec042ec9bf..1c783e39dfd 100644 --- a/substrate/wasm-runtime/polkadot/src/codec/slicable.rs +++ b/substrate/wasm-runtime/polkadot/src/codec/slicable.rs @@ -23,8 +23,10 @@ use endiansensitive::EndianSensitive; /// Trait that allows zero-copy read/write of value-references to/from slices in LE format. pub trait Slicable: Sized { fn from_slice(value: &[u8]) -> Option<Self> { - Self::set_as_slice(|out| if value.len() == out.len() { - out.copy_from_slice(&value); + Self::set_as_slice(&|out, offset| if value.len() >= out.len() + offset { + let value = &value[offset..]; + let len = out.len(); + out.copy_from_slice(&value[0..len]); true } else { false @@ -33,7 +35,7 @@ pub trait Slicable: Sized { fn to_vec(&self) -> Vec<u8> { self.as_slice_then(|s| s.to_vec()) } - fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(set_slice: F) -> Option<Self>; + fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(set_slice: &F) -> Option<Self>; fn as_slice_then<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { f(&self.to_vec()) } @@ -44,13 +46,13 @@ pub trait Slicable: Sized { pub trait NonTrivialSlicable: Slicable {} impl<T: EndianSensitive> Slicable for T { - 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> { let size = size_of::<T>(); let mut result: T = unsafe { uninitialized() }; let result_slice = unsafe { slice::from_raw_parts_mut(transmute::<*mut T, *mut u8>(&mut result), size) }; - if fill_slice(result_slice) { + if fill_slice(result_slice, 0) { Some(result.from_le()) } else { None @@ -74,8 +76,16 @@ impl Slicable for Vec<u8> { fn from_slice(value: &[u8]) -> Option<Self> { Some(value[4..].to_vec()) } - fn set_as_slice<F: FnOnce(&mut[u8]) -> bool>(_fill_slice: F) -> Option<Self> { - unimplemented!(); + fn set_as_slice<F: Fn(&mut[u8], usize) -> bool>(fill_slice: &F) -> Option<Self> { + u32::set_as_slice(fill_slice).and_then(|len| { + let mut v = Vec::with_capacity(len as usize); + unsafe { v.set_len(len as usize); } + if fill_slice(&mut v, 4) { + Some(v) + } else { + None + } + }) } fn to_vec(&self) -> Vec<u8> { let mut r: Vec<u8> = Vec::new().join(&(self.len() as u32)); diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index 7a96b6cee44..713d8eb0919 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -30,7 +30,7 @@ mod codec; mod support; mod runtime; pub use codec::{endiansensitive, streamreader, joiner, slicable, keyedvec}; -pub use support::{primitives, function, environment, storable}; +pub use support::{primitives, function, proposal, environment, storable}; #[cfg(test)] pub use support::{testing, statichex}; diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs new file mode 100644 index 00000000000..783522ffdf8 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs @@ -0,0 +1,97 @@ +// 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/>. + +//! Governance system: Handles administration and dispatch of sensitive operations including +//! setting new code, minting new tokens and changing parameters. + +use runtime_support::Vec; +use keyedvec::KeyedVec; +use storable::{Storable, StorageVec, kill}; +use primitives::{AccountID, Hash, BlockNumber}; +use proposal::Proposal; +use runtime::{staking, system, session}; + +// TRANSACTION API + +pub fn propose(transactor: &AccountID, proposal: &Proposal) { + if Proposal::lookup(b"gov:pro").is_some() { + panic!("there may only be one proposal per era."); + } + proposal.store(b"gov:pro"); + approve(transactor, staking::current_era()); +} + +pub fn approve(transactor: &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 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"); +} + +// 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) +} + +pub fn approvals_required() -> u32 { + approval_ppm_required() * staking::validator_count() as u32 / 1000 +} + +// PUBLIC API + +/// Current era is ending; we should finish up any proposals. +pub fn end_of_an_era() { + // TODO: 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 { + proposal.enact(); + } + } +} + +// 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/runtime/mod.rs b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs index f83922c42fe..6af33d6a657 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/mod.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs @@ -26,5 +26,7 @@ pub mod staking; pub mod timestamp; #[allow(unused)] pub mod session; +#[allow(unused)] +pub mod governance; -// TODO: governance, polkadao +// TODO: polkadao diff --git a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs index fc7c12798e3..96843290a2b 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/staking.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/staking.rs @@ -16,11 +16,11 @@ //! Staking manager: Handles balances and periodically determines the best set of validators. -use runtime_support::Vec; +use runtime_support::{Vec, RefCell}; use keyedvec::KeyedVec; use storable::{Storable, StorageVec}; use primitives::{BlockNumber, AccountID}; -use runtime::{system, session}; +use runtime::{system, session, governance}; /// The balance of an account. pub type Balance = u64; @@ -31,7 +31,7 @@ pub type Bondage = u64; struct IntentionStorageVec {} impl StorageVec for IntentionStorageVec { type Item = AccountID; - const PREFIX: &'static[u8] = b"ses:wil:"; + const PREFIX: &'static[u8] = b"sta:wil:"; } // Each identity's stake may be in one of three bondage states, given by an integer: @@ -40,45 +40,7 @@ impl StorageVec for IntentionStorageVec { // - n | n > current_era(): deactivating: recently representing a validator and not yet // ready for transfer. -/// The length of the bonding duration in eras. -pub fn bonding_duration() -> BlockNumber { - Storable::lookup_default(b"sta:loc") -} - -/// The length of a staking era in sessions. -pub fn validator_count() -> usize { - u32::lookup_default(b"sta:vac") as usize -} - -/// The length of a staking era in blocks. -pub fn era_length() -> BlockNumber { - sessions_per_era() * session::length() -} - -/// The length of a staking era in sessions. -pub fn sessions_per_era() -> BlockNumber { - Storable::lookup_default(b"sta:spe") -} - -/// The current era index. -pub fn current_era() -> BlockNumber { - Storable::lookup_default(b"sta:era") -} - -/// The block number at which the era length last changed. -pub fn last_era_length_change() -> BlockNumber { - Storable::lookup_default(b"sta:lec") -} - -/// The balance of a given account. -pub fn balance(who: &AccountID) -> Balance { - Storable::lookup_default(&who.to_keyed_vec(b"sta:bal:")) -} - -/// The liquidity-state of a given account. -pub fn bondage(who: &AccountID) -> Bondage { - Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:")) -} +// TRANSACTION API /// Transfer some unlocked staking balance to another staker. pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) { @@ -119,6 +81,54 @@ pub fn unstake(transactor: &AccountID) { (current_era() + bonding_duration()).store(&transactor.to_keyed_vec(b"sta:bon:")); } +// PUBLIC API + +pub fn set_sessions_per_era(new: BlockNumber) { + new.store(b"sta:nse"); +} + +// INSPECTION API + +/// The length of the bonding duration in eras. +pub fn bonding_duration() -> BlockNumber { + Storable::lookup_default(b"sta:loc") +} + +/// The length of a staking era in sessions. +pub fn validator_count() -> usize { + u32::lookup_default(b"sta:vac") as usize +} + +/// The length of a staking era in blocks. +pub fn era_length() -> BlockNumber { + sessions_per_era() * session::length() +} + +/// The length of a staking era in sessions. +pub fn sessions_per_era() -> BlockNumber { + Storable::lookup_default(b"sta:spe") +} + +/// The current era index. +pub fn current_era() -> BlockNumber { + Storable::lookup_default(b"sta:era") +} + +/// The block number at which the era length last changed. +pub fn last_era_length_change() -> BlockNumber { + Storable::lookup_default(b"sta:lec") +} + +/// The balance of a given account. +pub fn balance(who: &AccountID) -> Balance { + Storable::lookup_default(&who.to_keyed_vec(b"sta:bal:")) +} + +/// The liquidity-state of a given account. +pub fn bondage(who: &AccountID) -> Bondage { + Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:")) +} + /// Hook to be called after to transaction processing. pub fn check_new_era() { // check block number and call new_era if necessary. @@ -131,8 +141,12 @@ pub fn check_new_era() { /// The era has changed - enact new staking set. /// -/// NOTE: This always happens on a session change. +/// NOTE: This always happens immediately before a session change to ensure that new validators +/// get a chance to set their session keys. fn new_era() { + // Inform governance module that it's the end of an era + governance::end_of_an_era(); + // Increment current era. (current_era() + 1).store(b"sta:era"); @@ -143,9 +157,8 @@ fn new_era() { system::block_number().store(b"sta:lec"); } - // TODO: evaluate desired staking amounts and nominations and optimise to find the best + // evaluate desired staking amounts and nominations and optimise to find the best // combination of validators, then use session::set_validators(). - // for now, this just orders would-be stakers by their balances and chooses the top-most // validator_count() of them. let mut intentions = IntentionStorageVec::items() @@ -161,11 +174,6 @@ fn new_era() { ); } -/// Set a new era length. Won't kick in until the next era change (at current length). -fn set_sessions_per_era(new: BlockNumber) { - new.store(b"sta:nse"); -} - #[cfg(test)] mod tests { use runtime_support::{with_externalities, twox_128}; diff --git a/substrate/wasm-runtime/polkadot/src/support/function.rs b/substrate/wasm-runtime/polkadot/src/support/function.rs index f4ee9c79e6e..dd54ba10806 100644 --- a/substrate/wasm-runtime/polkadot/src/support/function.rs +++ b/substrate/wasm-runtime/polkadot/src/support/function.rs @@ -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); + } } } } diff --git a/substrate/wasm-runtime/polkadot/src/support/mod.rs b/substrate/wasm-runtime/polkadot/src/support/mod.rs index 56ad3a355c5..384909d019b 100644 --- a/substrate/wasm-runtime/polkadot/src/support/mod.rs +++ b/substrate/wasm-runtime/polkadot/src/support/mod.rs @@ -18,6 +18,7 @@ pub mod primitives; pub mod function; +pub mod proposal; pub mod environment; pub mod storable; diff --git a/substrate/wasm-runtime/polkadot/src/support/primitives.rs b/substrate/wasm-runtime/polkadot/src/support/primitives.rs index 3ab385d4889..c59e7c5dcd4 100644 --- a/substrate/wasm-runtime/polkadot/src/support/primitives.rs +++ b/substrate/wasm-runtime/polkadot/src/support/primitives.rs @@ -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!(); } diff --git a/substrate/wasm-runtime/polkadot/src/support/proposal.rs b/substrate/wasm-runtime/polkadot/src/support/proposal.rs new file mode 100644 index 00000000000..bffd7aa76ce --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/support/proposal.rs @@ -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); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/support/statichex.rs b/substrate/wasm-runtime/polkadot/src/support/statichex.rs index 152ceadc0b4..b750a8ca10f 100644 --- a/substrate/wasm-runtime/polkadot/src/support/statichex.rs +++ b/substrate/wasm-runtime/polkadot/src/support/statichex.rs @@ -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`). diff --git a/substrate/wasm-runtime/polkadot/src/support/storable.rs b/substrate/wasm-runtime/polkadot/src/support/storable.rs index 3caf3ed19bf..5cfa1549373 100644 --- a/substrate/wasm-runtime/polkadot/src/support/storable.rs +++ b/substrate/wasm-runtime/polkadot/src/support/storable.rs @@ -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)); diff --git a/substrate/wasm-runtime/support/src/lib.rs b/substrate/wasm-runtime/support/src/lib.rs index b65b5743515..5e689746c83 100644 --- a/substrate/wasm-runtime/support/src/lib.rs +++ b/substrate/wasm-runtime/support/src/lib.rs @@ -27,7 +27,7 @@ extern "C" { fn ext_print_num(value: u64); fn ext_set_storage(key_data: *const u8, key_len: u32, value_data: *const u8, value_len: u32); fn ext_get_allocated_storage(key_data: *const u8, key_len: u32, written_out: *mut u32) -> *mut u8; - fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32) -> u32; + fn ext_get_storage_into(key_data: *const u8, key_len: u32, value_data: *mut u8, value_len: u32, value_offset: u32) -> u32; fn ext_chain_id() -> u64; fn ext_blake2_256(data: *const u8, len: u32, out: *mut u8); fn ext_twox_128(data: *const u8, len: u32, out: *mut u8); @@ -52,9 +52,9 @@ pub fn set_storage(key: &[u8], value: &[u8]) { } } -pub fn read_storage(key: &[u8], value_out: &mut [u8]) -> usize { +pub fn read_storage(key: &[u8], value_offset: usize, value_out: &mut [u8]) -> usize { unsafe { - ext_get_storage_into(&key[0], key.len() as u32, &mut value_out[0], value_out.len() as u32) as usize + ext_get_storage_into(&key[0], key.len() as u32, &mut value_out[0], value_out.len() as u32, value_offset as u32) as usize } } -- GitLab