diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 31433d65cf51de0ac9a689cf3a41ea700baf28a6..c0c3ef6b80b893771e46671c5e5f57a5a27d3745 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -1727,7 +1727,7 @@ dependencies = [
  "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "unsigned-varint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "wasm-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2862,7 +2862,7 @@ dependencies = [
  "fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "impl-codec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "impl-serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -5586,7 +5586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "uint"
-version = "0.8.0"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -6470,7 +6470,7 @@ dependencies = [
 "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
 "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
 "checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874"
-"checksum uint 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5375d2c574f89adad4108ad525c93e39669853a602560bf5ed4ca9943b10799"
+"checksum uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8f0f47ed099f0db671ce82c66548c5de012e3c0cba3963514d1db15c7588701"
 "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
 "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6"
 "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs
index 06e57873289011ce891191da95bd6d85080eecd4..e2e36fdbfba15b1136ef0a3b10bc7f5558fbffd2 100644
--- a/substrate/core/consensus/aura/src/lib.rs
+++ b/substrate/core/consensus/aura/src/lib.rs
@@ -225,7 +225,12 @@ impl<H, B, C, E, I, P, Error, SO> slots::SimpleSlotWorker<B> for AuraWorker<C, E
 		epoch_data.len()
 	}
 
-	fn claim_slot(&self, slot_number: u64, epoch_data: &Self::EpochData) -> Option<Self::Claim> {
+	fn claim_slot(
+		&self,
+		_header: &B::Header,
+		slot_number: u64,
+		epoch_data: &Self::EpochData,
+	) -> Option<Self::Claim> {
 		let expected_author = slot_author::<P>(slot_number, epoch_data);
 
 		expected_author.and_then(|p| {
diff --git a/substrate/core/consensus/babe/primitives/src/digest.rs b/substrate/core/consensus/babe/primitives/src/digest.rs
index 3b6e3221bd8a87ab5aa1fe399c4ba5e8edb44e5e..427c4fec57e7df3ee1d435703e340e9bd2c3e315 100644
--- a/substrate/core/consensus/babe/primitives/src/digest.rs
+++ b/substrate/core/consensus/babe/primitives/src/digest.rs
@@ -22,7 +22,7 @@ use super::AuthoritySignature;
 use super::{BABE_ENGINE_ID, Epoch};
 #[cfg(not(feature = "std"))]
 use super::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH};
-use super::SlotNumber;
+use super::{AuthorityIndex, BabeBlockWeight, SlotNumber};
 #[cfg(feature = "std")]
 use sr_primitives::{DigestItem, generic::OpaqueDigestItemId};
 #[cfg(feature = "std")]
@@ -36,18 +36,61 @@ use schnorrkel::{
 	vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}
 };
 
-/// A BABE pre-digest
+/// A BABE pre-runtime digest. This contains all data required to validate a
+/// block and for the BABE runtime module. Slots can be assigned to a primary
+/// (VRF based) and to a secondary (slot number based).
 #[cfg(feature = "std")]
 #[derive(Clone, Debug)]
-pub struct BabePreDigest {
-	/// VRF output
-	pub vrf_output: VRFOutput,
-	/// VRF proof
-	pub vrf_proof: VRFProof,
-	/// Authority index
-	pub authority_index: super::AuthorityIndex,
-	/// Slot number
-	pub slot_number: SlotNumber,
+pub enum BabePreDigest {
+	/// A primary VRF-based slot assignment.
+	Primary {
+		/// VRF output
+		vrf_output: VRFOutput,
+		/// VRF proof
+		vrf_proof: VRFProof,
+		/// Authority index
+		authority_index: super::AuthorityIndex,
+		/// Slot number
+		slot_number: SlotNumber,
+		/// Chain weight (measured in number of Primary blocks)
+		weight: BabeBlockWeight,
+	},
+	/// A secondary deterministic slot assignment.
+	Secondary {
+		/// Authority index
+		authority_index: super::AuthorityIndex,
+		/// Slot number
+		slot_number: SlotNumber,
+		/// Chain weight (measured in number of Primary blocks)
+		weight: BabeBlockWeight,
+	},
+}
+
+#[cfg(feature = "std")]
+impl BabePreDigest {
+	/// Returns the slot number of the pre digest.
+	pub fn authority_index(&self) -> AuthorityIndex {
+		match self {
+			BabePreDigest::Primary { authority_index, .. } => *authority_index,
+			BabePreDigest::Secondary { authority_index, .. } => *authority_index,
+		}
+	}
+
+	/// Returns the slot number of the pre digest.
+	pub fn slot_number(&self) -> SlotNumber {
+		match self {
+			BabePreDigest::Primary { slot_number, .. } => *slot_number,
+			BabePreDigest::Secondary { slot_number, .. } => *slot_number,
+		}
+	}
+
+	/// Returns the weight of the pre digest.
+	pub fn weight(&self) -> BabeBlockWeight {
+		match self {
+			BabePreDigest::Primary { weight, .. } => *weight,
+			BabePreDigest::Secondary { weight, .. } => *weight,
+		}
+	}
 }
 
 /// The prefix used by BABE for its VRF keys.
@@ -55,27 +98,74 @@ pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf";
 
 /// A raw version of `BabePreDigest`, usable on `no_std`.
 #[derive(Copy, Clone, Encode, Decode)]
-pub struct RawBabePreDigest {
-	/// Slot number
-	pub slot_number: SlotNumber,
-	/// Authority index
-	pub authority_index: super::AuthorityIndex,
-	/// VRF output
-	pub vrf_output: [u8; VRF_OUTPUT_LENGTH],
-	/// VRF proof
-	pub vrf_proof: [u8; VRF_PROOF_LENGTH],
+pub enum RawBabePreDigest {
+	/// A primary VRF-based slot assignment.
+	Primary {
+		/// Authority index
+		authority_index: AuthorityIndex,
+		/// Slot number
+		slot_number: SlotNumber,
+		/// Chain weight (measured in number of Primary blocks)
+		weight: BabeBlockWeight,
+		/// VRF output
+		vrf_output: [u8; VRF_OUTPUT_LENGTH],
+		/// VRF proof
+		vrf_proof: [u8; VRF_PROOF_LENGTH],
+	},
+	/// A secondary deterministic slot assignment.
+	Secondary {
+		/// Authority index
+		authority_index: AuthorityIndex,
+		/// Slot number
+		slot_number: SlotNumber,
+		/// Chain weight (measured in number of Primary blocks)
+		weight: BabeBlockWeight,
+	},
+}
+
+impl RawBabePreDigest {
+	/// Returns the slot number of the pre digest.
+	pub fn slot_number(&self) -> SlotNumber {
+		match self {
+			RawBabePreDigest::Primary { slot_number, .. } => *slot_number,
+			RawBabePreDigest::Secondary { slot_number, .. } => *slot_number,
+		}
+	}
 }
 
 #[cfg(feature = "std")]
 impl Encode for BabePreDigest {
 	fn encode(&self) -> Vec<u8> {
-		let tmp =  RawBabePreDigest {
-			vrf_output: *self.vrf_output.as_bytes(),
-			vrf_proof: self.vrf_proof.to_bytes(),
-			authority_index: self.authority_index,
-			slot_number: self.slot_number,
+		let raw = match self {
+			BabePreDigest::Primary {
+				vrf_output,
+				vrf_proof,
+				authority_index,
+				slot_number,
+				weight,
+			} => {
+				RawBabePreDigest::Primary {
+					vrf_output: *vrf_output.as_bytes(),
+					vrf_proof: vrf_proof.to_bytes(),
+					authority_index: *authority_index,
+					slot_number: *slot_number,
+					weight: *weight,
+				}
+			},
+			BabePreDigest::Secondary {
+				authority_index,
+				slot_number,
+				weight,
+			} => {
+				RawBabePreDigest::Secondary {
+					authority_index: *authority_index,
+					slot_number: *slot_number,
+					weight: *weight,
+				}
+			},
 		};
-		codec::Encode::encode(&tmp)
+
+		codec::Encode::encode(&raw)
 	}
 }
 
@@ -85,19 +175,26 @@ impl codec::EncodeLike for BabePreDigest {}
 #[cfg(feature = "std")]
 impl Decode for BabePreDigest {
 	fn decode<R: Input>(i: &mut R) -> Result<Self, Error> {
-		let RawBabePreDigest { vrf_output, vrf_proof, authority_index, slot_number } = Decode::decode(i)?;
-
-		// Verify (at compile time) that the sizes in babe_primitives are correct
-		let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output;
-		let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof;
-		Ok(BabePreDigest {
-			vrf_proof: VRFProof::from_bytes(&vrf_proof)
-				.map_err(convert_error)?,
-			vrf_output: VRFOutput::from_bytes(&vrf_output)
-				.map_err(convert_error)?,
-			authority_index,
-			slot_number,
-		})
+		let pre_digest = match Decode::decode(i)? {
+			RawBabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number, weight } => {
+				// Verify (at compile time) that the sizes in babe_primitives are correct
+				let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output;
+				let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof;
+
+				BabePreDigest::Primary {
+					vrf_proof: VRFProof::from_bytes(&vrf_proof).map_err(convert_error)?,
+					vrf_output: VRFOutput::from_bytes(&vrf_output).map_err(convert_error)?,
+					authority_index,
+					slot_number,
+					weight,
+				}
+			},
+			RawBabePreDigest::Secondary { authority_index, slot_number, weight } => {
+				BabePreDigest::Secondary { authority_index, slot_number, weight }
+			},
+		};
+
+		Ok(pre_digest)
 	}
 }
 
diff --git a/substrate/core/consensus/babe/primitives/src/lib.rs b/substrate/core/consensus/babe/primitives/src/lib.rs
index f4da908080c8b524112827ba935fde7d1c8f1ac5..09ac2f20123ac5b9ba407ae57a451291992b0864 100644
--- a/substrate/core/consensus/babe/primitives/src/lib.rs
+++ b/substrate/core/consensus/babe/primitives/src/lib.rs
@@ -68,7 +68,10 @@ pub type SlotNumber = u64;
 /// The weight of an authority.
 // NOTE: we use a unique name for the weight to avoid conflicts with other
 //       `Weight` types, since the metadata isn't able to disambiguate.
-pub type BabeWeight = u64;
+pub type BabeAuthorityWeight = u64;
+
+/// The weight of a BABE block.
+pub type BabeBlockWeight = u32;
 
 /// BABE epoch information
 #[derive(Decode, Encode, Default, PartialEq, Eq, Clone)]
@@ -81,9 +84,11 @@ pub struct Epoch {
 	/// The duration of this epoch
 	pub duration: SlotNumber,
 	/// The authorities and their weights
-	pub authorities: Vec<(AuthorityId, BabeWeight)>,
+	pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
 	/// Randomness for this epoch
 	pub randomness: [u8; VRF_OUTPUT_LENGTH],
+	/// Whether secondary slot assignments should be used during the epoch.
+	pub secondary_slots: bool,
 }
 
 /// An consensus log item for BABE.
diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs
index 3fbcd84a005d59f6d3720e8f5c88f925121f7c6f..e29de64de2b77d56f460390425fac493c58034e8 100644
--- a/substrate/core/consensus/babe/src/lib.rs
+++ b/substrate/core/consensus/babe/src/lib.rs
@@ -14,9 +14,47 @@
 // You should have received a copy of the GNU General Public License
 // along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
 
-//! # BABE consensus
+//! # BABE (Blind Assignment for Blockchain Extension)
 //!
-//! BABE (Blind Assignment for Blockchain Extension) consensus in Substrate.
+//! BABE is a slot-based block production mechanism which uses a VRF PRNG to
+//! randomly perform the slot allocation. On every slot, all the authorities
+//! generate a new random number with the VRF function and if it is lower than a
+//! given threshold (which is proportional to their weight/stake) they have a
+//! right to produce a block. The proof of the VRF function execution will be
+//! used by other peer to validate the legitimacy of the slot claim.
+//!
+//! The engine is also responsible for collecting entropy on-chain which will be
+//! used to seed the given VRF PRNG. An epoch is a contiguous number of slots
+//! under which we will be using the same authority set. During an epoch all VRF
+//! outputs produced as a result of block production will be collected on an
+//! on-chain randomness pool. Epoch changes are announced one epoch in advance,
+//! i.e. when ending epoch N, we announce the parameters (randomness,
+//! authorities, etc.) for epoch N+2.
+//!
+//! Since the slot assignment is randomized, it is possible that a slot is
+//! assigned to multiple validators in which case we will have a temporary fork,
+//! or that a slot is assigned to no validator in which case no block is
+//! produced. Which means that block times are not deterministic.
+//!
+//! The protocol has a parameter `c` [0, 1] for which `1 - c` is the probability
+//! of a slot being empty. The choice of this parameter affects the security of
+//! the protocol relating to maximum tolerable network delays.
+//!
+//! In addition to the VRF-based slot assignment described above, which we will
+//! call primary slots, the engine also supports a deterministic secondary slot
+//! assignment. Primary slots take precedence over secondary slots, when
+//! authoring the node starts by trying to claim a primary slot and falls back
+//! to a secondary slot claim attempt. The secondary slot assignment is done
+//! by picking the authority at index:
+//!
+//! `blake2_256(epoch_randomness ++ slot_number) % authorities_len`.
+//!
+//! The fork choice rule is weight-based, where weight equals the number of
+//! primary blocks in the chain. We will pick the heaviest chain (more primary
+//! blocks) and will go with the longest one in case of a tie.
+//!
+//! An in-depth description and analysis of the protocol can be found here:
+//! <https://research.web3.foundation/en/latest/polkadot/BABE/Babe>
 
 #![forbid(unsafe_code, missing_docs)]
 pub use babe_primitives::*;
@@ -36,7 +74,7 @@ use sr_primitives::traits::{
 use keystore::KeyStorePtr;
 use codec::{Decode, Encode};
 use parking_lot::{Mutex, MutexGuard};
-use primitives::{Blake2Hasher, H256, Pair, Public};
+use primitives::{blake2_256, Blake2Hasher, H256, Pair, Public, U256};
 use merlin::Transcript;
 use inherents::{InherentDataProviders, InherentData};
 use substrate_telemetry::{
@@ -47,7 +85,7 @@ use substrate_telemetry::{
 use schnorrkel::{
 	keys::Keypair,
 	vrf::{
-		VRFProof, VRFProofBatchable, VRFInOut,
+		VRFProof, VRFInOut, VRFOutput,
 	},
 };
 use consensus_common::{
@@ -238,7 +276,7 @@ impl<H, B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<C, E, I
 	Error: std::error::Error + Send + From<::consensus_common::Error> + From<I::Error> + 'static,
 {
 	type EpochData = Epoch;
-	type Claim = (VRFInOut, VRFProof, u32, AuthorityPair);
+	type Claim = (BabePreDigest, AuthorityPair);
 	type SyncOracle = SO;
 	type Proposer = E::Proposer;
 	type BlockImport = I;
@@ -260,27 +298,29 @@ impl<H, B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<C, E, I
 		epoch_data.authorities.len()
 	}
 
-	fn claim_slot(&self, slot_number: u64, epoch_data: &Self::EpochData) -> Option<Self::Claim> {
+	fn claim_slot(
+		&self,
+		header: &B::Header,
+		slot_number: u64,
+		epoch_data: &Self::EpochData,
+	) -> Option<Self::Claim> {
+		let parent_weight = {
+			let pre_digest = find_pre_digest::<B>(&header).ok()?;
+			pre_digest.weight()
+		};
+
 		claim_slot(
 			slot_number,
+			parent_weight,
 			epoch_data,
 			self.c,
 			&self.keystore,
-		).map(|((inout, vrf_proof, _), authority_index, key)| {
-			(inout, vrf_proof, authority_index as u32, key)
-		})
+		)
 	}
 
-	fn pre_digest_data(&self, slot_number: u64, claim: &Self::Claim) -> Vec<sr_primitives::DigestItem<B::Hash>> {
-		let inherent_digest = BabePreDigest {
-			vrf_proof: claim.1.clone(),
-			vrf_output: claim.0.to_output(),
-			authority_index: claim.2,
-			slot_number,
-		};
-
+	fn pre_digest_data(&self, _slot_number: u64, claim: &Self::Claim) -> Vec<sr_primitives::DigestItem<B::Hash>> {
 		vec![
-			<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(inherent_digest),
+			<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(claim.0.clone()),
 		]
 	}
 
@@ -290,12 +330,15 @@ impl<H, B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<C, E, I
 		Vec<B::Extrinsic>,
 		Self::Claim,
 	) -> consensus_common::BlockImportParams<B> + Send> {
-		Box::new(|header, header_hash, body, (_, _, _, pair)| {
+		Box::new(|header, header_hash, body, (_, pair)| {
 			// sign the pre-sealed hash of the block and then
 			// add it to a digest item.
 			let signature = pair.sign(header_hash.as_ref());
 			let signature_digest_item = <DigestItemFor<B> as CompatibleDigestItem>::babe_seal(signature);
 
+			// When we building our own blocks we always author on top of the
+			// current best according to `SelectChain`, therefore our own block
+			// proposal should always become the new best.
 			BlockImportParams {
 				origin: BlockOrigin::Own,
 				header,
@@ -304,7 +347,7 @@ impl<H, B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<C, E, I
 				body: Some(body),
 				finalized: false,
 				auxiliary: Vec::new(),
-				fork_choice: ForkChoiceStrategy::LongestChain,
+				fork_choice: ForkChoiceStrategy::Custom(true),
 			}
 		})
 	}
@@ -356,6 +399,16 @@ macro_rules! babe_err {
 fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<BabePreDigest, String>
 	where DigestItemFor<B>: CompatibleDigestItem,
 {
+	// genesis block doesn't contain a pre digest so let's generate a
+	// dummy one to not break any invariants in the rest of the code
+	if header.number().is_zero() {
+		return Ok(BabePreDigest::Secondary {
+			slot_number: 0,
+			authority_index: 0,
+			weight: 0,
+		});
+	}
+
 	let mut pre_digest: Option<_> = None;
 	for log in header.digest().logs() {
 		trace!(target: "babe", "Checking log {:?}, looking for pre runtime digest", log);
@@ -394,16 +447,20 @@ fn find_next_epoch_digest<B: BlockT>(header: &B::Header) -> Result<Option<Epoch>
 /// unsigned.  This is required for security and must not be changed.
 ///
 /// This digest item will always return `Some` when used with `as_babe_pre_digest`.
+///
+/// The given header can either be from a primary or secondary slot assignment,
+/// with each having different validation logic.
 // FIXME #1018 needs misbehavior types. The `transaction_pool` parameter will be
 // used to submit such misbehavior reports.
 fn check_header<B: BlockT + Sized, C: AuxStore, T>(
-	client: &C,
-	slot_now: u64,
 	mut header: B::Header,
-	hash: B::Hash,
-	authorities: &[(AuthorityId, BabeWeight)],
+	parent_header: B::Header,
+	slot_now: u64,
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
+	client: &C,
 	randomness: [u8; 32],
 	epoch_index: u64,
+	secondary_slots: bool,
 	c: (u64, u64),
 	_transaction_pool: Option<&T>,
 ) -> Result<CheckedHeader<B::Header, (DigestItemFor<B>, DigestItemFor<B>)>, String> where
@@ -413,67 +470,184 @@ fn check_header<B: BlockT + Sized, C: AuxStore, T>(
 	trace!(target: "babe", "Checking header");
 	let seal = match header.digest_mut().pop() {
 		Some(x) => x,
-		None => return Err(babe_err!("Header {:?} is unsealed", hash)),
+		None => return Err(babe_err!("Header {:?} is unsealed", header.hash())),
 	};
 
 	let sig = seal.as_babe_seal().ok_or_else(|| {
-		babe_err!("Header {:?} has a bad seal", hash)
+		babe_err!("Header {:?} has a bad seal", header.hash())
 	})?;
 
-	let pre_digest = find_pre_digest::<B>(&header)?;
+	// the pre-hash of the header doesn't include the seal
+	// and that's what we sign
+	let pre_hash = header.hash();
 
-	let BabePreDigest { slot_number, authority_index, ref vrf_proof, ref vrf_output } = pre_digest;
+	let pre_digest = find_pre_digest::<B>(&header)?;
 
-	if slot_number > slot_now {
+	if pre_digest.slot_number() > slot_now {
 		header.digest_mut().push(seal);
-		Ok(CheckedHeader::Deferred(header, slot_number))
-	} else if authority_index > authorities.len() as u32 {
-		Err(babe_err!("Slot author not found"))
-	} else {
-		let (pre_hash, author) = (header.hash(), &authorities[authority_index as usize].0);
+		return Ok(CheckedHeader::Deferred(header, pre_digest.slot_number()));
+	}
 
-		if AuthorityPair::verify(&sig, pre_hash, &author) {
-			let (inout, _batchable_proof) = {
-				let transcript = make_transcript(
-					&randomness,
-					slot_number,
-					epoch_index,
-				);
+	if pre_digest.authority_index() > authorities.len() as u32 {
+		return Err(babe_err!("Slot author not found"));
+	}
 
-				schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| {
-					p.vrf_verify(transcript, vrf_output, vrf_proof)
-				}).map_err(|s| {
-					babe_err!("VRF verification failed: {:?}", s)
-				})?
-			};
+	let parent_weight = {
+		let parent_pre_digest = find_pre_digest::<B>(&parent_header)?;
+		parent_pre_digest.weight()
+	};
 
-			let threshold = calculate_threshold(c, authorities, authority_index as usize);
-			if !check(&inout, threshold) {
-				return Err(babe_err!("VRF verification of block by author {:?} failed: \
-									  threshold {} exceeded", author, threshold));
-			}
+	match &pre_digest {
+		BabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number, weight } => {
+			debug!(target: "babe", "Verifying Primary block");
+
+			let digest = (vrf_output, vrf_proof, *authority_index, *slot_number, *weight);
+
+			check_primary_header::<B>(
+				pre_hash,
+				digest,
+				sig,
+				parent_weight,
+				authorities,
+				randomness,
+				epoch_index,
+				c,
+			)?;
+		},
+		BabePreDigest::Secondary { authority_index, slot_number, weight } if secondary_slots => {
+			debug!(target: "babe", "Verifying Secondary block");
+
+			let digest = (*authority_index, *slot_number, *weight);
+
+			check_secondary_header::<B>(
+				pre_hash,
+				digest,
+				sig,
+				parent_weight,
+				&authorities,
+				randomness,
+			)?;
+		},
+		_ => {
+			return Err(babe_err!("Secondary slot assignments are disabled for the current epoch."));
+		}
+	}
+
+	let author = &authorities[pre_digest.authority_index() as usize].0;
+
+	// the header is valid but let's check if there was something else already
+	// proposed at the same slot by the given author
+	if let Some(equivocation_proof) = check_equivocation(
+		client,
+		slot_now,
+		pre_digest.slot_number(),
+		&header,
+		author,
+	).map_err(|e| e.to_string())? {
+		info!(
+			"Slot author {:?} is equivocating at slot {} with headers {:?} and {:?}",
+			author,
+			pre_digest.slot_number(),
+			equivocation_proof.fst_header().hash(),
+			equivocation_proof.snd_header().hash(),
+		);
+	}
+
+	let pre_digest = CompatibleDigestItem::babe_pre_digest(pre_digest);
+	Ok(CheckedHeader::Checked(header, (pre_digest, seal)))
+}
+
+/// Check a primary slot proposal header. We validate that the given header is
+/// properly signed by the expected authority, and that the contained VRF proof
+/// is valid. Additionally, the weight of this block must increase compared to
+/// its parent since it is a primary block.
+fn check_primary_header<B: BlockT + Sized>(
+	pre_hash: B::Hash,
+	pre_digest: (&VRFOutput, &VRFProof, AuthorityIndex, SlotNumber, BabeBlockWeight),
+	signature: AuthoritySignature,
+	parent_weight: BabeBlockWeight,
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
+	randomness: [u8; 32],
+	epoch_index: u64,
+	c: (u64, u64),
+) -> Result<(), String>
+	where DigestItemFor<B>: CompatibleDigestItem,
+{
+	let (vrf_output, vrf_proof, authority_index, slot_number, weight) = pre_digest;
+	if weight != parent_weight + 1 {
+		return Err("Invalid weight: should increase with Primary block.".into());
+	}
 
-			if let Some(equivocation_proof) = check_equivocation(
-				client,
-				slot_now,
+	let author = &authorities[authority_index as usize].0;
+
+	if AuthorityPair::verify(&signature, pre_hash, &author) {
+		let (inout, _) = {
+			let transcript = make_transcript(
+				&randomness,
 				slot_number,
-				&header,
-				author,
-			).map_err(|e| e.to_string())? {
-				info!(
-					"Slot author {:?} is equivocating at slot {} with headers {:?} and {:?}",
-					author,
-					slot_number,
-					equivocation_proof.fst_header().hash(),
-					equivocation_proof.snd_header().hash(),
-				);
-			}
+				epoch_index,
+			);
 
-			let pre_digest = CompatibleDigestItem::babe_pre_digest(pre_digest);
-			Ok(CheckedHeader::Checked(header, (pre_digest, seal)))
-		} else {
-			Err(babe_err!("Bad signature on {:?}", hash))
+			schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| {
+				p.vrf_verify(transcript, vrf_output, vrf_proof)
+			}).map_err(|s| {
+				babe_err!("VRF verification failed: {:?}", s)
+			})?
+		};
+
+		let threshold = calculate_primary_threshold(c, authorities, authority_index as usize);
+		if !check_primary_threshold(&inout, threshold) {
+			return Err(babe_err!("VRF verification of block by author {:?} failed: \
+								  threshold {} exceeded", author, threshold));
 		}
+
+		Ok(())
+	} else {
+		Err(babe_err!("Bad signature on {:?}", pre_hash))
+	}
+}
+
+/// Check a secondary slot proposal header. We validate that the given header is
+/// properly signed by the expected authority, which we have a deterministic way
+/// of computing. Additionally, the weight of this block must stay the same
+/// compared to its parent since it is a secondary block.
+fn check_secondary_header<B: BlockT>(
+	pre_hash: B::Hash,
+	pre_digest: (AuthorityIndex, SlotNumber, BabeBlockWeight),
+	signature: AuthoritySignature,
+	parent_weight: BabeBlockWeight,
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
+	randomness: [u8; 32],
+) -> Result<(), String> {
+	let (authority_index, slot_number, weight) = pre_digest;
+
+	if weight != parent_weight {
+		return Err("Invalid weight: Should stay the same with secondary block.".into());
+	}
+
+	// check the signature is valid under the expected authority and
+	// chain state.
+	let expected_author = secondary_slot_author(
+		slot_number,
+		authorities,
+		randomness,
+	).ok_or_else(|| "No secondary author expected.".to_string())?;
+
+	let author = &authorities[authority_index as usize].0;
+
+	if expected_author != author {
+		let msg = format!("Invalid author: Expected secondary author: {:?}, got: {:?}.",
+			expected_author,
+			author,
+		);
+
+		return Err(msg);
+	}
+
+	if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
+		Ok(())
+	} else {
+		Err(format!("Bad signature on {:?}", pre_hash))
 	}
 }
 
@@ -482,22 +656,23 @@ fn check_header<B: BlockT + Sized, C: AuxStore, T>(
 pub struct BabeLink(Arc<Mutex<(Option<Duration>, Vec<(Instant, u64)>)>>);
 
 /// A verifier for Babe blocks.
-pub struct BabeVerifier<C, T> {
-	api: Arc<C>,
+pub struct BabeVerifier<B, E, Block: BlockT, RA, PRA, T> {
+	client: Arc<Client<B, E, Block, RA>>,
+	api: Arc<PRA>,
 	inherent_data_providers: inherents::InherentDataProviders,
 	config: Config,
 	time_source: BabeLink,
 	transaction_pool: Option<Arc<T>>,
 }
 
-impl<C, T> BabeVerifier<C, T> {
-	fn check_inherents<B: BlockT>(
+impl<B, E, Block: BlockT, RA, PRA, T> BabeVerifier<B, E, Block, RA, PRA, T> {
+	fn check_inherents(
 		&self,
-		block: B,
-		block_id: BlockId<B>,
+		block: Block,
+		block_id: BlockId<Block>,
 		inherent_data: InherentData,
 	) -> Result<(), String>
-		where C: ProvideRuntimeApi, C::Api: BlockBuilderApi<B>
+		where PRA: ProvideRuntimeApi, PRA::Api: BlockBuilderApi<Block>
 	{
 		let inherent_res = self.api.runtime_api().check_inherents(
 			&block_id,
@@ -560,18 +735,22 @@ fn median_algorithm(
 	}
 }
 
-impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
-	C: ProvideRuntimeApi + Send + Sync + AuxStore + ProvideCache<B>,
-	C::Api: BlockBuilderApi<B> + BabeApi<B>,
+impl<B, E, Block, RA, PRA, T> Verifier<Block> for BabeVerifier<B, E, Block, RA, PRA, T> where
+	Block: BlockT<Hash=H256>,
+	B: Backend<Block, Blake2Hasher> + 'static,
+	E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
+	RA: Send + Sync,
+	PRA: ProvideRuntimeApi + Send + Sync + AuxStore + ProvideCache<Block>,
+	PRA::Api: BlockBuilderApi<Block> + BabeApi<Block>,
 	T: Send + Sync + 'static,
 {
 	fn verify(
 		&mut self,
 		origin: BlockOrigin,
-		header: B::Header,
+		header: Block::Header,
 		justification: Option<Justification>,
-		mut body: Option<Vec<B::Extrinsic>>,
-	) -> Result<(BlockImportParams<B>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
+		mut body: Option<Vec<Block::Extrinsic>>,
+	) -> Result<(BlockImportParams<Block>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
 		trace!(
 			target: "babe",
 			"Verifying origin: {:?} header: {:?} justification: {:?} body: {:?}",
@@ -596,18 +775,23 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
 		let epoch = epoch(self.api.as_ref(), &BlockId::Hash(parent_hash))
 			.map_err(|e| format!("Could not fetch epoch at {:?}: {:?}", parent_hash, e))?;
 		let (epoch, maybe_next_epoch) = epoch.deconstruct();
-		let Epoch { authorities, randomness, epoch_index, .. } = epoch;
+		let Epoch { authorities, randomness, epoch_index, secondary_slots, .. } = epoch;
+
+		let parent_header = self.client.header(&BlockId::Hash(parent_hash))
+			.map_err(|e| format!("Could not fetch parent header {:?}: {:?}", parent_hash, e))?
+			.ok_or_else(|| format!("Parent header {:?} not found.", parent_hash))?;
 
 		// We add one to allow for some small drift.
 		// FIXME #1019 in the future, alter this queue to allow deferring of headers
-		let mut checked_header = check_header::<B, C, T>(
-			&self.api,
-			slot_now + 1,
+		let mut checked_header = check_header::<Block, PRA, T>(
 			header.clone(),
-			hash,
+			parent_header.clone(),
+			slot_now + 1,
 			&authorities,
+			&self.api,
 			randomness,
 			epoch_index,
+			secondary_slots,
 			self.config.c(),
 			self.transaction_pool.as_ref().map(|x| &**x),
 		);
@@ -617,14 +801,15 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
 		// (this is only possible on the light client at epoch#0)
 		if epoch_index == 0 && checked_header.is_err() {
 			if let Some(Epoch { authorities, randomness, epoch_index, .. }) = maybe_next_epoch {
-				let checked_header_next = check_header::<B, C, T>(
-					&self.api,
-					slot_now + 1,
+				let checked_header_next = check_header::<Block, PRA, T>(
 					header,
-					hash,
+					parent_header,
+					slot_now + 1,
 					&authorities,
+					&self.api,
 					randomness,
 					epoch_index,
+					secondary_slots,
 					self.config.c(),
 					self.transaction_pool.as_ref().map(|x| &**x),
 				);
@@ -639,15 +824,17 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
 		let checked_header = checked_header?;
 		match checked_header {
 			CheckedHeader::Checked(pre_header, (pre_digest, seal)) => {
-				let BabePreDigest { slot_number, .. } = pre_digest.as_babe_pre_digest()
+				let babe_pre_digest = pre_digest.as_babe_pre_digest()
 					.expect("check_header always returns a pre-digest digest item; qed");
 
+				let slot_number = babe_pre_digest.slot_number();
+
 				// if the body is passed through, we need to use the runtime
 				// to check that the internally-set timestamp in the inherents
 				// actually matches the slot set in the seal.
 				if let Some(inner_body) = body.take() {
 					inherent_data.babe_replace_inherent_data(slot_number);
-					let block = B::new(pre_header.clone(), inner_body);
+					let block = Block::new(pre_header.clone(), inner_body);
 
 					self.check_inherents(
 						block.clone(),
@@ -665,6 +852,34 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
 					"babe.checked_and_importing";
 					"pre_header" => ?pre_header);
 
+				// The fork choice rule is that we pick the heaviest chain (i.e.
+				// more primary blocks), if there's a tie we go with the longest
+				// chain.
+				let new_best = {
+					let (last_best, last_best_number) = {
+						#[allow(deprecated)]
+						let info = self.client.backend().blockchain().info();
+						(info.best_hash, info.best_number)
+					};
+
+					let best_header = self.client.header(&BlockId::Hash(last_best))
+												 .map_err(|_| "Failed fetching best header")?
+					.expect("parent_header must be imported; qed");
+
+					let best_weight = find_pre_digest::<Block>(&best_header)
+						.map(|babe_pre_digest| babe_pre_digest.weight())?;
+
+					let new_weight = babe_pre_digest.weight();
+
+					if new_weight > best_weight {
+						true
+					} else if new_weight == best_weight {
+						*pre_header.number() > last_best_number
+					} else {
+						false
+					}
+				};
+
 				let import_block = BlockImportParams {
 					origin,
 					header: pre_header,
@@ -673,7 +888,7 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
 					finalized: false,
 					justification,
 					auxiliary: Vec::new(),
-					fork_choice: ForkChoiceStrategy::LongestChain,
+					fork_choice: ForkChoiceStrategy::Custom(new_best),
 				};
 
 				Ok((import_block, Default::default()))
@@ -797,13 +1012,17 @@ fn make_transcript(
 	transcript
 }
 
-fn check(inout: &VRFInOut, threshold: u128) -> bool {
+/// Returns true if the given VRF output is lower than the given threshold,
+/// false otherwise.
+fn check_primary_threshold(inout: &VRFInOut, threshold: u128) -> bool {
 	u128::from_le_bytes(inout.make_bytes::<[u8; 16]>(BABE_VRF_PREFIX)) < threshold
 }
 
-fn calculate_threshold(
+/// Calculates the primary selection threshold for a given authority, taking
+/// into account `c` (`1 - c` represents the probability of a slot being empty).
+fn calculate_primary_threshold(
 	c: (u64, u64),
-	authorities: &[(AuthorityId, BabeWeight)],
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
 	authority_index: usize,
 ) -> u128 {
 	use num_bigint::BigUint;
@@ -826,34 +1045,146 @@ fn calculate_threshold(
 	calc().unwrap_or(u128::max_value())
 }
 
-/// Claim a slot if it is our turn.  Returns `None` if it is not our turn.
-///
+/// Tries to claim the given slot number. This method starts by trying to claim
+/// a primary VRF based slot. If we are not able to claim it, then if we have
+/// secondary slots enabled for the given epoch, we will fallback to trying to
+/// claim a secondary slot.
+fn claim_slot(
+	slot_number: SlotNumber,
+	parent_weight: BabeBlockWeight,
+	epoch: &Epoch,
+	c: (u64, u64),
+	keystore: &KeyStorePtr,
+) -> Option<(BabePreDigest, AuthorityPair)> {
+	claim_primary_slot(slot_number, parent_weight, epoch, c, keystore)
+		.or_else(|| {
+			if epoch.secondary_slots {
+				claim_secondary_slot(
+					slot_number,
+					parent_weight,
+					&epoch.authorities,
+					keystore,
+					epoch.randomness,
+				)
+			} else {
+				None
+			}
+		})
+}
+
+/// Claim a primary slot if it is our turn.  Returns `None` if it is not our turn.
 /// This hashes the slot number, epoch, genesis hash, and chain randomness into
 /// the VRF.  If the VRF produces a value less than `threshold`, it is our turn,
-/// so it returns `Some(_)`.  Otherwise, it returns `None`.
-fn claim_slot(
-	slot_number: u64,
-	Epoch { authorities, randomness, epoch_index, .. }: &Epoch,
+/// so it returns `Some(_)`. Otherwise, it returns `None`.
+fn claim_primary_slot(
+	slot_number: SlotNumber,
+	parent_weight: BabeBlockWeight,
+	epoch: &Epoch,
 	c: (u64, u64),
 	keystore: &KeyStorePtr,
-) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize, AuthorityPair)> {
+) -> Option<(BabePreDigest, AuthorityPair)> {
+	let Epoch { authorities, randomness, epoch_index, .. } = epoch;
+	let keystore = keystore.read();
+
+	for (pair, authority_index) in authorities.iter()
+		.enumerate()
+		.flat_map(|(i, a)| {
+			keystore.key_pair::<AuthorityPair>(&a.0).ok().map(|kp| (kp, i))
+		})
+	{
+		let transcript = make_transcript(randomness, slot_number, *epoch_index);
+
+		// Compute the threshold we will use.
+		//
+		// We already checked that authorities contains `key.public()`, so it can't
+		// be empty.  Therefore, this division in `calculate_threshold` is safe.
+		let threshold = calculate_primary_threshold(c, authorities, authority_index);
+
+		let pre_digest = get_keypair(&pair)
+			.vrf_sign_after_check(transcript, |inout| check_primary_threshold(inout, threshold))
+			.map(|s| {
+				BabePreDigest::Primary {
+					slot_number,
+					vrf_output: s.0.to_output(),
+					vrf_proof: s.1,
+					authority_index: authority_index as u32,
+					weight: parent_weight + 1,
+				}
+			});
+
+		// early exit on first successful claim
+		if let Some(pre_digest) = pre_digest {
+			return Some((pre_digest, pair));
+		}
+	}
+
+	None
+}
+
+/// Get the expected secondary author for the given slot and with given
+/// authorities. This should always assign the slot to some authority unless the
+/// authorities list is empty.
+fn secondary_slot_author(
+	slot_number: u64,
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
+	randomness: [u8; 32],
+) -> Option<&AuthorityId> {
+	if authorities.is_empty() {
+		return None;
+	}
+
+	let rand = U256::from((randomness, slot_number).using_encoded(blake2_256));
+
+	let authorities_len = U256::from(authorities.len());
+	let idx = rand % authorities_len;
+
+	let expected_author = authorities.get(idx.as_u32() as usize)
+		.expect("authorities not empty; index constrained to list length; \
+				this is a valid index; qed");
+
+	Some(&expected_author.0)
+}
+
+/// Claim a secondary slot if it is our turn to propose, returning the
+/// pre-digest to use when authoring the block, or `None` if it is not our turn
+/// to propose.
+fn claim_secondary_slot(
+	slot_number: SlotNumber,
+	parent_weight: BabeBlockWeight,
+	authorities: &[(AuthorityId, BabeAuthorityWeight)],
+	keystore: &KeyStorePtr,
+	randomness: [u8; 32],
+) -> Option<(BabePreDigest, AuthorityPair)> {
+	if authorities.is_empty() {
+		return None;
+	}
+
+	let expected_author = secondary_slot_author(
+		slot_number,
+		authorities,
+		randomness,
+	)?;
+
 	let keystore = keystore.read();
-	let (key_pair, authority_index) = authorities.iter()
+
+	for (pair, authority_index) in authorities.iter()
 		.enumerate()
-		.find_map(|(i, a)| {
+		.flat_map(|(i, a)| {
 			keystore.key_pair::<AuthorityPair>(&a.0).ok().map(|kp| (kp, i))
-		})?;
-	let transcript = make_transcript(randomness, slot_number, *epoch_index);
-
-	// Compute the threshold we will use.
-	//
-	// We already checked that authorities contains `key.public()`, so it can't
-	// be empty.  Therefore, this division in `calculate_threshold` is safe.
-	let threshold = calculate_threshold(c, authorities, authority_index);
-
-	get_keypair(&key_pair)
-		.vrf_sign_after_check(transcript, |inout| check(inout, threshold))
-		.map(|s|(s, authority_index, key_pair))
+		})
+	{
+		if pair.public() == *expected_author {
+			let pre_digest = BabePreDigest::Secondary {
+				slot_number,
+				authority_index: authority_index as u32,
+				weight: parent_weight,
+			};
+
+			return Some((pre_digest, pair));
+		}
+	}
+
+	None
 }
 
 fn initialize_authorities_cache<B, C>(client: &C) -> Result<(), ConsensusError> where
@@ -1001,8 +1332,7 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
 			let pre_digest = find_pre_digest::<Block>(&block.header)
 				.expect("valid babe headers must contain a predigest; \
 						 header has been already verified; qed");
-			let BabePreDigest { slot_number, .. } = pre_digest;
-			slot_number
+			pre_digest.slot_number()
 		};
 
 		// returns a function for checking whether a block is a descendent of another
@@ -1158,6 +1488,7 @@ pub fn import_queue<B, E, Block: BlockT<Hash=H256>, I, RA, PRA, T>(
 	initialize_authorities_cache(&*api)?;
 
 	let verifier = BabeVerifier {
+		client: client.clone(),
 		api: api.clone(),
 		inherent_data_providers,
 		time_source: Default::default(),
@@ -1209,9 +1540,9 @@ pub mod test_helpers {
 	/// Try to claim the given slot and return a `BabePreDigest` if
 	/// successful.
 	pub fn claim_slot<B, C>(
-		client: &C,
-		at: &BlockId<B>,
 		slot_number: u64,
+		parent: &B::Header,
+		client: &C,
 		c: (u64, u64),
 		keystore: &KeyStorePtr,
 	) -> Option<BabePreDigest> where
@@ -1219,23 +1550,20 @@ pub mod test_helpers {
 		C: ProvideRuntimeApi + ProvideCache<B>,
 		C::Api: BabeApi<B>,
 	{
-		let epoch = match epoch(client, at).unwrap() {
+		let epoch = match epoch(client, &BlockId::Hash(parent.hash())).unwrap() {
 			MaybeSpanEpoch::Regular(epoch) => epoch,
 			_ => unreachable!("it is always Regular epoch on full nodes"),
 		};
 
+		let weight = find_pre_digest::<B>(parent).ok()
+			.map(|d| d.weight())?;
+
 		super::claim_slot(
 			slot_number,
+			weight,
 			&epoch,
 			c,
 			keystore,
-		).map(|((inout, vrf_proof, _), authority_index, _)| {
-			BabePreDigest {
-				vrf_proof,
-				vrf_output: inout.to_output(),
-				authority_index: authority_index as u32,
-				slot_number,
-			}
-		})
+		).map(|(digest, _)| digest)
 	}
 }
diff --git a/substrate/core/consensus/babe/src/tests.rs b/substrate/core/consensus/babe/src/tests.rs
index 482842aaaffd0b8df19196b5d575eac7ee78454f..8635473a593f9c3e5d4fc89ca0bef5d980665a06 100644
--- a/substrate/core/consensus/babe/src/tests.rs
+++ b/substrate/core/consensus/babe/src/tests.rs
@@ -20,14 +20,13 @@
 // https://github.com/paritytech/substrate/issues/2532
 #![allow(deprecated)]
 use super::*;
-use sr_primitives::generic::{self, DigestItem};
 
 use babe_primitives::AuthorityPair;
 use client::{LongestChain, block_builder::BlockBuilder};
 use consensus_common::NoNetwork as DummyOracle;
 use network::test::*;
 use network::test::{Block as TestBlock, PeersClient};
-use sr_primitives::traits::{Block as BlockT, DigestFor};
+use sr_primitives::{generic::DigestItem, traits::{Block as BlockT, DigestFor}};
 use network::config::ProtocolConfig;
 use tokio::runtime::current_thread;
 use keyring::sr25519::Keyring;
@@ -35,7 +34,8 @@ use client::BlockchainEvents;
 use test_client;
 use log::debug;
 use std::{time::Duration, borrow::Borrow, cell::RefCell};
-type Item = generic::DigestItem<Hash>;
+
+type Item = DigestItem<Hash>;
 
 type Error = client::error::Error;
 
@@ -88,7 +88,14 @@ type TestHeader = <TestBlock as BlockT>::Header;
 type TestExtrinsic = <TestBlock as BlockT>::Extrinsic;
 
 pub struct TestVerifier {
-	inner: BabeVerifier<PeersFullClient, ()>,
+	inner: BabeVerifier<
+		test_client::Backend,
+		test_client::Executor,
+		TestBlock,
+		test_client::runtime::RuntimeApi,
+		PeersFullClient,
+		(),
+	>,
 	mutator: Mutator,
 }
 
@@ -126,9 +133,9 @@ impl TestNetFactory for BabeTestNet {
 	fn make_verifier(&self, client: PeersClient, _cfg: &ProtocolConfig)
 		-> Self::Verifier
 	{
-		let api = client.as_full().expect("only full clients are used in test");
+		let client = client.as_full().expect("only full clients are used in test");
 		trace!(target: "babe", "Creating a verifier");
-		let config = Config::get_or_compute(&*api)
+		let config = Config::get_or_compute(&*client)
 			.expect("slot duration available");
 		let inherent_data_providers = InherentDataProviders::new();
 		register_babe_inherent_data_provider(
@@ -139,7 +146,8 @@ impl TestNetFactory for BabeTestNet {
 
 		TestVerifier {
 			inner: BabeVerifier {
-				api,
+				client: client.clone(),
+				api: client,
 				inherent_data_providers,
 				config,
 				time_source: Default::default(),
@@ -317,19 +325,32 @@ fn can_author_block() {
 		.expect("Generates authority pair");
 
 	let mut i = 0;
-	let epoch = Epoch {
+	let mut epoch = Epoch {
 		start_slot: 0,
 		authorities: vec![(pair.public(), 1)],
 		randomness: [0; 32],
 		epoch_index: 1,
 		duration: 100,
+		secondary_slots: true,
 	};
+
+	let parent_weight = 0;
+
+	// with secondary slots enabled it should never be empty
+	match claim_slot(i, parent_weight, &epoch, (3, 10), &keystore) {
+		None => i += 1,
+		Some(s) => debug!(target: "babe", "Authored block {:?}", s.0),
+	}
+
+	// otherwise with only vrf-based primary slots we might need to try a couple
+	// of times.
+	epoch.secondary_slots = false;
 	loop {
-		match claim_slot(i, &epoch.clone(), (3, 10), &keystore) {
+		match claim_slot(i, parent_weight, &epoch, (3, 10), &keystore) {
 			None => i += 1,
 			Some(s) => {
 				debug!(target: "babe", "Authored block {:?}", s.0);
-				break
+				break;
 			}
 		}
 	}
diff --git a/substrate/core/consensus/slots/src/lib.rs b/substrate/core/consensus/slots/src/lib.rs
index d28510c8c60f976efbb5db7817aa4eef46cf1bd6..2513071a9fe40afb929093429c8cd3ff008ea5f1 100644
--- a/substrate/core/consensus/slots/src/lib.rs
+++ b/substrate/core/consensus/slots/src/lib.rs
@@ -84,7 +84,12 @@ pub trait SimpleSlotWorker<B: BlockT> {
 	fn authorities_len(&self, epoch_data: &Self::EpochData) -> usize;
 
 	/// Tries to claim the given slot, returning an object with claim data if successful.
-	fn claim_slot(&self, slot_number: u64, epoch_data: &Self::EpochData) -> Option<Self::Claim>;
+	fn claim_slot(
+		&self,
+		header: &B::Header,
+		slot_number: u64,
+		epoch_data: &Self::EpochData,
+	) -> Option<Self::Claim>;
 
 	/// Return the pre digest data to include in a block authored with the given claim.
 	fn pre_digest_data(&self, slot_number: u64, claim: &Self::Claim) -> Vec<sr_primitives::DigestItem<B::Hash>>;
@@ -143,7 +148,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
 			return Box::pin(future::ready(Ok(())));
 		}
 
-		let claim = match self.claim_slot(slot_number, &epoch_data) {
+		let claim = match self.claim_slot(&chain_head, slot_number, &epoch_data) {
 			None => return Box::pin(future::ready(Ok(()))),
 			Some(claim) => claim,
 		};
diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs
index 8abb4d920fec19a310b8536c9c506c58a47bb9c7..e9ee40af75a6f69d38e60f43c5820a2d1ab3cf2f 100644
--- a/substrate/core/test-runtime/src/lib.rs
+++ b/substrate/core/test-runtime/src/lib.rs
@@ -617,6 +617,7 @@ cfg_if! {
 						randomness: <srml_babe::Module<Runtime>>::randomness(),
 						epoch_index: <srml_babe::Module<Runtime>>::epoch_index(),
 						duration: EpochDuration::get(),
+						secondary_slots: <srml_babe::Module<Runtime>>::secondary_slots().0,
 					}
 				}
 			}
@@ -831,6 +832,7 @@ cfg_if! {
 						randomness: <srml_babe::Module<Runtime>>::randomness(),
 						epoch_index: <srml_babe::Module<Runtime>>::epoch_index(),
 						duration: EpochDuration::get(),
+						secondary_slots: <srml_babe::Module<Runtime>>::secondary_slots().0,
 					}
 				}
 			}
diff --git a/substrate/node/cli/src/service.rs b/substrate/node/cli/src/service.rs
index 90c76eda84cbe2f37215169aa90fe050307ff8cc..ebd2f29fd510bd250a5d3a4ca1d3641a39e688d7 100644
--- a/substrate/node/cli/src/service.rs
+++ b/substrate/node/cli/src/service.rs
@@ -401,9 +401,9 @@ mod tests {
 			let babe_pre_digest = loop {
 				inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SLOT_DURATION));
 				if let Some(babe_pre_digest) = babe::test_helpers::claim_slot(
-					&*service.client(),
-					&parent_id,
 					slot_num,
+					&parent_header,
+					&*service.client(),
 					(278, 1000),
 					&keystore,
 				) {
diff --git a/substrate/node/runtime/src/constants.rs b/substrate/node/runtime/src/constants.rs
index f728efb3be89ff096341e257a5422937cfed48a0..4108e66bd61d63dada1dc43e6fca77bf275c12a8 100644
--- a/substrate/node/runtime/src/constants.rs
+++ b/substrate/node/runtime/src/constants.rs
@@ -44,7 +44,7 @@ pub mod time {
 	pub const MILLISECS_PER_BLOCK: Moment = 6000;
 	pub const SECS_PER_BLOCK: Moment = MILLISECS_PER_BLOCK / 1000;
 
-	pub const SLOT_DURATION: Moment = 1650;
+	pub const SLOT_DURATION: Moment = 6000;
 
 	pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10 * MINUTES;
 	pub const EPOCH_DURATION_IN_SLOTS: u64 = {
diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs
index 8dd62bf407d04b4b7fd40fe8fe456e016a24a8fd..af7533863e08359e057ad96f1b97daa0a25c46db 100644
--- a/substrate/node/runtime/src/lib.rs
+++ b/substrate/node/runtime/src/lib.rs
@@ -80,7 +80,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	// and set impl_version to equal spec_version. If only runtime
 	// implementation changes and behavior does not, then leave spec_version as
 	// is and increment impl_version.
-	spec_version: 144,
+	spec_version: 145,
 	impl_version: 145,
 	apis: RUNTIME_API_VERSIONS,
 };
@@ -559,6 +559,7 @@ impl_runtime_apis! {
 				epoch_index: Babe::epoch_index(),
 				randomness: Babe::randomness(),
 				duration: EpochDuration::get(),
+				secondary_slots: Babe::secondary_slots().0,
 			}
 		}
 	}
diff --git a/substrate/srml/babe/src/lib.rs b/substrate/srml/babe/src/lib.rs
index 65c589ecab674634a27a6d34409b0bf5bacfb464..b0db04d12c7c113bcb95ecfb3d514b12a1862ea4 100644
--- a/substrate/srml/babe/src/lib.rs
+++ b/substrate/srml/babe/src/lib.rs
@@ -14,7 +14,8 @@
 // You should have received a copy of the GNU General Public License
 // along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Consensus extension module for BABE consensus.
+//! Consensus extension module for BABE consensus. Collects on-chain randomness
+//! from VRF outputs and manages epoch transitions.
 
 #![cfg_attr(not(feature = "std"), no_std)]
 #![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)]
@@ -26,14 +27,16 @@ use srml_support::{decl_storage, decl_module, StorageValue, StorageMap, traits::
 use timestamp::{OnTimestampSet};
 use sr_primitives::{generic::DigestItem, ConsensusEngineId};
 use sr_primitives::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon};
+use sr_primitives::weights::SimpleDispatchInfo;
 #[cfg(feature = "std")]
 use timestamp::TimestampInherentData;
 use codec::{Encode, Decode};
 use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError};
 #[cfg(feature = "std")]
 use inherents::{InherentDataProviders, ProvideInherentData};
-use babe_primitives::{BABE_ENGINE_ID, ConsensusLog, BabeWeight, Epoch, RawBabePreDigest};
+use babe_primitives::{BABE_ENGINE_ID, ConsensusLog, BabeAuthorityWeight, Epoch, RawBabePreDigest};
 pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, PUBLIC_KEY_LENGTH};
+use system::ensure_root;
 
 /// The BABE inherent identifier.
 pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"babeslot";
@@ -123,7 +126,7 @@ decl_storage! {
 		pub EpochIndex get(epoch_index): u64;
 
 		/// Current epoch authorities.
-		pub Authorities get(authorities): Vec<(AuthorityId, BabeWeight)>;
+		pub Authorities get(authorities): Vec<(AuthorityId, BabeAuthorityWeight)>;
 
 		/// Slot at which the current epoch started. It is possible that no
 		/// block was authored at the given slot and the epoch change was
@@ -133,6 +136,14 @@ decl_storage! {
 		/// Current slot number.
 		pub CurrentSlot get(current_slot): u64;
 
+		/// Whether secondary slots are enabled in case the VRF-based slot is
+		/// empty for the current epoch and the next epoch, respectively.
+		pub SecondarySlots get(secondary_slots): (bool, bool) = (true, true);
+
+		/// Pending change to enable/disable secondary slots which will be
+		/// triggered at `current_epoch + 2`.
+		pub PendingSecondarySlotsChange get(pending_secondary_slots_change): Option<bool> = None;
+
 		/// The epoch randomness for the *current* epoch.
 		///
 		/// # Security
@@ -168,7 +179,7 @@ decl_storage! {
 		Initialized get(initialized): Option<bool>;
 	}
 	add_extra_genesis {
-		config(authorities): Vec<(AuthorityId, BabeWeight)>;
+		config(authorities): Vec<(AuthorityId, BabeAuthorityWeight)>;
 		build(|
 			storage: &mut (sr_primitives::StorageOverlay, sr_primitives::ChildrenStorageOverlay),
 			config: &GenesisConfig
@@ -204,6 +215,20 @@ decl_module! {
 		fn on_finalize() {
 			Initialized::kill();
 		}
+
+		/// Sets a pending change to enable / disable secondary slot assignment.
+		/// The pending change will be set at the end of the current epoch and
+		/// will be enacted at `current_epoch + 2`.
+		#[weight = SimpleDispatchInfo::FixedOperational(10_000)]
+		fn set_pending_secondary_slots_change(origin, change: Option<bool>) {
+			ensure_root(origin)?;
+			match change {
+				Some(change) =>	PendingSecondarySlotsChange::put(change),
+				None => {
+					PendingSecondarySlotsChange::take();
+				},
+			}
+		}
 	}
 }
 
@@ -222,9 +247,16 @@ impl<T: Trait> FindAuthor<u32> for Module<T> {
 	{
 		for (id, mut data) in digests.into_iter() {
 			if id == BABE_ENGINE_ID {
-				return Some(RawBabePreDigest::decode(&mut data).ok()?.authority_index);
+				let pre_digest = RawBabePreDigest::decode(&mut data).ok()?;
+				return Some(match pre_digest {
+					RawBabePreDigest::Primary { authority_index, .. } =>
+						authority_index,
+					RawBabePreDigest::Secondary { authority_index, .. } =>
+						authority_index,
+				});
 			}
 		}
+
 		return None;
 	}
 }
@@ -302,11 +334,14 @@ impl<T: Trait> Module<T> {
 			})
 		{
 			if EpochStartSlot::get() == 0 {
-				EpochStartSlot::put(digest.slot_number);
+				EpochStartSlot::put(digest.slot_number());
 			}
 
-			CurrentSlot::put(digest.slot_number);
-			Self::deposit_vrf_output(&digest.vrf_output);
+			CurrentSlot::put(digest.slot_number());
+
+			if let RawBabePreDigest::Primary { vrf_output, .. } = digest {
+				Self::deposit_vrf_output(&vrf_output);
+			}
 
 			return;
 		}
@@ -331,7 +366,7 @@ impl<T: Trait> Module<T> {
 		this_randomness
 	}
 
-	fn initialize_authorities(authorities: &[(AuthorityId, BabeWeight)]) {
+	fn initialize_authorities(authorities: &[(AuthorityId, BabeAuthorityWeight)]) {
 		if !authorities.is_empty() {
 			assert!(Authorities::get().is_empty(), "Authorities are already initialized!");
 			Authorities::put_ref(authorities);
@@ -404,12 +439,31 @@ impl<T: Trait> session::OneSessionHandler<T::AccountId> for Module<T> {
 		let next_epoch_start_slot = EpochStartSlot::get().saturating_add(T::EpochDuration::get());
 		let next_randomness = NextRandomness::get();
 
+		// Update any pending secondary slots change
+		let mut secondary_slots = SecondarySlots::get();
+
+		// change for E + 1 now becomes change at E
+		secondary_slots.0 = secondary_slots.1;
+
+		if let Some(change) = PendingSecondarySlotsChange::take() {
+			// if there's a pending change schedule it for E + 1
+			secondary_slots.1 = change;
+		} else {
+			// otherwise E + 1 will have the same value as E
+			secondary_slots.1 = secondary_slots.0;
+		}
+
+		SecondarySlots::mutate(|secondary| {
+			*secondary = secondary_slots;
+		});
+
 		let next = Epoch {
 			epoch_index: next_epoch_index,
 			start_slot: next_epoch_start_slot,
 			duration: T::EpochDuration::get(),
 			authorities: next_authorities,
 			randomness: next_randomness,
+			secondary_slots: secondary_slots.1,
 		};
 
 		Self::deposit_consensus(ConsensusLog::NextEpochData(next))