merge duty roster changes

This commit is contained in:
Robert Habermeier
2018-02-06 17:26:52 +01:00
9 changed files with 208 additions and 51 deletions
+1
View File
@@ -826,6 +826,7 @@ name = "native-runtime"
version = "0.1.0"
dependencies = [
"hex-literal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"polkadot-primitives 0.1.0",
"polkadot-runtime-codec 0.1.0",
"polkadot-runtime-std 0.1.0",
+2 -1
View File
@@ -9,7 +9,8 @@ polkadot-runtime-std = { path = "../runtime-std", version = "0.1" }
polkadot-primitives = { path = "../primitives", version = "0.1" }
rustc-hex = "1.0"
hex-literal = "0.1.0"
log = { version = "0.3", optional = true }
[features]
default = ["std"]
std = ["polkadot-runtime-codec/std", "polkadot-runtime-std/std", "polkadot-primitives/std"]
std = ["polkadot-runtime-codec/std", "polkadot-runtime-std/std", "polkadot-primitives/std", "log"]
@@ -23,6 +23,9 @@ extern crate polkadot_runtime_std as runtime_std;
#[cfg(feature = "std")]
extern crate rustc_hex;
#[cfg(feature = "with-std")]
#[macro_use]
extern crate log;
extern crate polkadot_runtime_codec as codec;
extern crate polkadot_primitives as primitives;
@@ -44,7 +44,7 @@ pub fn approval_ppm_required() -> u32 {
/// The number of concrete validator approvals required for a proposal to pass.
pub fn approvals_required() -> u32 {
approval_ppm_required() * session::validator_count() as u32 / 1000
approval_ppm_required() * session::validator_count() / 1000
}
pub mod public {
@@ -185,7 +185,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -210,7 +210,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -243,7 +243,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -268,7 +268,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -294,7 +294,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -321,7 +321,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -349,7 +349,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -373,7 +373,7 @@ mod tests {
with_externalities(&mut t, || {
assert_eq!(staking::era_length(), 1u64);
assert_eq!(staking::current_era(), 1u64);
assert_eq!(session::validator_count(), 3usize);
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());
@@ -28,10 +28,10 @@ pub mod timestamp;
pub mod session;
#[allow(unused)]
pub mod governance;
#[allow(unused)]
pub mod parachains;
// TODO: polkadao
// TODO: parachains
#[cfg(feature = "std")]
pub mod genesismap;
@@ -0,0 +1,141 @@
// 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 runtime_std::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_std::{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);
});
}
}
@@ -46,8 +46,8 @@ pub fn length() -> BlockNumber {
}
/// The number of validators currently.
pub fn validator_count() -> usize {
ValidatorStorageVec::count() as usize
pub fn validator_count() -> u32 {
ValidatorStorageVec::count() as u32
}
/// The current era index.
@@ -64,41 +64,23 @@ pub mod internal {
// populate environment from header.
with_env(|e| {
e.block_number = block.header.number;
e.parent_hash = block.header.parent_hash;
});
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();
debug_assert_hash(&header.transaction_root, &txs_root);
assert!(header.transaction_root == txs_root, "Transaction trie root must be valid.");
// any initial checks
initial_checks(&block);
// execute transactions
for tx in &block.transactions {
super::execute_transaction(tx.clone());
}
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);
// check storage root.
let storage_root = storage_root().into();
debug_assert_hash(&header.state_root, &storage_root);
assert!(header.state_root == storage_root, "Storage root must match that calculated.");
// any stuff that we do after taking the storage root.
post_finalise(header);
post_finalise(&block.header);
}
/// Execute a transaction outside of the block execution function.
@@ -107,6 +89,7 @@ pub mod internal {
// 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);
});
@@ -160,17 +143,6 @@ pub mod internal {
}
}
}
#[cfg(feature = "with-std")]
fn debug_assert_hash(given: &Hash, expected: &Hash) {
use support::HexDisplay;
if given != expected {
println!("Hash: given={}, expected={}", HexDisplay::from(given), HexDisplay::from(expected));
}
}
#[cfg(not(feature = "with-std"))]
fn debug_assert_hash(_given: &Hash, _expected: &Hash) {}
}
fn execute_transaction(utx: UncheckedTransaction) {
@@ -194,10 +166,35 @@ fn execute_transaction(utx: UncheckedTransaction) {
internal::dispatch_function(&tx.function, &tx.signed);
}
fn final_checks(_block: &Block) {
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!(_block.header.digest == e.digest);
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) {
@@ -206,6 +203,17 @@ fn post_finalise(header: &Header) {
storage::put(&header.number.to_keyed_vec(BLOCK_HASH_AT), &header.blake2_256());
}
#[cfg(feature = "with-std")]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
use support::HexDisplay;
if given != expected {
info!("Hash: given={}, expected={}", HexDisplay::from(given), HexDisplay::from(expected));
}
}
#[cfg(not(feature = "with-std"))]
fn info_expect_equal_hash(_given: &Hash, _expected: &Hash) {}
#[cfg(test)]
mod tests {
use super::*;
@@ -22,12 +22,15 @@ use runtime_std::cell::RefCell;
use runtime_std::rc::Rc;
use primitives::block::{Number as BlockNumber, Digest};
use primitives::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,
}