mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 00:57:57 +00:00
Simple governance subsytem.
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,6 +40,55 @@ impl StorageVec for IntentionStorageVec {
|
||||
// - n | n > current_era(): deactivating: recently representing a validator and not yet
|
||||
// ready for transfer.
|
||||
|
||||
// TRANSACTION API
|
||||
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) {
|
||||
let from_key = transactor.to_keyed_vec(b"sta:bal:");
|
||||
let from_balance = Balance::lookup_default(&from_key);
|
||||
assert!(from_balance >= value);
|
||||
let to_key = dest.to_keyed_vec(b"sta:bal:");
|
||||
let to_balance: Balance = Storable::lookup_default(&to_key);
|
||||
assert!(bondage(transactor) <= bondage(dest));
|
||||
assert!(to_balance + value > to_balance); // no overflow
|
||||
(from_balance - value).store(&from_key);
|
||||
(to_balance + value).store(&to_key);
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn stake(transactor: &AccountID) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(transactor.clone());
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:"));
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn unstake(transactor: &AccountID) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
if let Some(position) = intentions.iter().position(|t| t == transactor) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
(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")
|
||||
@@ -80,45 +129,6 @@ pub fn bondage(who: &AccountID) -> Bondage {
|
||||
Storable::lookup_default(&who.to_keyed_vec(b"sta:bon:"))
|
||||
}
|
||||
|
||||
/// Transfer some unlocked staking balance to another staker.
|
||||
pub fn transfer(transactor: &AccountID, dest: &AccountID, value: Balance) {
|
||||
let from_key = transactor.to_keyed_vec(b"sta:bal:");
|
||||
let from_balance = Balance::lookup_default(&from_key);
|
||||
assert!(from_balance >= value);
|
||||
let to_key = dest.to_keyed_vec(b"sta:bal:");
|
||||
let to_balance: Balance = Storable::lookup_default(&to_key);
|
||||
assert!(bondage(transactor) <= bondage(dest));
|
||||
assert!(to_balance + value > to_balance); // no overflow
|
||||
(from_balance - value).store(&from_key);
|
||||
(to_balance + value).store(&to_key);
|
||||
}
|
||||
|
||||
/// Declare the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn stake(transactor: &AccountID) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
// can't be in the list twice.
|
||||
assert!(intentions.iter().find(|t| *t == transactor).is_none(), "Cannot stake if already staked.");
|
||||
intentions.push(transactor.clone());
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
u64::max_value().store(&transactor.to_keyed_vec(b"sta:bon:"));
|
||||
}
|
||||
|
||||
/// Retract the desire to stake for the transactor.
|
||||
///
|
||||
/// Effects will be felt at the beginning of the next era.
|
||||
pub fn unstake(transactor: &AccountID) {
|
||||
let mut intentions = IntentionStorageVec::items();
|
||||
if let Some(position) = intentions.iter().position(|t| t == transactor) {
|
||||
intentions.swap_remove(position);
|
||||
} else {
|
||||
panic!("Cannot unstake if not already staked.");
|
||||
}
|
||||
IntentionStorageVec::set_items(&intentions);
|
||||
(current_era() + bonding_duration()).store(&transactor.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};
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user