diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 8291eb6d15c1ef25fe0ae2dc4826c5b66ab48e70..cd135d16f1d3bab77e387206e4ad49ae951a4355 100644
--- a/substrate/Cargo.lock
+++ b/substrate/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/substrate/native-runtime/Cargo.toml b/substrate/native-runtime/Cargo.toml
index 5ee1706e9cf08f630bee634b589cd15c0a2f74b8..2d8f7c7c65a550f4898bb075d6f08e122c39c402 100644
--- a/substrate/native-runtime/Cargo.toml
+++ b/substrate/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/substrate/wasm-runtime/polkadot/src/lib.rs b/substrate/wasm-runtime/polkadot/src/lib.rs
index eefa6e44a875b7605cbd3eb24c59f5521d1c2262..3729611866e0be401d3bacdd3defbde098af7b6d 100644
--- a/substrate/wasm-runtime/polkadot/src/lib.rs
+++ b/substrate/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/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..1c991d292919b10d96a296c5a760a863f6bb6a21
--- /dev/null
+++ b/substrate/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 <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![].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/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..9655b0ea53619244fd0246a415d3adbcde95e58c 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);
+	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/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,
 }