diff --git a/Cargo.lock b/Cargo.lock
index 8291eb6d15c1e..cd135d16f1d3b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -786,6 +786,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.8 (registry+https://github.com/rust-lang/crates.io-index)",
"runtime-std 0.1.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/native-runtime/Cargo.toml b/native-runtime/Cargo.toml
index 5ee1706e9cf08..2d8f7c7c65a55 100644
--- a/native-runtime/Cargo.toml
+++ b/native-runtime/Cargo.toml
@@ -12,3 +12,4 @@ without-std = []
runtime-std = { path = "./std", version = "0.1" }
rustc-hex = "1.0"
hex-literal = "0.1.0"
+log = "0.3"
diff --git a/wasm-runtime/polkadot/src/lib.rs b/wasm-runtime/polkadot/src/lib.rs
index eefa6e44a875b..3729611866e0b 100644
--- a/wasm-runtime/polkadot/src/lib.rs
+++ b/wasm-runtime/polkadot/src/lib.rs
@@ -24,6 +24,9 @@ extern crate runtime_std;
#[cfg(feature = "with-std")]
extern crate rustc_hex;
+#[cfg(feature = "with-std")]
+#[macro_use]
+extern crate log;
#[cfg(test)]
#[macro_use]
diff --git a/wasm-runtime/polkadot/src/runtime/governance.rs b/wasm-runtime/polkadot/src/runtime/governance.rs
index 49605ce3eff6a..da01c8a54b0ed 100644
--- a/wasm-runtime/polkadot/src/runtime/governance.rs
+++ b/wasm-runtime/polkadot/src/runtime/governance.rs
@@ -43,7 +43,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 {
@@ -152,7 +152,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());
@@ -178,7 +178,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());
@@ -213,7 +213,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());
@@ -239,7 +239,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());
@@ -266,7 +266,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());
@@ -324,7 +324,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());
@@ -348,7 +348,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());
diff --git a/wasm-runtime/polkadot/src/runtime/mod.rs b/wasm-runtime/polkadot/src/runtime/mod.rs
index 664cbac8c095b..99aa0b86c24f4 100644
--- a/wasm-runtime/polkadot/src/runtime/mod.rs
+++ b/wasm-runtime/polkadot/src/runtime/mod.rs
@@ -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 = "with-std")]
pub mod genesismap;
diff --git a/wasm-runtime/polkadot/src/runtime/parachains.rs b/wasm-runtime/polkadot/src/runtime/parachains.rs
new file mode 100644
index 0000000000000..1c991d292919b
--- /dev/null
+++ b/wasm-runtime/polkadot/src/runtime/parachains.rs
@@ -0,0 +1,142 @@
+// Copyright 2017 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot. If not, see .
+
+//! 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,
+ /// Lookup from validator index to chain on which that validator has a duty to guarantee
+ /// availability.
+ pub guarantor_duty: Vec,
+}
+
+/// 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::>();
+ let mut roles_gua = roles_val.clone();
+
+ let h = with_env(|e| e.parent_hash.clone());
+ let mut seed = vec![].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(&seed[offset..offset + 4]).expect("using 4 bytes for a 32-byte quantity") as usize % remaining;
+ let gua_index = u32::from_slice(&seed[offset + 4..offset + 8]).expect("using 4 bytes for a 32-byte 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 primitives::AccountID;
+ 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]);
+ let duty_roster_0 = calculate_duty_roster();
+ check_roster(&duty_roster_0);
+
+ with_env(|e| e.parent_hash = [1u8; 32]);
+ 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]);
+ 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);
+ });
+ }
+}
diff --git a/wasm-runtime/polkadot/src/runtime/session.rs b/wasm-runtime/polkadot/src/runtime/session.rs
index 5ebc444f8f3b4..1d1b4f3afd717 100644
--- a/wasm-runtime/polkadot/src/runtime/session.rs
+++ b/wasm-runtime/polkadot/src/runtime/session.rs
@@ -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.
diff --git a/wasm-runtime/polkadot/src/runtime/system.rs b/wasm-runtime/polkadot/src/runtime/system.rs
index 5daf530cb4707..9655b0ea53619 100644
--- a/wasm-runtime/polkadot/src/runtime/system.rs
+++ b/wasm-runtime/polkadot/src/runtime/system.rs
@@ -60,41 +60,24 @@ 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::>();
- let txs = txs.iter().map(Vec::as_slice).collect::>();
- let txs_root = enumerated_trie_root(&txs);
- 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);
- }
+ block.transactions.iter().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();
- 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.
@@ -103,6 +86,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);
});
@@ -129,17 +113,6 @@ pub mod internal {
header
}
-
- #[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) {
@@ -160,10 +133,35 @@ fn execute_transaction(utx: &UncheckedTransaction) {
tx.function.dispatch(&tx.signed, &tx.input_data);
}
-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::>();
+ let txs = txs.iter().map(Vec::as_slice).collect::>();
+ let txs_root = enumerated_trie_root(&txs);
+ 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();
+ 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) {
@@ -172,6 +170,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::*;
diff --git a/wasm-runtime/polkadot/src/support/environment.rs b/wasm-runtime/polkadot/src/support/environment.rs
index 3f652530a2b3e..833825619a0fe 100644
--- a/wasm-runtime/polkadot/src/support/environment.rs
+++ b/wasm-runtime/polkadot/src/support/environment.rs
@@ -21,13 +21,15 @@ use runtime_std::mem;
use runtime_std::cell::RefCell;
use runtime_std::rc::Rc;
-use primitives::{BlockNumber, Digest};
+use 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,
}