Fix warning and directory restructure.

This commit is contained in:
Gav
2018-02-08 14:29:30 +01:00
parent ac5a750f45
commit 4582038b3d
145 changed files with 129 additions and 123 deletions
@@ -0,0 +1,91 @@
// 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/>.
//! Tool for creating the genesis block.
use std::collections::HashMap;
use runtime_io::twox_128;
use codec::{KeyedVec, Joiner};
use support::Hashable;
use polkadot_primitives::{BlockNumber, Block, AccountId};
use runtime::staking::Balance;
/// Configuration of a general Polkadot genesis block.
pub struct GenesisConfig {
pub validators: Vec<AccountId>,
pub authorities: Vec<AccountId>,
pub balances: Vec<(AccountId, Balance)>,
pub block_time: u64,
pub session_length: BlockNumber,
pub sessions_per_era: BlockNumber,
pub bonding_duration: BlockNumber,
pub approval_ratio: u32,
}
impl GenesisConfig {
pub fn new_simple(authorities_validators: Vec<AccountId>, balance: Balance) -> Self {
GenesisConfig {
validators: authorities_validators.clone(),
authorities: authorities_validators.clone(),
balances: authorities_validators.iter().map(|v| (v.clone(), balance)).collect(),
block_time: 5, // 5 second block time.
session_length: 720, // that's 1 hour per session.
sessions_per_era: 24, // 24 hours per era.
bonding_duration: 90, // 90 days per bond.
approval_ratio: 667, // 66.7% approvals required for legislation.
}
}
pub fn genesis_map(&self) -> HashMap<Vec<u8>, Vec<u8>> {
let wasm_runtime = include_bytes!("../wasm/genesis.wasm").to_vec();
vec![
(&b"gov:apr"[..], vec![].join(&self.approval_ratio)),
(&b"ses:len"[..], vec![].join(&self.session_length)),
(&b"ses:val:len"[..], vec![].join(&(self.validators.len() as u32))),
(&b"sta:wil:len"[..], vec![].join(&0u32)),
(&b"sta:spe"[..], vec![].join(&self.sessions_per_era)),
(&b"sta:vac"[..], vec![].join(&(self.validators.len() as u32))),
(&b"sta:era"[..], vec![].join(&0u64)),
].into_iter()
.map(|(k, v)| (k.into(), v))
.chain(self.validators.iter()
.enumerate()
.map(|(i, account)| ((i as u32).to_keyed_vec(b"ses:val:"), vec![].join(account)))
).chain(self.authorities.iter()
.enumerate()
.map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].join(account)))
).chain(self.balances.iter()
.map(|&(account, balance)| (account.to_keyed_vec(b"sta:bal:"), vec![].join(&balance)))
)
.map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec()))
.chain(vec![
(b":code"[..].into(), wasm_runtime),
(b":auth:len"[..].into(), vec![].join(&(self.authorities.len() as u32))),
].into_iter())
.chain(self.authorities.iter()
.enumerate()
.map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].join(account)))
)
.collect()
}
}
pub fn additional_storage_with_genesis(genesis_block: &Block) -> HashMap<Vec<u8>, Vec<u8>> {
use codec::Slicable;
map![
twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => genesis_block.header.blake2_256().to_vec()
]
}
+121
View File
@@ -0,0 +1,121 @@
// 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/>.
//! The Polkadot runtime. This can be compiled with #[no_std], ready for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate substrate_runtime_std as rstd;
#[macro_use]
extern crate substrate_runtime_io as runtime_io;
#[cfg(feature = "std")]
extern crate rustc_hex;
extern crate substrate_codec as codec;
extern crate substrate_primitives;
extern crate polkadot_primitives;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[macro_use]
pub mod support;
pub mod runtime;
#[cfg(feature = "std")]
pub mod genesismap;
use rstd::prelude::*;
use codec::Slicable;
use polkadot_primitives::{Header, Block, UncheckedTransaction};
/// Type definitions and helpers for transactions.
pub mod transaction {
use rstd::ops;
use polkadot_primitives::Signature;
pub use polkadot_primitives::{Transaction, UncheckedTransaction};
/// A type-safe indicator that a transaction has been checked.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct CheckedTransaction(UncheckedTransaction);
impl CheckedTransaction {
/// Get a reference to the checked signature.
pub fn signature(&self) -> &Signature {
&self.0.signature
}
}
impl ops::Deref for CheckedTransaction {
type Target = Transaction;
fn deref(&self) -> &Transaction {
&self.0.transaction
}
}
/// Check the signature on a transaction.
///
/// On failure, return the transaction back.
pub fn check(tx: UncheckedTransaction) -> Result<CheckedTransaction, UncheckedTransaction> {
let msg = ::codec::Slicable::to_vec(&tx.transaction);
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
Ok(CheckedTransaction(tx))
} else {
Err(tx)
}
}
}
/// Execute a block, with `input` being the canonical serialisation of the block. Returns the
/// empty vector.
pub fn execute_block(mut input: &[u8]) -> Vec<u8> {
runtime::system::internal::execute_block(Block::from_slice(&mut input).unwrap());
Vec::new()
}
/// Execute a given, serialised, transaction. Returns the empty vector.
pub fn execute_transaction(mut input: &[u8]) -> Vec<u8> {
let header = Header::from_slice(&mut input).unwrap();
let utx = UncheckedTransaction::from_slice(&mut input).unwrap();
let header = runtime::system::internal::execute_transaction(utx, header);
header.to_vec()
}
/// Execute a given, serialised, transaction. Returns the empty vector.
pub fn finalise_block(mut input: &[u8]) -> Vec<u8> {
let header = Header::from_slice(&mut input).unwrap();
let header = runtime::system::internal::finalise_block(header);
header.to_vec()
}
/// Run whatever tests we have.
pub fn run_tests(mut input: &[u8]) -> Vec<u8> {
use runtime_io::print;
print("run_tests...");
let block = Block::from_slice(&mut input).unwrap();
print("deserialised block.");
let stxs = block.transactions.iter().map(Slicable::to_vec).collect::<Vec<_>>();
print("reserialised transactions.");
[stxs.len() as u8].to_vec()
}
impl_stubs!(execute_block, execute_transaction, finalise_block, run_tests);
@@ -0,0 +1,48 @@
// 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/>.
//! Conensus module for runtime; manages the authority set ready for the native code.
use rstd::prelude::*;
use support::storage::unhashed::StorageVec;
use polkadot_primitives::SessionKey;
struct AuthorityStorageVec {}
impl StorageVec for AuthorityStorageVec {
type Item = SessionKey;
const PREFIX: &'static[u8] = b":auth:";
}
/// Get the current set of authorities. These are the session keys.
pub fn authorities() -> Vec<SessionKey> {
AuthorityStorageVec::items()
}
pub mod internal {
use super::*;
/// Set the current set of authorities' session keys.
///
/// Called by `next_session` only.
pub fn set_authorities(authorities: &[SessionKey]) {
AuthorityStorageVec::set_items(authorities);
}
/// Set a single authority by index.
pub fn set_authority(index: u32, key: &SessionKey) {
AuthorityStorageVec::set_item(index, key);
}
}
@@ -0,0 +1,370 @@
// 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.
//!
//! 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 through a simple tag in storage for
//! each validator that has approved.
//!
//! At the end of the era, all validators approvals are tallied and if there are sufficient to pass
//! the proposal then it is enacted. All items in storage concerning the proposal are reset.
use rstd::prelude::*;
use codec::KeyedVec;
use support::storage;
use polkadot_primitives::{Proposal, AccountId, Hash, BlockNumber};
use runtime::{staking, system, session};
const APPROVALS_REQUIRED: &[u8] = b"gov:apr";
const CURRENT_PROPOSAL: &[u8] = b"gov:pro";
const APPROVAL_OF: &[u8] = b"gov:app:";
/// The proportion of validators required for a propsal to be approved measured as the number out
/// of 1000.
pub fn approval_ppm_required() -> u32 {
storage::get_or(APPROVALS_REQUIRED, 1000)
}
/// The number of concrete validator approvals required for a proposal to pass.
pub fn approvals_required() -> u32 {
approval_ppm_required() * session::validator_count() / 1000
}
pub mod public {
use super::*;
/// Propose a sensitive action to be taken. Any action that is enactable by `Proposal` is valid.
/// Proposal is by the `transactor` and will automatically count as an approval. Transactor must
/// be a current validator. It is illegal to propose when there is already a proposal in effect.
pub fn propose(validator: &AccountId, proposal: &Proposal) {
if storage::exists(CURRENT_PROPOSAL) {
panic!("there may only be one proposal per era.");
}
storage::put(CURRENT_PROPOSAL, proposal);
approve(validator, staking::current_era());
}
/// Approve the current era's proposal. Transactor must be a validator. This may not be done more
/// than once for any validator in an era.
pub fn approve(validator: &AccountId, era_index: BlockNumber) {
if era_index != staking::current_era() {
panic!("approval vote applied on non-current era.")
}
if !storage::exists(CURRENT_PROPOSAL) {
panic!("there must be a proposal in order to approve.");
}
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(APPROVAL_OF);
if storage::exists(&key) {
panic!("transactor may not approve a proposal twice in one era.");
}
storage::put(&key, &true);
}
}
pub mod privileged {
use super::*;
/// Set the proportion of validators that must approve for a proposal to be enacted at the end of
/// its era. The value, `ppm`, is measured as a fraction of 1000 rounded down to the nearest whole
/// validator. `1000` would require the approval of all validators; `667` would require two-thirds
/// (or there abouts) of validators.
pub fn set_approval_ppm_required(ppm: u32) {
storage::put(APPROVALS_REQUIRED, &ppm);
}
}
pub mod internal {
use super::*;
use polkadot_primitives::Proposal;
/// Current era is ending; we should finish up any proposals.
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) = storage::take::<Proposal>(CURRENT_PROPOSAL) {
let approvals_required = approvals_required();
let approved = session::validators().into_iter()
.filter_map(|v| storage::take::<bool>(&v.to_keyed_vec(APPROVAL_OF)))
.take(approvals_required as usize)
.count() as u32;
if approved == approvals_required {
enact_proposal(proposal);
}
}
}
fn enact_proposal(proposal: Proposal) {
match proposal {
Proposal::SystemSetCode(code) => {
system::privileged::set_code(&code);
}
Proposal::SessionSetLength(value) => {
session::privileged::set_length(value);
}
Proposal::SessionForceNewSession => {
session::privileged::force_new_session();
}
Proposal::StakingSetSessionsPerEra(value) => {
staking::privileged::set_sessions_per_era(value);
}
Proposal::StakingSetBondingDuration(value) => {
staking::privileged::set_bonding_duration(value);
}
Proposal::StakingSetValidatorCount(value) => {
staking::privileged::set_validator_count(value);
}
Proposal::StakingForceNewEra => {
staking::privileged::force_new_era()
}
Proposal::GovernanceSetApprovalPpmRequired(value) => {
self::privileged::set_approval_ppm_required(value);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{KeyedVec, Joiner};
use support::{one, two, with_env};
use polkadot_primitives::{AccountId, Proposal};
use runtime::{staking, session};
fn new_test_ext() -> TestExternalities {
let one = one();
let two = two();
let three = [3u8; 32];
TestExternalities { storage: map![
twox_128(APPROVALS_REQUIRED).to_vec() => vec![].join(&667u32),
twox_128(b"ses:len").to_vec() => vec![].join(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].join(&3u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => one.to_vec(),
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => two.to_vec(),
twox_128(&2u32.to_keyed_vec(b"ses:val:")).to_vec() => three.to_vec(),
twox_128(b"sta:wil:len").to_vec() => vec![].join(&3u32),
twox_128(&0u32.to_keyed_vec(b"sta:wil:")).to_vec() => one.to_vec(),
twox_128(&1u32.to_keyed_vec(b"sta:wil:")).to_vec() => two.to_vec(),
twox_128(&2u32.to_keyed_vec(b"sta:wil:")).to_vec() => three.to_vec(),
twox_128(b"sta:spe").to_vec() => vec![].join(&1u64),
twox_128(b"sta:vac").to_vec() => vec![].join(&3u64),
twox_128(b"sta:era").to_vec() => vec![].join(&1u64)
], }
}
#[test]
fn majority_voting_should_work() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Approve it. Era length changes.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&two, 1);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 2);
});
}
#[test]
fn majority_voting_should_work_after_unsuccessful_previous() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Fail it.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
// Block 2: Make proposal. Approve it. It should change era length.
with_env(|e| e.block_number = 2);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&two, 2);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 2);
});
}
#[test]
fn minority_voting_should_not_succeed() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
#[test]
#[should_panic]
fn old_voting_should_be_illegal() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&two, 0);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
#[test]
#[should_panic]
fn double_voting_should_be_illegal() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&two, 1);
public::approve(&two, 1);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
#[test]
#[should_panic]
fn over_proposing_should_be_illegal() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::propose(&two, &Proposal::StakingSetSessionsPerEra(2));
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
#[test]
#[should_panic]
fn approving_without_proposal_should_be_illegal() {
let one = one();
let two = two();
let three = [3u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::approve(&two, 1);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
#[test]
#[should_panic]
fn non_validator_approving_should_be_illegal() {
let one = one();
let two = two();
let three = [3u8; 32];
let four = [4u8; 32];
let mut t = new_test_ext();
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3u32);
assert_eq!(session::validators(), vec![one.clone(), two.clone(), three.clone()]);
assert!(!session::validators().into_iter().position(|v| &v == &one).is_none());
// Block 1: Make proposal. Will have only 1 vote. No change.
with_env(|e| e.block_number = 1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&four, 1);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
}
@@ -0,0 +1,34 @@
// 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/>.
//! The Polkadot runtime.
#[allow(unused)]
pub mod system;
#[allow(unused)]
pub mod consensus;
#[allow(unused)]
pub mod staking;
#[allow(unused)]
pub mod timestamp;
#[allow(unused)]
pub mod session;
#[allow(unused)]
pub mod governance;
#[allow(unused)]
pub mod parachains;
// TODO: polkadao
@@ -0,0 +1,142 @@
// 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/>.
//! Main parachains logic. For now this is just the determination of which validators do what.
use rstd::prelude::*;
use rstd::mem;
use codec::{Slicable, Joiner};
use support::{Hashable, with_env, storage};
use runtime::session;
const PARACHAIN_COUNT: &[u8] = b"par:cou";
/// Identifier for a chain, either one of a number of parachains or the relay chain.
#[derive(Copy, Clone, PartialEq)]
#[cfg_attr(test, derive(Debug))]
pub enum Chain {
/// The relay chain.
Relay,
/// A parachain of the given index.
Parachain(u32),
}
/// The duty roster specifying what jobs each validator must do.
#[derive(Clone, PartialEq)]
#[cfg_attr(test, derive(Default, Debug))]
pub struct DutyRoster {
/// Lookup from validator index to chain on which that validator has a duty to validate.
pub validator_duty: Vec<Chain>,
/// Lookup from validator index to chain on which that validator has a duty to guarantee
/// availability.
pub guarantor_duty: Vec<Chain>,
}
/// Get the number of parachains registered at present.
pub fn parachain_count() -> u32 {
storage::get_or(PARACHAIN_COUNT, 0)
}
/// Calculate the current block's duty roster.
pub fn calculate_duty_roster() -> DutyRoster {
let parachain_count = parachain_count();
let validator_count = session::validator_count() as u32;
let validators_per_parachain = (validator_count - 1) / parachain_count;
let mut roles_val = (0..validator_count).map(|i| match i {
i if i < parachain_count * validators_per_parachain => Chain::Parachain(i / validators_per_parachain as u32),
_ => Chain::Relay,
}).collect::<Vec<_>>();
let mut roles_gua = roles_val.clone();
let h = with_env(|e| e.parent_hash.clone());
let mut seed = Vec::<u8>::new().join(&h).join(b"validator_role_pairs").blake2_256();
// shuffle
for i in 0..(validator_count - 1) {
// 8 bytes of entropy used per cycle, 32 bytes entropy per hash
let offset = (i * 8 % 32) as usize;
// number of roles remaining to select from.
let remaining = (validator_count - i) as usize;
// 4 * 2 32-bit ints per 256-bit seed.
let val_index = u32::from_slice(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining;
let gua_index = u32::from_slice(&mut &seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining;
if offset == 24 {
// into the last 8 bytes - rehash to gather new entropy
seed = seed.blake2_256();
}
// exchange last item with randomly chosen first.
roles_val.swap(remaining - 1, val_index);
roles_gua.swap(remaining - 1, gua_index);
}
DutyRoster {
validator_duty: roles_val,
guarantor_duty: roles_gua,
}
}
#[cfg(test)]
mod tests {
use super::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{KeyedVec, Joiner};
use support::{one, two, with_env};
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![
twox_128(b"ses:val:len").to_vec() => vec![].join(&8u32),
twox_128(b"par:cou").to_vec() => vec![].join(&2u32)
], }
}
#[test]
fn should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
let check_roster = |duty_roster: &DutyRoster| {
assert_eq!(duty_roster.validator_duty.len(), 8);
assert_eq!(duty_roster.guarantor_duty.len(), 8);
for i in 0..2 {
assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3);
assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Parachain(i)).count(), 3);
}
assert_eq!(duty_roster.validator_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
assert_eq!(duty_roster.guarantor_duty.iter().filter(|&&j| j == Chain::Relay).count(), 2);
};
with_env(|e| e.parent_hash = [0u8; 32].into());
let duty_roster_0 = calculate_duty_roster();
check_roster(&duty_roster_0);
with_env(|e| e.parent_hash = [1u8; 32].into());
let duty_roster_1 = calculate_duty_roster();
check_roster(&duty_roster_1);
assert!(duty_roster_0 != duty_roster_1);
with_env(|e| e.parent_hash = [2u8; 32].into());
let duty_roster_2 = calculate_duty_roster();
check_roster(&duty_roster_2);
assert!(duty_roster_0 != duty_roster_2);
assert!(duty_roster_1 != duty_roster_2);
});
}
}
@@ -0,0 +1,249 @@
// 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/>.
//! Session manager: is told the validators and allows them to manage their session keys for the
//! consensus module.
use rstd::prelude::*;
use codec::KeyedVec;
use support::{storage, StorageVec};
use polkadot_primitives::{AccountId, SessionKey, BlockNumber};
use runtime::{system, staking, consensus};
const SESSION_LENGTH: &[u8] = b"ses:len";
const CURRENT_INDEX: &[u8] = b"ses:ind";
const LAST_LENGTH_CHANGE: &[u8] = b"ses:llc";
const NEXT_KEY_FOR: &[u8] = b"ses:nxt:";
const NEXT_SESSION_LENGTH: &[u8] = b"ses:nln";
struct ValidatorStorageVec {}
impl StorageVec for ValidatorStorageVec {
type Item = AccountId;
const PREFIX: &'static[u8] = b"ses:val:";
}
/// Get the current set of authorities. These are the session keys.
pub fn validators() -> Vec<AccountId> {
ValidatorStorageVec::items()
}
/// The number of blocks in each session.
pub fn length() -> BlockNumber {
storage::get_or(SESSION_LENGTH, 0)
}
/// The number of validators currently.
pub fn validator_count() -> u32 {
ValidatorStorageVec::count() as u32
}
/// The current era index.
pub fn current_index() -> BlockNumber {
storage::get_or(CURRENT_INDEX, 0)
}
/// The block number at which the era length last changed.
pub fn last_length_change() -> BlockNumber {
storage::get_or(LAST_LENGTH_CHANGE, 0)
}
pub mod public {
use super::*;
/// Sets the session key of `_validator` to `_key`. This doesn't take effect until the next
/// session.
pub fn set_key(validator: &AccountId, key: &SessionKey) {
// set new value for next session
storage::put(&validator.to_keyed_vec(NEXT_KEY_FOR), key);
}
}
pub mod privileged {
use super::*;
/// Set a new era length. Won't kick in until the next era change (at current length).
pub fn set_length(new: BlockNumber) {
storage::put(NEXT_SESSION_LENGTH, &new);
}
/// Forces a new session.
pub fn force_new_session() {
rotate_session();
}
}
// INTERNAL API (available to other runtime modules)
pub mod internal {
use super::*;
/// Set the current set of validators.
///
/// Called by staking::next_era() only. `next_session` should be called after this in order to
/// update the session keys to the next validator set.
pub fn set_validators(new: &[AccountId]) {
ValidatorStorageVec::set_items(new);
consensus::internal::set_authorities(new);
}
/// Hook to be called after transaction processing.
pub fn check_rotate_session() {
// do this last, after the staking system has had chance to switch out the authorities for the
// new set.
// check block number and call next_session if necessary.
if (system::block_number() - last_length_change()) % length() == 0 {
rotate_session();
}
}
}
/// Move onto next session: register the new authority set.
fn rotate_session() {
// Increment current session index.
storage::put(CURRENT_INDEX, &(current_index() + 1));
// Enact era length change.
if let Some(next_len) = storage::get::<u64>(NEXT_SESSION_LENGTH) {
storage::put(SESSION_LENGTH, &next_len);
storage::put(LAST_LENGTH_CHANGE, &system::block_number());
storage::kill(NEXT_SESSION_LENGTH);
}
// Update any changes in session keys.
validators().iter().enumerate().for_each(|(i, v)| {
let k = v.to_keyed_vec(NEXT_KEY_FOR);
if let Some(n) = storage::take(&k) {
consensus::internal::set_authority(i as u32, &n);
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use super::public::*;
use super::privileged::*;
use super::internal::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{KeyedVec, Joiner};
use support::{one, two, with_env};
use polkadot_primitives::AccountId;
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
TestExternalities { storage: map![
twox_128(SESSION_LENGTH).to_vec() => vec![].join(&2u64),
// the validators (10, 20, ...)
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32),
twox_128(&0u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![10; 32],
twox_128(&1u32.to_keyed_vec(ValidatorStorageVec::PREFIX)).to_vec() => vec![20; 32],
// initial session keys (11, 21, ...)
b":auth:len".to_vec() => vec![].join(&2u32),
0u32.to_keyed_vec(b":auth:") => vec![11; 32],
1u32.to_keyed_vec(b":auth:") => vec![21; 32]
], }
}
#[test]
fn simple_setup_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
assert_eq!(length(), 2u64);
assert_eq!(validators(), vec![[10u8; 32], [20u8; 32]]);
});
}
#[test]
fn session_length_change_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
// Block 1: Change to length 3; no visible change.
with_env(|e| e.block_number = 1);
set_length(3);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 0);
// Block 2: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 2);
set_length(3);
check_rotate_session();
assert_eq!(length(), 3);
assert_eq!(current_index(), 1);
// Block 3: Length now changed to 3. Index incremented.
with_env(|e| e.block_number = 3);
check_rotate_session();
assert_eq!(length(), 3);
assert_eq!(current_index(), 1);
// Block 4: Change to length 2; no visible change.
with_env(|e| e.block_number = 4);
set_length(2);
check_rotate_session();
assert_eq!(length(), 3);
assert_eq!(current_index(), 1);
// Block 5: Length now changed to 2. Index incremented.
with_env(|e| e.block_number = 5);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 2);
// Block 6: No change.
with_env(|e| e.block_number = 6);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 2);
// Block 7: Next index.
with_env(|e| e.block_number = 7);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 3);
});
}
#[test]
fn session_change_should_work() {
let mut t = simple_setup();
with_externalities(&mut t, || {
// Block 1: No change
with_env(|e| e.block_number = 1);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 2: Session rollover, but no change.
with_env(|e| e.block_number = 2);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 3: Set new key for validator 2; no visible change.
with_env(|e| e.block_number = 3);
set_key(&[20; 32], &[22; 32]);
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 4: Session rollover, authority 2 changes.
with_env(|e| e.block_number = 4);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]);
});
}
}
@@ -0,0 +1,406 @@
// 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/>.
//! Staking manager: Handles balances and periodically determines the best set of validators.
use rstd::prelude::*;
use rstd::cell::RefCell;
use runtime_io::print;
use codec::KeyedVec;
use support::{storage, StorageVec};
use polkadot_primitives::{BlockNumber, AccountId};
use runtime::{system, session, governance};
/// The balance of an account.
pub type Balance = u64;
/// The amount of bonding period left in an account. Measured in eras.
pub type Bondage = u64;
struct IntentionStorageVec {}
impl StorageVec for IntentionStorageVec {
type Item = AccountId;
const PREFIX: &'static[u8] = b"sta:wil:";
}
const BONDING_DURATION: &[u8] = b"sta:loc";
const VALIDATOR_COUNT: &[u8] = b"sta:vac";
const SESSIONS_PER_ERA: &[u8] = b"sta:spe";
const NEXT_SESSIONS_PER_ERA: &[u8] = b"sta:nse";
const CURRENT_ERA: &[u8] = b"sta:era";
const LAST_ERA_LENGTH_CHANGE: &[u8] = b"sta:lec";
const BALANCE_OF: &[u8] = b"sta:bal:";
const BONDAGE_OF: &[u8] = b"sta:bon:";
/// The length of the bonding duration in eras.
pub fn bonding_duration() -> BlockNumber {
storage::get_or_default(BONDING_DURATION)
}
/// The length of a staking era in sessions.
pub fn validator_count() -> usize {
storage::get_or_default::<u32>(VALIDATOR_COUNT) 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 {
storage::get_or_default(SESSIONS_PER_ERA)
}
/// The current era index.
pub fn current_era() -> BlockNumber {
storage::get_or_default(CURRENT_ERA)
}
/// The block number at which the era length last changed.
pub fn last_era_length_change() -> BlockNumber {
storage::get_or_default(LAST_ERA_LENGTH_CHANGE)
}
/// The balance of a given account.
pub fn balance(who: &AccountId) -> Balance {
storage::get_or_default(&who.to_keyed_vec(BALANCE_OF))
}
/// The liquidity-state of a given account.
pub fn bondage(who: &AccountId) -> Bondage {
storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF))
}
// Each identity's stake may be in one of three bondage states, given by an integer:
// - n | n <= current_era(): inactive: free to be transferred.
// - ~0: active: currently representing a validator.
// - n | n > current_era(): deactivating: recently representing a validator and not yet
// ready for transfer.
pub mod public {
use super::*;
/// Transfer some unlocked staking balance to another staker.
pub fn transfer(transactor: &AccountId, dest: &AccountId, value: Balance) {
let from_key = transactor.to_keyed_vec(BALANCE_OF);
let from_balance = storage::get_or_default::<Balance>(&from_key);
assert!(from_balance >= value);
let to_key = dest.to_keyed_vec(BALANCE_OF);
let to_balance: Balance = storage::get_or_default(&to_key);
assert!(bondage(transactor) <= bondage(dest));
assert!(to_balance + value > to_balance); // no overflow
storage::put(&from_key, &(from_balance - value));
storage::put(&to_key, &(to_balance + value));
}
/// 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);
storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &u64::max_value());
}
/// 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);
storage::put(&transactor.to_keyed_vec(BONDAGE_OF), &(current_era() + bonding_duration()));
}
}
pub mod privileged {
use super::*;
/// Set the number of sessions in an era.
pub fn set_sessions_per_era(new: BlockNumber) {
storage::put(NEXT_SESSIONS_PER_ERA, &new);
}
/// The length of the bonding duration in eras.
pub fn set_bonding_duration(new: BlockNumber) {
storage::put(BONDING_DURATION, &new);
}
/// The length of a staking era in sessions.
pub fn set_validator_count(new: u32) {
storage::put(VALIDATOR_COUNT, &new);
}
/// Force there to be a new era. This also forces a new session immediately after.
pub fn force_new_era() {
new_era();
session::privileged::force_new_session();
}
}
pub mod internal {
use super::*;
/// Hook to be called after to transaction processing.
pub fn check_new_era() {
// check block number and call new_era if necessary.
if (system::block_number() - last_era_length_change()) % era_length() == 0 {
new_era();
}
}
}
/// The era has changed - enact new staking set.
///
/// 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::internal::end_of_an_era();
// Increment current era.
storage::put(CURRENT_ERA, &(current_era() + 1));
// Enact era length change.
let next_spe: u64 = storage::get_or_default(NEXT_SESSIONS_PER_ERA);
if next_spe > 0 && next_spe != sessions_per_era() {
storage::put(SESSIONS_PER_ERA, &next_spe);
storage::put(LAST_ERA_LENGTH_CHANGE, &system::block_number());
}
// evaluate desired staking amounts and nominations and optimise to find the best
// combination of validators, then use session::internal::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()
.into_iter()
.map(|v| (balance(&v), v))
.collect::<Vec<_>>();
intentions.sort_unstable_by(|&(b1, _), &(b2, _)| b2.cmp(&b1));
session::internal::set_validators(
&intentions.into_iter()
.map(|(_, v)| v)
.take(validator_count())
.collect::<Vec<_>>()
);
}
#[cfg(test)]
mod tests {
use super::*;
use super::internal::*;
use super::public::*;
use super::privileged::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{KeyedVec, Joiner};
use support::{one, two, with_env};
use polkadot_primitives::AccountId;
use runtime::{staking, session};
#[test]
fn staking_should_work() {
let one = one();
let two = two();
let three = [3u8; 32];
let four = [4u8; 32];
let mut t = TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].join(&2u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![10; 32],
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => vec![20; 32],
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].join(&2u64),
twox_128(VALIDATOR_COUNT).to_vec() => vec![].join(&2u32),
twox_128(BONDING_DURATION).to_vec() => vec![].join(&3u64),
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&10u64),
twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&20u64),
twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&30u64),
twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&40u64)
], };
with_externalities(&mut t, || {
assert_eq!(era_length(), 2u64);
assert_eq!(validator_count(), 2usize);
assert_eq!(bonding_duration(), 3u64);
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 1: Add three validators. No obvious change.
with_env(|e| e.block_number = 1);
stake(&one);
stake(&two);
stake(&four);
check_new_era();
assert_eq!(session::validators(), vec![[10u8; 32], [20u8; 32]]);
// Block 2: New validator set now.
with_env(|e| e.block_number = 2);
check_new_era();
assert_eq!(session::validators(), vec![four.clone(), two.clone()]);
// Block 3: Unstake highest, introduce another staker. No change yet.
with_env(|e| e.block_number = 3);
stake(&three);
unstake(&four);
check_new_era();
// Block 4: New era - validators change.
with_env(|e| e.block_number = 4);
check_new_era();
assert_eq!(session::validators(), vec![three.clone(), two.clone()]);
// Block 5: Transfer stake from highest to lowest. No change yet.
with_env(|e| e.block_number = 5);
transfer(&four, &one, 40);
check_new_era();
// Block 6: Lowest now validator.
with_env(|e| e.block_number = 6);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 7: Unstake three. No change yet.
with_env(|e| e.block_number = 7);
unstake(&three);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 8: Back to one and two.
with_env(|e| e.block_number = 8);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), two.clone()]);
});
}
#[test]
fn staking_eras_work() {
let mut t = TestExternalities { storage: map![
twox_128(b"ses:len").to_vec() => vec![].join(&1u64),
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].join(&2u64)
], };
with_externalities(&mut t, || {
assert_eq!(era_length(), 2u64);
assert_eq!(sessions_per_era(), 2u64);
assert_eq!(last_era_length_change(), 0u64);
assert_eq!(current_era(), 0u64);
// Block 1: No change.
with_env(|e| e.block_number = 1);
check_new_era();
assert_eq!(sessions_per_era(), 2u64);
assert_eq!(last_era_length_change(), 0u64);
assert_eq!(current_era(), 0u64);
// Block 2: Simple era change.
with_env(|e| e.block_number = 2);
check_new_era();
assert_eq!(sessions_per_era(), 2u64);
assert_eq!(last_era_length_change(), 0u64);
assert_eq!(current_era(), 1u64);
// Block 3: Schedule an era length change; no visible changes.
with_env(|e| e.block_number = 3);
set_sessions_per_era(3);
check_new_era();
assert_eq!(sessions_per_era(), 2u64);
assert_eq!(last_era_length_change(), 0u64);
assert_eq!(current_era(), 1u64);
// Block 4: Era change kicks in.
with_env(|e| e.block_number = 4);
check_new_era();
assert_eq!(sessions_per_era(), 3u64);
assert_eq!(last_era_length_change(), 4u64);
assert_eq!(current_era(), 2u64);
// Block 5: No change.
with_env(|e| e.block_number = 5);
check_new_era();
assert_eq!(sessions_per_era(), 3u64);
assert_eq!(last_era_length_change(), 4u64);
assert_eq!(current_era(), 2u64);
// Block 6: No change.
with_env(|e| e.block_number = 6);
check_new_era();
assert_eq!(sessions_per_era(), 3u64);
assert_eq!(last_era_length_change(), 4u64);
assert_eq!(current_era(), 2u64);
// Block 7: Era increment.
with_env(|e| e.block_number = 7);
check_new_era();
assert_eq!(sessions_per_era(), 3u64);
assert_eq!(last_era_length_change(), 4u64);
assert_eq!(current_era(), 3u64);
});
}
#[test]
fn staking_balance_works() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&42u64)
], };
with_externalities(&mut t, || {
assert_eq!(balance(&one), 42);
assert_eq!(balance(&two), 0);
});
}
#[test]
fn staking_balance_transfer_works() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&111u64)
], };
with_externalities(&mut t, || {
transfer(&one, &two, 69);
assert_eq!(balance(&one), 42);
assert_eq!(balance(&two), 69);
});
}
#[test]
#[should_panic]
fn staking_balance_transfer_when_bonded_doesnt_work() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].join(&111u64)
], };
with_externalities(&mut t, || {
stake(&one);
transfer(&one, &two, 69);
});
}
}
@@ -0,0 +1,367 @@
// 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/>.
//! System manager: Handles all of the top-level stuff; executing block/transaction, setting code
//! and depositing logs.
use rstd::prelude::*;
use rstd::mem;
use runtime_io::{print, storage_root, enumerated_trie_root};
use codec::{KeyedVec, Slicable};
use support::{Hashable, storage, with_env};
use polkadot_primitives::{AccountId, Hash, TxOrder, BlockNumber, Block, Header,
UncheckedTransaction, Function, Log};
use runtime::{staking, session};
const NONCE_OF: &[u8] = b"sys:non:";
const BLOCK_HASH_AT: &[u8] = b"sys:old:";
const CODE: &[u8] = b"sys:cod";
/// The current block number being processed. Set by `execute_block`.
pub fn block_number() -> BlockNumber {
with_env(|e| e.block_number)
}
/// Get the block hash of a given block (uses storage).
pub fn block_hash(number: BlockNumber) -> Hash {
storage::get_or_default(&number.to_keyed_vec(BLOCK_HASH_AT))
}
pub mod privileged {
use super::*;
/// Set the new code.
pub fn set_code(new: &[u8]) {
storage::unhashed::put_raw(b":code", new);
}
}
pub mod internal {
use super::*;
struct CheckedTransaction(UncheckedTransaction);
/// Deposits a log and ensures it matches the blocks log data.
pub fn deposit_log(log: Log) {
with_env(|e| e.digest.logs.push(log));
}
/// Actually execute all transitioning for `block`.
pub fn execute_block(mut block: Block) {
// populate environment from header.
with_env(|e| {
e.block_number = block.header.number;
e.parent_hash = block.header.parent_hash;
});
// any initial checks
initial_checks(&block);
// execute transactions
block.transactions.iter().cloned().for_each(super::execute_transaction);
// post-transactional book-keeping.
staking::internal::check_new_era();
session::internal::check_rotate_session();
// any final checks
final_checks(&block);
// any stuff that we do after taking the storage root.
post_finalise(&block.header);
}
/// Execute a transaction outside of the block execution function.
/// This doesn't attempt to validate anything regarding the block.
pub fn execute_transaction(utx: UncheckedTransaction, mut header: Header) -> Header {
// populate environment from header.
with_env(|e| {
e.block_number = header.number;
e.parent_hash = header.parent_hash;
mem::swap(&mut header.digest, &mut e.digest);
});
super::execute_transaction(utx);
with_env(|e| {
mem::swap(&mut header.digest, &mut e.digest);
});
header
}
/// Finalise the block - it is up the caller to ensure that all header fields are valid
/// except state-root.
pub fn finalise_block(mut header: Header) -> Header {
// populate environment from header.
with_env(|e| {
e.block_number = header.number;
e.parent_hash = header.parent_hash;
mem::swap(&mut header.digest, &mut e.digest);
});
staking::internal::check_new_era();
session::internal::check_rotate_session();
header.state_root = storage_root().into();
with_env(|e| {
mem::swap(&mut header.digest, &mut e.digest);
});
post_finalise(&header);
header
}
/// Dispatch a function.
pub fn dispatch_function(function: &Function, transactor: &AccountId) {
match *function {
Function::StakingStake => {
::runtime::staking::public::stake(transactor);
}
Function::StakingUnstake => {
::runtime::staking::public::unstake(transactor);
}
Function::StakingTransfer(dest, value) => {
::runtime::staking::public::transfer(transactor, &dest, value);
}
Function::SessionSetKey(session) => {
::runtime::session::public::set_key(transactor, &session);
}
Function::TimestampSet(t) => {
::runtime::timestamp::public::set(t);
}
Function::GovernancePropose(ref proposal) => {
::runtime::governance::public::propose(transactor, proposal);
}
Function::GovernanceApprove(era_index) => {
::runtime::governance::public::approve(transactor, era_index);
}
}
}
}
fn execute_transaction(utx: UncheckedTransaction) {
use ::transaction;
// Verify the signature is good.
let tx = match transaction::check(utx) {
Ok(tx) => tx,
Err(_) => panic!("All transactions should be properly signed"),
};
// check nonce
let nonce_key = tx.signed.to_keyed_vec(NONCE_OF);
let expected_nonce: TxOrder = storage::get_or(&nonce_key, 0);
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
// increment nonce in storage
storage::put(&nonce_key, &(expected_nonce + 1));
// decode parameters and dispatch
internal::dispatch_function(&tx.function, &tx.signed);
}
fn initial_checks(block: &Block) {
let ref header = block.header;
// check parent_hash is correct.
assert!(
header.number > 0 && block_hash(header.number - 1) == header.parent_hash,
"Parent hash should be valid."
);
// check transaction trie root represents the transactions.
let txs = block.transactions.iter().map(Slicable::to_vec).collect::<Vec<_>>();
let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>();
let txs_root = enumerated_trie_root(&txs).into();
info_expect_equal_hash(&header.transaction_root, &txs_root);
assert!(header.transaction_root == txs_root, "Transaction trie root must be valid.");
}
fn final_checks(block: &Block) {
let ref header = block.header;
// check digest
with_env(|e| {
assert!(header.digest == e.digest);
});
// check storage root.
let storage_root = storage_root().into();
info_expect_equal_hash(&header.state_root, &storage_root);
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
}
fn post_finalise(header: &Header) {
// store the header hash in storage; we can't do it before otherwise there would be a
// cyclic dependency.
storage::put(&header.number.to_keyed_vec(BLOCK_HASH_AT), &header.blake2_256());
}
#[cfg(feature = "std")]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
use support::HexDisplay;
if given != expected {
println!("Hash: given={}, expected={}", HexDisplay::from(&given.0), HexDisplay::from(&expected.0));
}
}
#[cfg(not(feature = "std"))]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
if given != expected {
print("Hash not equal");
print(&given.0[..]);
print(&expected.0[..]);
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::internal::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{Joiner, KeyedVec, Slicable};
use support::{StaticHexInto, HexDisplay, one, two};
use polkadot_primitives::{Header, Digest, UncheckedTransaction, Transaction, Function};
use runtime::staking;
#[test]
fn staking_balance_transfer_dispatch_works() {
let one = one();
let two = two();
let mut t = TestExternalities { storage: map![
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], };
let tx = UncheckedTransaction {
transaction: Transaction {
signed: one.clone(),
nonce: 0,
function: Function::StakingTransfer(two, 69),
},
signature: "5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05".parse().unwrap(),
};
with_externalities(&mut t, || {
internal::execute_transaction(tx, Header::from_block_number(1));
assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 69);
});
}
fn new_test_ext() -> TestExternalities {
let one = one();
let two = two();
let three = [3u8; 32];
TestExternalities { storage: map![
twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].to_vec(),
twox_128(b"gov:apr").to_vec() => vec![].join(&667u32),
twox_128(b"ses:len").to_vec() => vec![].join(&2u64),
twox_128(b"ses:val:len").to_vec() => vec![].join(&3u32),
twox_128(&0u32.to_keyed_vec(b"ses:val:")).to_vec() => one.to_vec(),
twox_128(&1u32.to_keyed_vec(b"ses:val:")).to_vec() => two.to_vec(),
twox_128(&2u32.to_keyed_vec(b"ses:val:")).to_vec() => three.to_vec(),
twox_128(b"sta:wil:len").to_vec() => vec![].join(&3u32),
twox_128(&0u32.to_keyed_vec(b"sta:wil:")).to_vec() => one.to_vec(),
twox_128(&1u32.to_keyed_vec(b"sta:wil:")).to_vec() => two.to_vec(),
twox_128(&2u32.to_keyed_vec(b"sta:wil:")).to_vec() => three.to_vec(),
twox_128(b"sta:spe").to_vec() => vec![].join(&2u64),
twox_128(b"sta:vac").to_vec() => vec![].join(&3u64),
twox_128(b"sta:era").to_vec() => vec![].join(&0u64),
twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0]
], }
}
#[test]
fn block_import_works() {
let one = one();
let two = two();
let mut t = new_test_ext();
let h = Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("1ab2dbb7d4868a670b181327b0b6a58dc64b10cfb9876f737a5aa014b8da31e0").into(),
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
digest: Digest { logs: vec![], },
};
let b = Block {
header: h,
transactions: vec![],
};
with_externalities(&mut t, || {
execute_block(b);
});
}
#[test]
#[should_panic]
fn block_import_of_bad_state_root_fails() {
let one = one();
let two = two();
let mut t = new_test_ext();
let h = Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: [0u8; 32].into(),
transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(),
digest: Digest { logs: vec![], },
};
let b = Block {
header: h,
transactions: vec![],
};
with_externalities(&mut t, || {
execute_block(b);
});
}
#[test]
#[should_panic]
fn block_import_of_bad_transaction_root_fails() {
let one = one();
let two = two();
let mut t = new_test_ext();
let h = Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("1ab2dbb7d4868a670b181327b0b6a58dc64b10cfb9876f737a5aa014b8da31e0").into(),
transaction_root: [0u8; 32].into(),
digest: Digest { logs: vec![], },
};
let b = Block {
header: h,
transactions: vec![],
};
with_externalities(&mut t, || {
execute_block(b);
});
}
}
@@ -0,0 +1,60 @@
// 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/>.
//! Timestamp manager: just handles the current timestamp.
use support::storage;
pub type Timestamp = u64;
const CURRENT_TIMESTAMP: &[u8] = b"tim:val";
/// Get the current time.
pub fn get() -> Timestamp {
storage::get_or_default(CURRENT_TIMESTAMP)
}
pub mod public {
use super::*;
/// Set the current time.
pub fn set(now: Timestamp) {
storage::put(CURRENT_TIMESTAMP, &now);
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::public::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use runtime::timestamp;
use codec::{Joiner, KeyedVec};
#[test]
fn timestamp_works() {
let mut t = TestExternalities { storage: map![
twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].join(&42u64)
], };
with_externalities(&mut t, || {
assert_eq!(get(), 42);
set(69);
assert_eq!(get(), 69);
});
}
}
@@ -0,0 +1,82 @@
// 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/>.
//! Environment API: Allows certain information to be accessed throughout the runtime.
use rstd::boxed::Box;
use rstd::mem;
use rstd::cell::RefCell;
use rstd::rc::Rc;
use polkadot_primitives::{BlockNumber, Digest, Hash};
#[derive(Default)]
/// The information that can be accessed globally.
pub struct Environment {
/// The current block number.
pub block_number: BlockNumber,
/// The current block's parent hash.
pub parent_hash: Hash,
/// The current block digest.
pub digest: Digest,
}
/// Do something with the environment and return its value. Keep the function short.
pub fn with_env<T, F: FnOnce(&mut Environment) -> T>(f: F) -> T {
let e = env();
let mut eb = e.borrow_mut();
f(&mut *eb)
}
#[cfg(target_arch = "wasm32")]
fn env() -> Rc<RefCell<Environment>> {
// Initialize it to a null value
static mut SINGLETON: *const Rc<RefCell<Environment>> = 0 as *const Rc<RefCell<Environment>>;
unsafe {
if SINGLETON == 0 as *const Rc<RefCell<Environment>> {
// Make it
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
// Put it in the heap so it can outlive this call
SINGLETON = mem::transmute(Box::new(singleton));
}
// Now we give out a copy of the data that is safe to use concurrently.
(*SINGLETON).clone()
}
}
#[cfg(not(target_arch = "wasm32"))]
fn env() -> Rc<RefCell<Environment>> {
// Initialize it to a null value
thread_local!{
static SINGLETON: RefCell<*const Rc<RefCell<Environment>>> = RefCell::new(0 as *const Rc<RefCell<Environment>>);
}
SINGLETON.with(|s| unsafe {
if *s.borrow() == 0 as *const Rc<RefCell<Environment>> {
// Make it
let singleton: Rc<RefCell<Environment>> = Rc::new(RefCell::new(Default::default()));
// Put it in the heap so it can outlive this call
*s.borrow_mut() = mem::transmute(Box::new(singleton));
}
// Now we give out a copy of the data that is safe to use concurrently.
(**s.borrow()).clone()
})
}
@@ -0,0 +1,38 @@
// 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/>.
//! Hashable trait.
use codec::Slicable;
use runtime_io::{blake2_256, twox_128, twox_256};
pub trait Hashable: Sized {
fn blake2_256(&self) -> [u8; 32];
fn twox_128(&self) -> [u8; 16];
fn twox_256(&self) -> [u8; 32];
}
impl<T: Slicable> Hashable for T {
fn blake2_256(&self) -> [u8; 32] {
blake2_256(&self.to_vec())
}
fn twox_128(&self) -> [u8; 16] {
twox_128(&self.to_vec())
}
fn twox_256(&self) -> [u8; 32] {
twox_256(&self.to_vec())
}
}
@@ -0,0 +1,36 @@
// 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/>.
//! Support code for the runtime.
mod environment;
pub mod storage;
mod hashable;
#[cfg(feature = "std")]
mod statichex;
#[macro_use]
#[cfg(feature = "std")]
mod testing;
pub use self::environment::with_env;
pub use self::storage::StorageVec;
pub use self::hashable::Hashable;
#[cfg(feature = "std")]
pub use self::statichex::{StaticHexConversion, StaticHexInto};
#[cfg(feature = "std")]
pub use self::testing::{AsBytesRef, HexDisplay, one, two};
@@ -0,0 +1,61 @@
// 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/>.
//! Interpret a static string of hex as a desired type.
use rustc_hex::FromHex;
/// Trait to allow conversion from a static hex string to an instance.
pub trait StaticHexConversion: Sized {
/// Convert the static str into Self. Use just like `From::from`.
fn from_static_hex(hex: &'static str) -> Self;
}
macro_rules! impl_sizes {
( $( $t:expr ),* ) => { $(
impl StaticHexConversion for [u8; $t] {
fn from_static_hex(hex: &'static str) -> Self {
let mut r = [0u8; $t];
r.copy_from_slice(&FromHex::from_hex(hex).unwrap());
r
}
}
)* }
}
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`).
pub trait StaticHexInto {
/// Convert self (i.e. a static str) into the appropriate type. Use just like `Into::into`.
fn convert<T: StaticHexConversion>(self) -> T;
}
impl StaticHexInto for &'static str {
fn convert<T: StaticHexConversion>(self) -> T {
T::from_static_hex(self)
}
}
@@ -0,0 +1,350 @@
// 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/>.
//! Stuff to do with the runtime's storage.
use rstd::prelude::*;
use runtime_io::{self, twox_128};
use codec::{Slicable, KeyedVec};
// TODO: consider using blake256 to avoid possible preimage attack.
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let raw = runtime_io::storage(&twox_128(key)[..]);
Slicable::from_slice(&mut &raw[..])
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T: Slicable + Sized + Default>(key: &[u8]) -> T {
get(key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T: Slicable + Sized>(key: &[u8], default_value: T) -> T {
get(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
get(key).unwrap_or_else(default_value)
}
/// Please `value` in storage under `key`.
pub fn put<T: Slicable>(key: &[u8], value: &T) {
value.as_slice_then(|slice| runtime_io::set_storage(&twox_128(key)[..], slice));
}
/// Please `value` in storage under `key`.
pub fn place<T: Slicable>(key: &[u8], value: T) {
value.as_slice_then(|slice| runtime_io::set_storage(&twox_128(key)[..], slice));
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let r = get(key);
if r.is_some() {
kill(key);
}
r
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T: Slicable + Sized + Default>(key: &[u8]) -> T {
take(key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T: Slicable + Sized>(key: &[u8], default_value: T) -> T {
take(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
take(key).unwrap_or_else(default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(key: &[u8]) -> bool {
let mut x = [0u8; 1];
runtime_io::read_storage(&twox_128(key)[..], &mut x[..], 0) >= 1
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(key: &[u8]) {
runtime_io::set_storage(&twox_128(key)[..], b"");
}
/// Get a Vec of bytes from storage.
pub fn get_raw(key: &[u8]) -> Vec<u8> {
runtime_io::storage(&twox_128(key)[..])
}
/// Put a raw byte slice into storage.
pub fn put_raw(key: &[u8], value: &[u8]) {
runtime_io::set_storage(&twox_128(key)[..], value)
}
/// A trait to conveniently store a vector of storable data.
// TODO: add iterator support
pub trait StorageVec {
type Item: Default + Sized + Slicable;
const PREFIX: &'static [u8];
/// Get the current set of items.
fn items() -> Vec<Self::Item> {
(0..Self::count()).into_iter().map(Self::item).collect()
}
/// Set the current set of items.
fn set_items(items: &[Self::Item]) {
Self::set_count(items.len() as u32);
items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i));
}
fn set_item(index: u32, item: &Self::Item) {
if index < Self::count() {
put(&index.to_keyed_vec(Self::PREFIX), item);
}
}
fn item(index: u32) -> Self::Item {
get_or_default(&index.to_keyed_vec(Self::PREFIX))
}
fn set_count(count: u32) {
(count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default()));
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
}
fn count() -> u32 {
get_or_default(&b"len".to_keyed_vec(Self::PREFIX))
}
}
pub mod unhashed {
use super::{runtime_io, Slicable, KeyedVec, Vec};
/// Return the value of the item in storage under `key`, or `None` if there is no explicit entry.
pub fn get<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let raw = runtime_io::storage(key);
T::from_slice(&mut &raw[..])
}
/// Return the value of the item in storage under `key`, or the type's default if there is no
/// explicit entry.
pub fn get_or_default<T: Slicable + Sized + Default>(key: &[u8]) -> T {
get(key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry.
pub fn get_or<T: Slicable + Sized>(key: &[u8], default_value: T) -> T {
get(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry.
pub fn get_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
get(key).unwrap_or_else(default_value)
}
/// Please `value` in storage under `key`.
pub fn put<T: Slicable>(key: &[u8], value: &T) {
value.as_slice_then(|slice| runtime_io::set_storage(key, slice));
}
/// Please `value` in storage under `key`.
pub fn place<T: Slicable>(key: &[u8], value: T) {
value.as_slice_then(|slice| runtime_io::set_storage(key, slice));
}
/// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise.
pub fn take<T: Slicable + Sized>(key: &[u8]) -> Option<T> {
let r = get(key);
if r.is_some() {
kill(key);
}
r
}
/// Remove `key` from storage, returning its value, or, if there was no explicit entry in storage,
/// the default for its type.
pub fn take_or_default<T: Slicable + Sized + Default>(key: &[u8]) -> T {
take(key).unwrap_or_else(Default::default)
}
/// Return the value of the item in storage under `key`, or `default_value` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or<T: Slicable + Sized>(key: &[u8], default_value: T) -> T {
take(key).unwrap_or(default_value)
}
/// Return the value of the item in storage under `key`, or `default_value()` if there is no
/// explicit entry. Ensure there is no explicit entry on return.
pub fn take_or_else<T: Slicable + Sized, F: FnOnce() -> T>(key: &[u8], default_value: F) -> T {
take(key).unwrap_or_else(default_value)
}
/// Check to see if `key` has an explicit entry in storage.
pub fn exists(key: &[u8]) -> bool {
let mut x = [0u8; 1];
runtime_io::read_storage(key, &mut x[..], 0) >= 1
}
/// Ensure `key` has no explicit entry in storage.
pub fn kill(key: &[u8]) {
runtime_io::set_storage(key, b"");
}
/// Get a Vec of bytes from storage.
pub fn get_raw(key: &[u8]) -> Vec<u8> {
runtime_io::storage(key)
}
/// Put a raw byte slice into storage.
pub fn put_raw(key: &[u8], value: &[u8]) {
runtime_io::set_storage(key, value)
}
/// A trait to conveniently store a vector of storable data.
// TODO: add iterator support
pub trait StorageVec {
type Item: Default + Sized + Slicable;
const PREFIX: &'static [u8];
/// Get the current set of items.
fn items() -> Vec<Self::Item> {
(0..Self::count()).into_iter().map(Self::item).collect()
}
/// Set the current set of items.
fn set_items(items: &[Self::Item]) {
Self::set_count(items.len() as u32);
items.iter().enumerate().for_each(|(v, ref i)| Self::set_item(v as u32, i));
}
fn set_item(index: u32, item: &Self::Item) {
if index < Self::count() {
put(&index.to_keyed_vec(Self::PREFIX), item);
}
}
fn item(index: u32) -> Self::Item {
get_or_default(&index.to_keyed_vec(Self::PREFIX))
}
fn set_count(count: u32) {
(count..Self::count()).for_each(|i| Self::set_item(i, &Self::Item::default()));
put(&b"len".to_keyed_vec(Self::PREFIX), &count);
}
fn count() -> u32 {
get_or_default(&b"len".to_keyed_vec(Self::PREFIX))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use support::HexDisplay;
use runtime_io::{storage, twox_128, TestExternalities, with_externalities};
#[test]
fn integers_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
with_externalities(&mut t, || {
let x = 69u32;
put(b":test", &x);
let y: u32 = get(b":test").unwrap();
assert_eq!(x, y);
});
with_externalities(&mut t, || {
let x = 69426942i64;
put(b":test", &x);
let y: i64 = get(b":test").unwrap();
assert_eq!(x, y);
});
}
#[test]
fn bools_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
with_externalities(&mut t, || {
let x = true;
put(b":test", &x);
let y: bool = get(b":test").unwrap();
assert_eq!(x, y);
});
with_externalities(&mut t, || {
let x = false;
put(b":test", &x);
let y: bool = get(b":test").unwrap();
assert_eq!(x, y);
});
}
#[test]
fn vecs_can_be_retrieved() {
let mut t = TestExternalities { storage: HashMap::new(), };
with_externalities(&mut t, || {
runtime_io::set_storage(&twox_128(b":test"), b"\x0b\0\0\0Hello world");
let x = b"Hello world".to_vec();
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test"))));
let y = get::<Vec<u8>>(b":test").unwrap();
assert_eq!(x, y);
});
}
#[test]
fn vecs_can_be_stored() {
let mut t = TestExternalities { storage: HashMap::new(), };
let x = b"Hello world".to_vec();
with_externalities(&mut t, || {
put(b":test", &x);
});
println!("Ext is {:?}", t);
with_externalities(&mut t, || {
println!("Hex: {}", HexDisplay::from(&storage(&twox_128(b":test"))));
let y: Vec<u8> = get(b":test").unwrap();
assert_eq!(x, y);
});
}
#[test]
fn proposals_can_be_stored() {
use polkadot_primitives::Proposal;
let mut t = TestExternalities { storage: HashMap::new(), };
with_externalities(&mut t, || {
let x = Proposal::StakingSetSessionsPerEra(25519);
put(b":test", &x);
let y: Proposal = get(b":test").unwrap();
assert_eq!(x, y);
});
}
}
@@ -0,0 +1,83 @@
// 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/>.
//! Testing helpers.
use polkadot_primitives::AccountId;
use super::statichex::StaticHexInto;
#[macro_export]
macro_rules! map {
($( $name:expr => $value:expr ),*) => (
vec![ $( ( $name, $value ) ),* ].into_iter().collect()
)
}
/// One account (to which we know the secret key).
pub fn one() -> AccountId {
"2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee".convert()
}
/// Another account (secret key known).
pub fn two() -> AccountId {
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".convert()
}
/// Hex display, this time for no_std. See main codebase for documentation.
pub struct HexDisplay<'a>(&'a [u8]);
impl<'a> HexDisplay<'a> {
/// See main codebase for documentation.
pub fn from(d: &'a AsBytesRef) -> Self { HexDisplay(d.as_bytes_ref()) }
}
impl<'a> ::std::fmt::Display for HexDisplay<'a> {
fn fmt(&self, fmtr: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
for byte in self.0 {
try!( fmtr.write_fmt(format_args!("{:02x}", byte)));
}
Ok(())
}
}
/// See main codebase for documentation.
pub trait AsBytesRef {
/// See main codebase for documentation.
fn as_bytes_ref(&self) -> &[u8];
}
impl AsBytesRef for [u8] {
fn as_bytes_ref(&self) -> &[u8] { &self }
}
impl<'a> AsBytesRef for &'a[u8] {
fn as_bytes_ref(&self) -> &[u8] { self }
}
impl AsBytesRef for Vec<u8> {
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
}
macro_rules! impl_non_endians {
( $( $t:ty ),* ) => { $(
impl AsBytesRef for $t {
fn as_bytes_ref(&self) -> &[u8] { &self[..] }
}
)* }
}
impl_non_endians!([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],
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);