diff --git a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs b/substrate/wasm-runtime/polkadot/src/runtime/governance.rs index 49605ce3eff6a1be77b529f25587015799cbab60..da01c8a54b0ed69b5454aaaa66ea47a9d699d26d 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/governance.rs +++ b/substrate/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/substrate/wasm-runtime/polkadot/src/runtime/mod.rs b/substrate/wasm-runtime/polkadot/src/runtime/mod.rs index 664cbac8c095ba49fb5f8e20ac2cb6b78a65a4ed..99aa0b86c24f4a0c1944d75bcc9209854dbf3cc7 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 = "with-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..61ee0f57856a01a0991b48814212537ff08247d7 --- /dev/null +++ b/substrate/wasm-runtime/polkadot/src/runtime/parachains.rs @@ -0,0 +1,165 @@ +// 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 validators_on_relay = validator_count - validators_per_parachain * parachain_count; + + let mut roles_val = (0..validator_count).map(|i| match i / validators_per_parachain { + i if i < parachain_count => Chain::Parachain(i 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![].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, || { + with_env(|e| e.parent_hash = [0u8; 32]); + let duty_roster = calculate_duty_roster(); + + 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); + assert_eq!(duty_roster, DutyRoster { + validator_duty: vec![Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Relay, Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0)], + guarantor_duty: vec![Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Parachain(1), Chain::Relay], + }); + + with_env(|e| e.parent_hash = [1u8; 32]); + let duty_roster = calculate_duty_roster(); + + 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); + assert_eq!(duty_roster, DutyRoster { + validator_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(1), Chain::Relay, Chain::Parachain(0), Chain::Relay], + guarantor_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Relay], + }); + + with_env(|e| e.parent_hash = [2u8; 32]); + let duty_roster = calculate_duty_roster(); + + 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); + assert_eq!(duty_roster, DutyRoster { + validator_duty: vec![Chain::Parachain(1), Chain::Parachain(0), Chain::Relay, Chain::Parachain(0), Chain::Parachain(0), Chain::Relay, Chain::Parachain(1), Chain::Parachain(1)], + guarantor_duty: vec![Chain::Parachain(0), Chain::Parachain(1), Chain::Parachain(1), Chain::Parachain(1), Chain::Relay, Chain::Parachain(0), Chain::Parachain(0), Chain::Relay], + }); + }); + } +} diff --git a/substrate/wasm-runtime/polkadot/src/runtime/session.rs b/substrate/wasm-runtime/polkadot/src/runtime/session.rs index 5ebc444f8f3b4d6de5c71f34357eda59e32e5270..1d1b4f3afd717898580687ab6c31f48a800c92d9 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 5daf530cb4707e3604468eec03d9fce068952f3b..acde21aa751d9d04372d7dea838a86f2df9e7185 100644 --- a/substrate/wasm-runtime/polkadot/src/runtime/system.rs +++ b/substrate/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::<Vec<_>>(); - let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>(); - 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::<Vec<_>>(); + let txs = txs.iter().map(Vec::as_slice).collect::<Vec<_>>(); + 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."); +} + +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(); + debug_assert_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 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) {} + #[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 3f652530a2b3e6f9947f1b19124a33ab3736f93c..833825619a0fe45541549f11f92c25521c5b7a0f 100644 --- a/substrate/wasm-runtime/polkadot/src/support/environment.rs +++ b/substrate/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, }