diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 5261f91b807dfe75f870720481ab8077a36ccc65..2509a54917daadedbf06d62afc093b15cb029bb7 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -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", diff --git a/substrate/native-runtime/Cargo.toml b/substrate/native-runtime/Cargo.toml index a0b874ece13918e94ef79b9ac49d0946fa53fb3b..a4052bf8ded26b0e69fc8a576d93be446c3dbc73 100644 --- a/substrate/native-runtime/Cargo.toml +++ b/substrate/native-runtime/Cargo.toml @@ -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"] diff --git a/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs index a4c8fb99ae2a05290c0e5e78f9ed036a1e5ce246..e3881434ea1a7b464969a8840028f1d714f3e0ee 100644 --- a/substrate/wasm-runtime/polkadot/src/lib.rs +++ b/substrate/wasm-runtime/polkadot/src/lib.rs @@ -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; diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs index 5c846beea7afe74fdbb7e8f2bc1c4785852c8a2b..6ab07169a7536c9776c47f7dd2697276fc3011b4 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs @@ -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()); diff --git a/substrate/wasm-runtime/polkadot/src/runtime/mod.rs b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs index ad870dba49ae098fc10922b2328a2b98c09daa65..ff70fe89284ea2a11331ab2e2bdaea8d9527a279 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/mod.rs +++ b/substrate/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 = "std")] pub mod genesismap; diff --git a/substrate/wasm-runtime/polkadot/src/runtime/parachains.rs b/substrate/wasm-runtime/polkadot/src/runtime/parachains.rs new file mode 100644 index 0000000000000000000000000000000000000000..00da191e57054538fb065f0adb878d9daf7c5318 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/runtime/parachains.rs @@ -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); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/runtime/session.rs b/substrate/wasm-runtime/polkadot/src/runtime/session.rs index 8d55f122ffdfa3a6c68ee0553f5b1c3041f65154..6710e8854aecb38643f2183d23732e649f114ab9 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/session.rs +++ b/substrate/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/substrate/wasm-runtime/polkadot/src/runtime/system.rs b/substrate/wasm-runtime/polkadot/src/runtime/system.rs index 2437fec912dca3739b4331e0aad4fbbbfe729ee2..c47058c8eaea0c4b5d98872422c9b8b86781e071 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/system.rs +++ b/substrate/wasm-runtime/polkadot/src/runtime/system.rs @@ -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::*; diff --git a/substrate/wasm-runtime/polkadot/src/support/environment.rs b/substrate/wasm-runtime/polkadot/src/support/environment.rs index eaae898f5282abc6daf7ab97fe34c0da5a244ed4..3e846b34a60a5e10ff1866fd301cf5915dfa3da0 100644 --- a/substrate/wasm-runtime/polkadot/src/support/environment.rs +++ b/substrate/wasm-runtime/polkadot/src/support/environment.rs @@ -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, }