Make Polkadot use the Substrate traity libraries (#105)

* Initial stuff.

* Various fixes.

* Fix tests.

* Fix another test

* Fix another test.

* Docs in polkadot runtime.

* Fix up ser/de tests.

* Update god keys

* Syntax

* Fix

* Merge remote-tracking branch 'origin/master' into gav-merge-runtime

* Permissions on init.sh

* Port-over the whitespace from @rphmeier

* Rename

* Merge branch 'master' into gav-merge-runtime

* Fix typo.

* Fix grumbles.

* Make more idiomatic.

* Move `Ed25519Signature` out of traits.
This commit is contained in:
Gav Wood
2018-04-05 17:13:12 +02:00
committed by Robert Habermeier
parent 6b83f11a11
commit 1d8a9a6dd3
60 changed files with 1335 additions and 3636 deletions
-29
View File
@@ -1,29 +0,0 @@
// 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/>.
use runtime::{system, parachains, consensus, session};
impl_stubs!(
execute_block => |block| system::internal::execute_block(block),
execute_transaction => |(header, utx)| system::internal::execute_transaction(utx, header),
finalise_block => |header| system::internal::finalise_block(header),
validator_count => |()| session::validator_count(),
validators => |()| session::validators(),
authorities => |()| consensus::authorities(),
duty_roster => |()| parachains::calculate_duty_roster(),
timestamp => |()| ::runtime::timestamp::get(),
nonce => |account_id| system::nonce(account_id)
);
@@ -1,81 +0,0 @@
// 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::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 = Box::into_raw(Box::new(singleton)) as *const _;
}
// 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() = Box::into_raw(Box::new(singleton)) as *const _;
}
// Now we give out a copy of the data that is safe to use concurrently.
(**s.borrow()).clone()
})
}
@@ -1,91 +0,0 @@
// 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 codec::{KeyedVec, Joiner};
use std::collections::HashMap;
use runtime_io::twox_128;
use runtime_support::Hashable;
use primitives::Block;
use polkadot_primitives::{Balance, BlockNumber, AccountId};
/// 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![].and(&self.approval_ratio)),
(&b"ses:len"[..], vec![].and(&self.session_length)),
(&b"ses:val:len"[..], vec![].and(&(self.validators.len() as u32))),
(&b"sta:wil:len"[..], vec![].and(&0u32)),
(&b"sta:spe"[..], vec![].and(&self.sessions_per_era)),
(&b"sta:vac"[..], vec![].and(&(self.validators.len() as u32))),
(&b"sta:era"[..], vec![].and(&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![].and(account)))
).chain(self.authorities.iter()
.enumerate()
.map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].and(account)))
).chain(self.balances.iter()
.map(|&(account, balance)| (account.to_keyed_vec(b"sta:bal:"), vec![].and(&balance)))
)
.map(|(k, v)| (twox_128(&k[..])[..].to_vec(), v.to_vec()))
.chain(vec![
(b":code"[..].into(), wasm_runtime),
(b":auth:len"[..].into(), vec![].and(&(self.authorities.len() as u32))),
].into_iter())
.chain(self.authorities.iter()
.enumerate()
.map(|(i, account)| ((i as u32).to_keyed_vec(b":auth:"), vec![].and(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().encode()
]
}
+269 -65
View File
@@ -14,84 +14,288 @@
// 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.
//! 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;
extern crate substrate_runtime_support as runtime_support;
extern crate substrate_codec as codec;
extern crate substrate_misbehavior_check as misbehavior_check;
extern crate polkadot_primitives;
#[cfg(all(feature = "std", test))]
extern crate substrate_keyring as keyring;
#[cfg(feature = "std")]
extern crate rustc_hex;
#[cfg_attr(any(test, feature = "std"), macro_use)]
extern crate substrate_primitives as primitives;
#[macro_use]
extern crate substrate_runtime_io as runtime_io;
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
extern crate substrate_runtime_support as runtime_support;
pub mod api;
pub mod environment;
pub mod runtime;
#[macro_use]
extern crate substrate_runtime_primitives as runtime_primitives;
#[cfg(feature = "std")]
pub mod genesismap;
#[cfg(test)]
extern crate substrate_serializer;
/// Type definitions and helpers for transactions.
pub mod transaction {
use rstd::ops;
use polkadot_primitives::Signature;
pub use polkadot_primitives::{Transaction, Function, UncheckedTransaction};
#[cfg_attr(feature = "std", macro_use)]
extern crate substrate_primitives;
/// A type-safe indicator that a transaction has been checked.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct CheckedTransaction(UncheckedTransaction);
extern crate substrate_runtime_std as rstd;
extern crate substrate_codec as codec;
extern crate substrate_runtime_consensus as consensus;
extern crate substrate_runtime_council as council;
extern crate substrate_runtime_democracy as democracy;
extern crate substrate_runtime_executive as executive;
extern crate substrate_runtime_session as session;
extern crate substrate_runtime_staking as staking;
extern crate substrate_runtime_system as system;
extern crate substrate_runtime_timestamp as timestamp;
extern crate polkadot_primitives;
impl CheckedTransaction {
/// Get a reference to the checked signature.
pub fn signature(&self) -> &Signature {
&self.0.signature
}
mod parachains;
use runtime_io::BlakeTwo256;
use polkadot_primitives::{AccountId, Balance, BlockNumber, Hash, Index, Log, SessionKey, Signature};
use runtime_primitives::generic;
use runtime_primitives::traits::{Identity, HasPublicAux};
#[cfg(feature = "std")] pub use runtime_primitives::BuildExternalities;
/// Concrete runtime type used to parameterize the various modules.
pub struct Concrete;
impl HasPublicAux for Concrete {
type PublicAux = AccountId; // TODO: Option<AccountId>
}
impl system::Trait for Concrete {
type Index = Index;
type BlockNumber = BlockNumber;
type Hash = Hash;
type Hashing = BlakeTwo256;
type Digest = generic::Digest<Log>;
type AccountId = AccountId;
type Header = generic::Header<BlockNumber, Hash, Log>;
}
/// System module for this concrete runtime.
pub type System = system::Module<Concrete>;
impl consensus::Trait for Concrete {
type PublicAux = <Self as HasPublicAux>::PublicAux;
type SessionKey = SessionKey;
}
/// Consensus module for this concrete runtime.
pub type Consensus = consensus::Module<Concrete>;
pub use consensus::Call as ConsensusCall;
impl timestamp::Trait for Concrete {
type Value = u64;
}
/// Timestamp module for this concrete runtime.
pub type Timestamp = timestamp::Module<Concrete>;
pub use timestamp::Call as TimestampCall;
impl session::Trait for Concrete {
type ConvertAccountIdToSessionKey = Identity;
}
/// Session module for this concrete runtime.
pub type Session = session::Module<Concrete>;
impl staking::Trait for Concrete {
type Balance = Balance;
type DetermineContractAddress = BlakeTwo256;
}
/// Staking module for this concrete runtime.
pub type Staking = staking::Module<Concrete>;
impl democracy::Trait for Concrete {
type Proposal = PrivCall;
}
/// Democracy module for this concrete runtime.
pub type Democracy = democracy::Module<Concrete>;
impl council::Trait for Concrete {}
/// Council module for this concrete runtime.
pub type Council = council::Module<Concrete>;
/// Council voting module for this concrete runtime.
pub type CouncilVoting = council::voting::Module<Concrete>;
impl parachains::Trait for Concrete {}
pub type Parachains = parachains::Module<Concrete>;
impl_outer_dispatch! {
pub enum Call where aux: <Concrete as HasPublicAux>::PublicAux {
Consensus = 0,
Session = 1,
Staking = 2,
Timestamp = 3,
Democracy = 5,
Council = 6,
CouncilVoting = 7,
}
impl ops::Deref for CheckedTransaction {
type Target = Transaction;
fn deref(&self) -> &Transaction {
&self.0.transaction
}
}
/// Check the validity of a transaction: whether it can appear at the given index
/// and whether it is correctly authenticated.
pub fn check(tx: UncheckedTransaction, index: u64) -> Result<CheckedTransaction, UncheckedTransaction> {
match tx.transaction.function.inherent_index() {
Some(correct_index) => {
if index != correct_index || !tx.is_well_formed() { return Err(tx) }
return Ok(CheckedTransaction(tx));
}
None => {
// non-inherent functions must appear after inherent.
if index < Function::inherent_functions() { return Err(tx) }
}
}
let msg = ::codec::Slicable::encode(&tx.transaction);
if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) {
Ok(CheckedTransaction(tx))
} else {
Err(tx)
}
pub enum PrivCall {
Consensus = 0,
Session = 1,
Staking = 2,
Democracy = 5,
Council = 6,
CouncilVoting = 7,
}
}
/// Block header type as expected by this runtime.
pub type Header = generic::Header<BlockNumber, Hash, Log>;
/// Block type as expected by this runtime.
pub type Block = generic::Block<BlockNumber, Hash, Log, AccountId, Index, Call, Signature>;
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<AccountId, Index, Call, Signature>;
/// Extrinsic type as expected by this runtime.
pub type Extrinsic = generic::Extrinsic<AccountId, Index, Call>;
/// Executive: handles dispatch to the various modules.
pub type Executive = executive::Executive<Concrete, Block, Staking,
((((((), Parachains), Council), Democracy), Staking), Session)>;
impl_outer_config! {
pub struct GenesisConfig for Concrete {
ConsensusConfig => consensus,
SystemConfig => system,
SessionConfig => session,
StakingConfig => staking,
DemocracyConfig => democracy,
CouncilConfig => council,
ParachainsConfig => parachains,
}
}
pub mod api {
impl_stubs!(
authorities => |()| super::Consensus::authorities(),
initialise_block => |header| super::Executive::initialise_block(&header),
apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic),
execute_block => |block| super::Executive::execute_block(block),
finalise_block => |()| super::Executive::finalise_block(),
validator_count => |()| super::Session::validator_count(),
validators => |()| super::Session::validators()
);
}
#[cfg(test)]
mod tests {
use super::*;
use substrate_primitives as primitives;
use ::codec::Slicable;
use substrate_primitives::hexdisplay::HexDisplay;
use substrate_serializer as ser;
use runtime_primitives::traits::{Digest as DigestT, Header as HeaderT};
type Digest = generic::Digest<Log>;
#[test]
fn test_header_serialization() {
let header = Header {
parent_hash: 5.into(),
number: 67,
state_root: 3.into(),
extrinsics_root: 6.into(),
digest: { let mut d = Digest::default(); d.push(Log(vec![1])); d },
};
assert_eq!(ser::to_string_pretty(&header), r#"{
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005",
"number": 67,
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003",
"extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006",
"digest": {
"logs": [
"0x01"
]
}
}"#);
let v = header.encode();
assert_eq!(Header::decode(&mut &v[..]).unwrap(), header);
}
#[test]
fn block_encoding_round_trip() {
let mut block = Block {
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
extrinsics: vec![
UncheckedExtrinsic {
extrinsic: Extrinsic {
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
signed: Default::default(),
index: Default::default(),
},
signature: Default::default(),
}
],
};
let raw = block.encode();
let decoded = Block::decode(&mut &raw[..]).unwrap();
assert_eq!(block, decoded);
block.extrinsics.push(UncheckedExtrinsic {
extrinsic: Extrinsic {
function: Call::Staking(staking::Call::stake()),
signed: Default::default(),
index: 10101,
},
signature: Default::default(),
});
let raw = block.encode();
let decoded = Block::decode(&mut &raw[..]).unwrap();
assert_eq!(block, decoded);
}
#[test]
fn block_encoding_substrate_round_trip() {
let mut block = Block {
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
extrinsics: vec![
UncheckedExtrinsic {
extrinsic: Extrinsic {
function: Call::Timestamp(timestamp::Call::set(100_000_000)),
signed: Default::default(),
index: Default::default(),
},
signature: Default::default(),
}
],
};
block.extrinsics.push(UncheckedExtrinsic {
extrinsic: Extrinsic {
function: Call::Staking(staking::Call::stake()),
signed: Default::default(),
index: 10101,
},
signature: Default::default(),
});
let raw = block.encode();
let decoded_substrate = primitives::block::Block::decode(&mut &raw[..]).unwrap();
let encoded_substrate = decoded_substrate.encode();
let decoded = Block::decode(&mut &encoded_substrate[..]).unwrap();
assert_eq!(block, decoded);
}
#[test]
fn serialize_unchecked() {
let tx = UncheckedExtrinsic {
extrinsic: Extrinsic {
signed: [1; 32],
index: 999u64,
function: Call::Timestamp(TimestampCall::set(135135)),
},
signature: primitives::hash::H512([0; 64]).into(),
};
// 71000000
// 0101010101010101010101010101010101010101010101010101010101010101
// e703000000000000
// 00
// df0f0200
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
let v = Slicable::encode(&tx);
println!("{}", HexDisplay::from(&v));
assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx);
}
}
@@ -0,0 +1,210 @@
// 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 polkadot_primitives;
#[cfg(any(feature = "std", test))] use {runtime_io, runtime_primitives};
use rstd::prelude::*;
#[cfg(any(feature = "std", test))] use rstd::marker::PhantomData;
use codec::{Slicable, Joiner};
use runtime_support::Hashable;
#[cfg(any(feature = "std", test))] use runtime_support::StorageValue;
use runtime_primitives::traits::Executable;
use polkadot_primitives::parachain::{Id, Chain, DutyRoster};
use {system, session};
pub trait Trait: system::Trait<Hash = polkadot_primitives::Hash> + session::Trait {}
decl_module! {
pub struct Module<T: Trait>;
}
decl_storage! {
pub trait Store for Module<T: Trait>;
// The number of parachains registered at present.
pub Count get(count): b"para:count" => default u32;
}
impl<T: Trait> Module<T> {
/// Calculate the current block's duty roster.
pub fn calculate_duty_roster() -> DutyRoster {
let parachain_count = Self::count();
let validator_count = <session::Module<T>>::validator_count();
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(Id::from(i / validators_per_parachain as u32)),
_ => Chain::Relay,
}).collect::<Vec<_>>();
let mut roles_gua = roles_val.clone();
let h = <system::Module<T>>::random_seed();
let mut seed = h.to_vec().and(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::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining;
let gua_index = u32::decode(&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,
}
}
}
impl<T: Trait> Executable for Module<T> {
fn execute() {
}
}
#[cfg(any(feature = "std", test))]
pub struct GenesisConfig<T: Trait> {
pub count: u32,
pub phantom: PhantomData<T>,
}
#[cfg(any(feature = "std", test))]
impl<T: Trait> Default for GenesisConfig<T> {
fn default() -> Self {
GenesisConfig {
count: 0,
phantom: PhantomData,
}
}
}
#[cfg(any(feature = "std", test))]
impl<T: Trait> runtime_primitives::BuildExternalities for GenesisConfig<T>
{
fn build_externalities(self) -> runtime_io::TestExternalities {
use runtime_io::twox_128;
use codec::Slicable;
map![
twox_128(<Count<T>>::key()).to_vec() => self.count.encode()
]
}
}
#[cfg(test)]
mod tests {
use super::*;
use runtime_io::with_externalities;
use substrate_primitives::H256;
use runtime_primitives::BuildExternalities;
use runtime_primitives::traits::{HasPublicAux, Identity};
use runtime_primitives::testing::{Digest, Header};
use consensus;
pub struct Test;
impl HasPublicAux for Test {
type PublicAux = u64;
}
impl consensus::Trait for Test {
type PublicAux = <Self as HasPublicAux>::PublicAux;
type SessionKey = u64;
}
impl system::Trait for Test {
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = runtime_io::BlakeTwo256;
type Digest = Digest;
type AccountId = u64;
type Header = Header;
}
impl session::Trait for Test {
type ConvertAccountIdToSessionKey = Identity;
}
impl Trait for Test {}
type System = system::Module<Test>;
type Parachains = Module<Test>;
fn new_test_ext() -> runtime_io::TestExternalities {
let mut t = system::GenesisConfig::<Test>::default().build_externalities();
t.extend(consensus::GenesisConfig::<Test>{
code: vec![],
authorities: vec![1, 2, 3],
}.build_externalities());
t.extend(session::GenesisConfig::<Test>{
session_length: 1000,
validators: vec![1, 2, 3, 4, 5, 6, 7, 8],
}.build_externalities());
t.extend(GenesisConfig::<Test>{
count: 2,
phantom: PhantomData,
}.build_externalities());
t
}
#[test]
fn simple_setup_should_work() {
with_externalities(&mut new_test_ext(), || {
assert_eq!(Parachains::count(), 2);
});
}
#[test]
fn should_work() {
with_externalities(&mut new_test_ext(), || {
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).map(Id::from) {
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);
};
System::set_random_seed([0u8; 32].into());
let duty_roster_0 = Parachains::calculate_duty_roster();
check_roster(&duty_roster_0);
System::set_random_seed([1u8; 32].into());
let duty_roster_1 = Parachains::calculate_duty_roster();
check_roster(&duty_roster_1);
assert!(duty_roster_0 != duty_roster_1);
System::set_random_seed([2u8; 32].into());
let duty_roster_2 = Parachains::calculate_duty_roster();
check_roster(&duty_roster_2);
assert!(duty_roster_0 != duty_roster_2);
assert!(duty_roster_1 != duty_roster_2);
});
}
}
@@ -1,48 +0,0 @@
// 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 runtime_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<'a, I: IntoIterator<Item=&'a SessionKey>>(authorities: I) {
AuthorityStorageVec::set_items(authorities);
}
/// Set a single authority by index.
pub fn set_authority(index: u32, key: &SessionKey) {
AuthorityStorageVec::set_item(index, key);
}
}
@@ -1,371 +0,0 @@
// 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 runtime_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 keyring::Keyring;
use environment::with_env;
use polkadot_primitives::{AccountId, Proposal};
use runtime::{staking, session};
fn new_test_ext() -> TestExternalities {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
map![
twox_128(APPROVALS_REQUIRED).to_vec() => vec![].and(&667u32),
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].and(&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![].and(&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![].and(&1u64),
twox_128(b"sta:vac").to_vec() => vec![].and(&3u64),
twox_128(b"sta:era").to_vec() => vec![].and(&1u64)
]
}
#[test]
fn majority_voting_should_work() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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.
system::testing::set_block_number(1);
public::propose(&one, &Proposal::StakingSetSessionsPerEra(2));
public::approve(&four, 1);
staking::internal::check_new_era();
assert_eq!(staking::era_length(), 1);
});
}
}
@@ -1,34 +0,0 @@
// 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
@@ -1,123 +0,0 @@
// 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 codec::{Slicable, Joiner};
use runtime_support::{Hashable, storage};
use environment::with_env;
use runtime::session;
use polkadot_primitives::parachain::{Id, Chain, DutyRoster};
const PARACHAIN_COUNT: &[u8] = b"par:cou";
/// 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(Id::from(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().and(&h).and(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::decode(&mut &seed[offset..offset + 4]).expect("using 4 bytes for a 32-bit quantity") as usize % remaining;
let gua_index = u32::decode(&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 keyring::Keyring;
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
map![
twox_128(b"ses:val:len").to_vec() => vec![].and(&8u32),
twox_128(b"par:cou").to_vec() => vec![].and(&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).map(Id::from) {
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);
});
}
}
@@ -1,294 +0,0 @@
// 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 runtime_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 CURRENT_SESSION_START: &[u8] = b"ses:sta";
const LAST_SESSION_START: &[u8] = b"ses:lst";
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:";
}
// the session keys before the previous.
struct LastValidators;
impl StorageVec for LastValidators {
type Item = (AccountId, SessionKey);
const PREFIX: &'static [u8] = b"ses:old:";
}
/// Get the current set of validators.
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 session index.
pub fn current_index() -> BlockNumber {
storage::get_or(CURRENT_INDEX, 0)
}
/// Get the starting block of the current session.
pub fn current_start_block() -> BlockNumber {
// this seems like it's computable just by examining the current block number, session length,
// and last length change, but it's not simple to tell whether we are before or after
// a session rotation on a block which will have one.
storage::get_or(CURRENT_SESSION_START, 0)
}
/// Get the last session's validators, paired with their authority keys.
pub fn last_session_keys() -> Vec<(AccountId, SessionKey)> {
LastValidators::items()
}
/// Get the start block of the last session.
/// In general this is computable from the session length,
/// but when the current session is the first with a new length it is uncomputable.
pub fn last_session_start() -> Option<BlockNumber> {
storage::get(LAST_SESSION_START)
}
/// 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::*;
/// Transition to a new era, with a new set of valiators.
///
/// 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]) {
LastValidators::set_items(
new.iter().cloned().zip(consensus::authorities())
);
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);
}
let validators = validators();
storage::put(LAST_SESSION_START, &current_start_block());
storage::put(CURRENT_SESSION_START, &system::block_number());
LastValidators::set_items(
validators.iter()
.cloned()
.zip(consensus::authorities())
);
// 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) {
// this is fine because the authorities vector currently
// matches the validators length perfectly.
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 keyring::Keyring;
use environment::with_env;
use polkadot_primitives::AccountId;
use runtime::{consensus, session};
fn simple_setup() -> TestExternalities {
map![
twox_128(SESSION_LENGTH).to_vec() => vec![].and(&2u64),
// the validators (10, 20, ...)
twox_128(b"ses:val:len").to_vec() => vec![].and(&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![].and(&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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_block_number(3);
check_rotate_session();
assert_eq!(length(), 3);
assert_eq!(current_index(), 1);
// Block 4: Change to length 2; no visible change.
system::testing::set_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.
system::testing::set_block_number(5);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 2);
// Block 6: No change.
system::testing::set_block_number(6);
check_rotate_session();
assert_eq!(length(), 2);
assert_eq!(current_index(), 2);
// Block 7: Next index.
system::testing::set_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
system::testing::set_block_number(1);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [21u8; 32]]);
// Block 2: Session rollover, but no change.
system::testing::set_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.
system::testing::set_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.
system::testing::set_block_number(4);
check_rotate_session();
assert_eq!(consensus::authorities(), vec![[11u8; 32], [22u8; 32]]);
});
}
}
@@ -1,496 +0,0 @@
// 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 runtime_support::{storage, StorageVec};
use polkadot_primitives::{BlockNumber, AccountId};
use primitives::bft::{MisbehaviorReport, MisbehaviorKind};
use runtime::{system, session, governance, consensus};
type Balance = u64;
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 TOTAL_STAKE: &[u8] = b"sta:tot";
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))
}
/// Gives the index of the era where the account's balance will no longer
/// be bonded.
pub fn bondage(who: &AccountId) -> Bondage {
storage::get_or_default(&who.to_keyed_vec(BONDAGE_OF))
}
fn set_balance(who: &AccountId, amount: Balance) {
storage::put(&who.to_keyed_vec(BALANCE_OF), &amount)
}
// 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()));
}
/// Report misbehavior. Only validators may do this, signing under
/// the authority key of the session the report corresponds to.
///
/// Reports older than one session in the past will be ignored.
pub fn report_misbehavior(transactor: &AccountId, report: &MisbehaviorReport) {
let (validators, authorities) = if report.parent_number < session::last_session_start().unwrap_or(0) {
panic!("report is too old");
} else if report.parent_number < session::current_start_block() {
session::last_session_keys().into_iter().unzip()
} else {
(session::validators(), consensus::authorities())
};
if report.parent_hash != system::block_hash(report.parent_number) {
// report out of chain.
panic!("report not from this blockchain");
}
let reporting_validator = match authorities.iter().position(|x| x == transactor) {
None => panic!("only validators may report"),
Some(pos) => validators.get(pos).expect("validators and authorities have same cardinality; qed"),
};
// any invalidity beyond this point is actually its own misbehavior.
let target = match authorities.iter().position(|x| x == &report.target) {
None => {
slash(reporting_validator, None);
return;
}
Some(pos) => validators.get(pos).expect("validators and authorities have same cardinality; qed"),
};
let misbehaved = ::misbehavior_check::evaluate_misbehavior(&report.target, report.parent_hash, &report.misbehavior);
if misbehaved {
slash(target, Some(reporting_validator))
} else {
slash(reporting_validator, None);
}
}
}
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();
}
}
}
/// Slash a validator, with an optional benefactor.
fn slash(who: &AccountId, benefactor: Option<&AccountId>) {
// the reciprocal of the proportion of the amount slashed to give
// to the benefactor.
const SLASH_REWARD_DENOMINATOR: Balance = 10;
let slashed = balance(who);
set_balance(who, 0);
if let Some(benefactor) = benefactor {
let reward = slashed / SLASH_REWARD_DENOMINATOR;
let prior = balance(benefactor);
set_balance(benefactor, prior + reward);
}
}
/// 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 keyring::Keyring;
use environment::with_env;
use polkadot_primitives::AccountId;
use runtime::{staking, session};
#[test]
fn staking_should_work() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
let four = [4u8; 32];
let mut t: TestExternalities = map![
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(b"ses:val:len").to_vec() => vec![].and(&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![].and(&2u64),
twox_128(VALIDATOR_COUNT).to_vec() => vec![].and(&2u32),
twox_128(BONDING_DURATION).to_vec() => vec![].and(&3u64),
twox_128(TOTAL_STAKE).to_vec() => vec![].and(&100u64),
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&10u64),
twox_128(&two.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&20u64),
twox_128(&three.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&30u64),
twox_128(&four.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_block_number(3);
stake(&three);
unstake(&four);
check_new_era();
// Block 4: New era - validators change.
system::testing::set_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.
system::testing::set_block_number(5);
transfer(&four, &one, 40);
check_new_era();
// Block 6: Lowest now validator.
system::testing::set_block_number(6);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 7: Unstake three. No change yet.
system::testing::set_block_number(7);
unstake(&three);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), three.clone()]);
// Block 8: Back to one and two.
system::testing::set_block_number(8);
check_new_era();
assert_eq!(session::validators(), vec![one.clone(), two.clone()]);
});
}
#[test]
fn staking_eras_work() {
let mut t: TestExternalities = map![
twox_128(b"ses:len").to_vec() => vec![].and(&1u64),
twox_128(SESSIONS_PER_ERA).to_vec() => vec![].and(&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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_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.
system::testing::set_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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&42u64)
];
with_externalities(&mut t, || {
assert_eq!(balance(&one), 42);
assert_eq!(balance(&two), 0);
});
}
#[test]
fn staking_balance_transfer_works() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&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 = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64)
];
with_externalities(&mut t, || {
stake(&one);
transfer(&one, &two, 69);
});
}
#[test]
#[should_panic]
fn misbehavior_report_by_non_validator_panics() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = map![
twox_128(&one.to_keyed_vec(BALANCE_OF)).to_vec() => vec![].and(&111u64)
];
with_externalities(&mut t, || {
// the misbehavior report here is invalid, but that
// actually doesn't panic; instead it would slash the bad
// reporter.
report_misbehavior(&one, &MisbehaviorReport {
parent_hash: [0; 32].into(),
parent_number: 0,
target: two,
misbehavior: MisbehaviorKind::BftDoubleCommit(
2,
([1; 32].into(), [2; 64].into()),
([3; 32].into(), [4; 64].into()),
),
})
});
}
}
@@ -1,450 +0,0 @@
// 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::mem;
use rstd::prelude::*;
use codec::{KeyedVec, Slicable};
use environment::with_env;
use polkadot_primitives::{
AccountId, Hash, TxOrder, BlockNumber, Block, Header,
UncheckedTransaction, Function, InherentFunction, Log
};
use runtime_io::{print, storage_root, enumerated_trie_root};
use runtime_support::{Hashable, storage};
use runtime::{staking, session};
/// Prefixes account ID and stores u64 nonce.
pub const NONCE_OF: &[u8] = b"sys:non:";
/// Prefixes block number and stores hash of that block.
pub const BLOCK_HASH_AT: &[u8] = b"sys:old:";
/// Stores the temporary current transaction number.
pub const TEMP_TRANSACTION_NUMBER: &[u8] = b"temp:txcount";
/// 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::*;
/// 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 all transactions, inherent or otherwise.
for (tx_num, tx) in block.all_transactions().enumerate() {
super::execute_transaction(tx, tx_num as u64);
}
// 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.
/// Note that when building a block transaction by transaction, the
/// inherent methods must be called manually.
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);
});
let tx_num: u64 = storage::get_or_default(TEMP_TRANSACTION_NUMBER);
super::execute_transaction(utx, tx_num);
with_env(|e| {
mem::swap(&mut header.digest, &mut e.digest);
});
storage::put(TEMP_TRANSACTION_NUMBER, &(tx_num + 1));
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);
});
let tx_count: u64 = storage::take_or_default(TEMP_TRANSACTION_NUMBER);
if tx_count < Function::inherent_functions() {
panic!("Not enough transactions provided to fulfill all inherent functions.");
}
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
}
}
/// Get an account's current nonce.
pub fn nonce(account: AccountId) -> TxOrder {
let nonce_key = account.to_keyed_vec(NONCE_OF);
storage::get_or(&nonce_key, 0)
}
/// Dispatch a function.
fn dispatch_function(function: &Function, transactor: &AccountId) {
match *function {
Function::Inherent(InherentFunction::TimestampSet(t)) => {
::runtime::timestamp::public::set(t);
}
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::ReportMisbehavior(ref report) => {
::runtime::staking::public::report_misbehavior(transactor, report)
}
Function::SessionSetKey(session) => {
::runtime::session::public::set_key(transactor, &session);
}
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, tx_num: u64) {
use ::transaction;
// Verify the transaction is authenticated at its position.
let tx = match transaction::check(utx, tx_num) {
Ok(tx) => tx,
Err(_) => panic!("Transaction at index {} not properly authenticated", tx_num),
};
// check nonce
let nonce_key = tx.signed.to_keyed_vec(NONCE_OF);
let (expected_nonce, increment_nonce) = if !tx.function.is_inherent() {
(storage::get_or(&nonce_key, 0), true)
} else {
(0, false)
};
assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce");
// increment nonce in storage, unless it's the EVERYBODY account.
if increment_nonce {
storage::put(&nonce_key, &(expected_nonce + 1));
}
// decode parameters and dispatch
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.all_transactions().map(|tx| Slicable::encode(&tx)).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 primitives::hexdisplay::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(any(feature = "std", test))]
pub mod testing {
use super::*;
pub fn set_block_number(n: BlockNumber) {
with_env(|e| e.block_number = n);
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::internal::*;
use runtime_io::{with_externalities, twox_128, TestExternalities};
use codec::{Joiner, KeyedVec, Slicable};
use keyring::Keyring;
use environment::with_env;
use primitives::hexdisplay::HexDisplay;
use polkadot_primitives::{Header, Body, Digest, UncheckedTransaction, Transaction, Function, InherentFunction};
use runtime::staking;
fn set_timestamp() -> UncheckedTransaction {
UncheckedTransaction::inherent(InherentFunction::TimestampSet(100_000))
}
#[test]
#[should_panic]
fn fails_if_first_not_timestamp_set() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = 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: hex!("5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05").into(),
};
with_externalities(&mut t, || {
internal::execute_transaction(tx, Header::from_block_number(1));
});
}
#[test]
fn staking_balance_transfer_dispatch_works() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let mut t: TestExternalities = 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: hex!("5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05").into(),
};
with_externalities(&mut t, || {
let header = Header::from_block_number(1);
let header = internal::execute_transaction(set_timestamp(), header);
internal::execute_transaction(tx, header);
assert_eq!(staking::balance(&one), 42);
assert_eq!(staking::balance(&two), 69);
});
}
fn new_test_ext() -> TestExternalities {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
let three = [3u8; 32];
map![
twox_128(&0u64.to_keyed_vec(b"sys:old:")).to_vec() => [69u8; 32].encode(),
twox_128(b"gov:apr").to_vec() => vec![].and(&667u32),
twox_128(b"ses:len").to_vec() => vec![].and(&2u64),
twox_128(b"ses:val:len").to_vec() => vec![].and(&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![].and(&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![].and(&2u64),
twox_128(b"sta:vac").to_vec() => vec![].and(&3u64),
twox_128(b"sta:era").to_vec() => vec![].and(&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 mut t = new_test_ext();
let h = Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("aa4fbcdc09b21e4366aebccd9b9ec0831a8a2765c712d3397f121ff8e60e21e2").into(),
transaction_root: hex!("328ae80be3adf358d2a2e188cbe1bfd3f8cd5b15a2e7666e2b4eccf7450efc32").into(),
digest: Digest { logs: vec![], },
};
let b = Block {
header: h,
body: Body {
timestamp: 100_000,
transactions: vec![]
}
};
with_externalities(&mut t, || {
execute_block(b);
});
}
#[test]
#[should_panic]
fn block_import_of_bad_state_root_fails() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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,
body: Body {
timestamp: 100_000,
transactions: vec![],
}
};
with_externalities(&mut t, || {
execute_block(b);
});
}
#[test]
#[should_panic]
fn block_import_of_bad_transaction_root_fails() {
let one = Keyring::One.to_raw_public();
let two = Keyring::Two.to_raw_public();
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,
body: Body {
timestamp: 100_000,
transactions: vec![],
}
};
with_externalities(&mut t, || {
execute_block(b);
});
}
}
@@ -1,63 +0,0 @@
// 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 runtime_support::storage;
use polkadot_primitives::Timestamp;
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) {
if super::get() > now {
panic!("last timestamp less than now");
}
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 = map![
twox_128(CURRENT_TIMESTAMP).to_vec() => vec![].and(&42u64)
];
with_externalities(&mut t, || {
assert_eq!(get(), 42);
set(69);
assert_eq!(get(), 69);
});
}
}