diff --git a/polkadot/core-primitives/src/lib.rs b/polkadot/core-primitives/src/lib.rs
index d3f567f66a00ad0df3113bf299c888daba18d021..0e080fff6f7c5e72b8c901bf1b944192fcb950fd 100644
--- a/polkadot/core-primitives/src/lib.rs
+++ b/polkadot/core-primitives/src/lib.rs
@@ -50,6 +50,14 @@ pub type ChainId = u32;
 /// A hash of some data used by the relay chain.
 pub type Hash = sp_core::H256;
 
+/// Unit type wrapper around [`Hash`] that represents a candidate hash.
+///
+/// This type is produced by [`CandidateReceipt::hash`].
+///
+/// This type makes it easy to enforce that a hash is a candidate hash on the type level.
+#[derive(Clone, Copy, codec::Encode, codec::Decode, Hash, Eq, PartialEq, Debug, Default)]
+pub struct CandidateHash(pub Hash);
+
 /// Index of a transaction in the relay chain. 32-bit should be plenty.
 pub type Nonce = u32;
 
diff --git a/polkadot/node/core/av-store/src/lib.rs b/polkadot/node/core/av-store/src/lib.rs
index 0f248ba347e1e9bf75383a824f1992f3ebf36dae..57bd6d804c688c0fc56e5deedd6dddf213495aa0 100644
--- a/polkadot/node/core/av-store/src/lib.rs
+++ b/polkadot/node/core/av-store/src/lib.rs
@@ -33,7 +33,7 @@ use kvdb_rocksdb::{Database, DatabaseConfig};
 use kvdb::{KeyValueDB, DBTransaction};
 
 use polkadot_primitives::v1::{
-	Hash, AvailableData, BlockNumber, CandidateEvent, ErasureChunk, ValidatorIndex,
+	Hash, AvailableData, BlockNumber, CandidateEvent, ErasureChunk, ValidatorIndex, CandidateHash,
 };
 use polkadot_subsystem::{
 	FromOverseer, OverseerSignal, SubsystemError, Subsystem, SubsystemContext, SpawnedSubsystem,
@@ -242,7 +242,7 @@ enum CandidateState {
 
 #[derive(Debug, Decode, Encode, Eq)]
 struct PoVPruningRecord {
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	block_number: BlockNumber,
 	candidate_state: CandidateState,
 	prune_at: PruningDelay,
@@ -272,7 +272,7 @@ impl PartialOrd for PoVPruningRecord {
 
 #[derive(Debug, Decode, Encode, Eq)]
 struct ChunkPruningRecord {
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	block_number: BlockNumber,
 	candidate_state: CandidateState,
 	chunk_index: u32,
@@ -387,11 +387,11 @@ impl AvailabilityStoreSubsystem {
 	}
 }
 
-fn available_data_key(candidate_hash: &Hash) -> Vec<u8> {
+fn available_data_key(candidate_hash: &CandidateHash) -> Vec<u8> {
 	(candidate_hash, 0i8).encode()
 }
 
-fn erasure_chunk_key(candidate_hash: &Hash, index: u32) -> Vec<u8> {
+fn erasure_chunk_key(candidate_hash: &CandidateHash, index: u32) -> Vec<u8> {
 	(candidate_hash, index, 0i8).encode()
 }
 
@@ -564,7 +564,7 @@ where
 				log::trace!(
 					target: LOG_TARGET,
 					"Updating pruning record for finalized block {}",
-					record.candidate_hash,
+					record.block_number,
 				);
 
 				record.prune_at = PruningDelay::into_the_future(
@@ -583,7 +583,7 @@ where
 				log::trace!(
 					target: LOG_TARGET,
 					"Updating chunk pruning record for finalized block {}",
-					record.candidate_hash,
+					record.block_number,
 				);
 
 				record.prune_at = PruningDelay::into_the_future(
@@ -620,7 +620,7 @@ where
 
 	for event in events.into_iter() {
 		if let CandidateEvent::CandidateIncluded(receipt, _) = event {
-			log::trace!(target: LOG_TARGET, "Candidate {} was included", receipt.hash());
+			log::trace!(target: LOG_TARGET, "Candidate {:?} was included", receipt.hash());
 			included.insert(receipt.hash());
 		}
 	}
@@ -729,7 +729,10 @@ where
 	Ok(())
 }
 
-fn available_data(db: &Arc<dyn KeyValueDB>, candidate_hash: &Hash) -> Option<StoredAvailableData> {
+fn available_data(
+	db: &Arc<dyn KeyValueDB>,
+	candidate_hash: &CandidateHash,
+) -> Option<StoredAvailableData> {
 	query_inner(db, columns::DATA, &available_data_key(candidate_hash))
 }
 
@@ -835,7 +838,7 @@ where
 
 fn store_available_data(
 	subsystem: &mut AvailabilityStoreSubsystem,
-	candidate_hash: &Hash,
+	candidate_hash: &CandidateHash,
 	id: Option<ValidatorIndex>,
 	n_validators: u32,
 	available_data: AvailableData,
@@ -872,7 +875,7 @@ fn store_available_data(
 	}
 
 	let pruning_record = PoVPruningRecord {
-		candidate_hash: candidate_hash.clone(),
+		candidate_hash: *candidate_hash,
 		block_number,
 		candidate_state: CandidateState::Stored,
 		prune_at,
@@ -901,7 +904,7 @@ fn store_available_data(
 
 fn store_chunk(
 	subsystem: &mut AvailabilityStoreSubsystem,
-	candidate_hash: &Hash,
+	candidate_hash: &CandidateHash,
 	_n_validators: u32,
 	chunk: ErasureChunk,
 	block_number: BlockNumber,
@@ -952,7 +955,7 @@ fn store_chunk(
 
 fn get_chunk(
 	subsystem: &mut AvailabilityStoreSubsystem,
-	candidate_hash: &Hash,
+	candidate_hash: &CandidateHash,
 	index: u32,
 ) -> Result<Option<ErasureChunk>, Error> {
 	if let Some(chunk) = query_inner(
@@ -981,7 +984,11 @@ fn get_chunk(
 	Ok(None)
 }
 
-fn query_inner<D: Decode>(db: &Arc<dyn KeyValueDB>, column: u32, key: &[u8]) -> Option<D> {
+fn query_inner<D: Decode>(
+	db: &Arc<dyn KeyValueDB>,
+	column: u32,
+	key: &[u8],
+) -> Option<D> {
 	match db.get(column, key) {
 		Ok(Some(raw)) => {
 			let res = D::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed");
diff --git a/polkadot/node/core/av-store/src/tests.rs b/polkadot/node/core/av-store/src/tests.rs
index c2d6c9550ac36a901c1e322470b49d77abee582c..acbab53f12ced15156f44f24cb4722d0bc5835be 100644
--- a/polkadot/node/core/av-store/src/tests.rs
+++ b/polkadot/node/core/av-store/src/tests.rs
@@ -27,7 +27,7 @@ use smallvec::smallvec;
 
 use polkadot_primitives::v1::{
 	AvailableData, BlockData, CandidateDescriptor, CandidateReceipt, HeadData,
-	PersistedValidationData, PoV, Id as ParaId,
+	PersistedValidationData, PoV, Id as ParaId, CandidateHash,
 };
 use polkadot_node_subsystem_util::TimeoutExt;
 use polkadot_subsystem::{
@@ -199,7 +199,7 @@ fn runtime_api_error_does_not_stop_the_subsystem() {
 
 		// but that's fine, we're still alive
 		let (tx, rx) = oneshot::channel();
-		let candidate_hash = Hash::repeat_byte(33);
+		let candidate_hash = CandidateHash(Hash::repeat_byte(33));
 		let validator_index = 5;
 		let query_chunk = AvailabilityStoreMessage::QueryChunk(
 			candidate_hash,
@@ -220,7 +220,7 @@ fn store_chunk_works() {
 	test_harness(PruningConfig::default(), store.clone(), |test_harness| async move {
 		let TestHarness { mut virtual_overseer } = test_harness;
 		let relay_parent = Hash::repeat_byte(32);
-		let candidate_hash = Hash::repeat_byte(33);
+		let candidate_hash = CandidateHash(Hash::repeat_byte(33));
 		let validator_index = 5;
 
 		let chunk = ErasureChunk {
@@ -273,7 +273,7 @@ fn store_block_works() {
 	let test_state = TestState::default();
 	test_harness(test_state.pruning_config.clone(), store.clone(), |test_harness| async move {
 		let TestHarness { mut virtual_overseer } = test_harness;
-		let candidate_hash = Hash::from([1; 32]);
+		let candidate_hash = CandidateHash(Hash::from([1; 32]));
 		let validator_index = 5;
 		let n_validators = 10;
 
@@ -327,7 +327,7 @@ fn store_pov_and_query_chunk_works() {
 
 	test_harness(test_state.pruning_config.clone(), store.clone(), |test_harness| async move {
 		let TestHarness { mut virtual_overseer } = test_harness;
-		let candidate_hash = Hash::from([1; 32]);
+		let candidate_hash = CandidateHash(Hash::from([1; 32]));
 		let n_validators = 10;
 
 		let pov = PoV {
@@ -370,7 +370,7 @@ fn stored_but_not_included_chunk_is_pruned() {
 
 	test_harness(test_state.pruning_config.clone(), store.clone(), |test_harness| async move {
 		let TestHarness { mut virtual_overseer } = test_harness;
-		let candidate_hash = Hash::repeat_byte(1);
+		let candidate_hash = CandidateHash(Hash::repeat_byte(1));
 		let relay_parent = Hash::repeat_byte(2);
 		let validator_index = 5;
 
@@ -425,7 +425,7 @@ fn stored_but_not_included_data_is_pruned() {
 
 	test_harness(test_state.pruning_config.clone(), store.clone(), |test_harness| async move {
 		let TestHarness { mut virtual_overseer } = test_harness;
-		let candidate_hash = Hash::repeat_byte(1);
+		let candidate_hash = CandidateHash(Hash::repeat_byte(1));
 		let n_validators = 10;
 
 		let pov = PoV {
@@ -852,7 +852,7 @@ fn forkfullness_works() {
 
 async fn query_available_data(
 	virtual_overseer: &mut test_helpers::TestSubsystemContextHandle<AvailabilityStoreMessage>,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 ) -> Option<AvailableData> {
 	let (tx, rx) = oneshot::channel();
 
@@ -864,7 +864,7 @@ async fn query_available_data(
 
 async fn query_chunk(
 	virtual_overseer: &mut test_helpers::TestSubsystemContextHandle<AvailabilityStoreMessage>,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	index: u32,
 ) -> Option<ErasureChunk> {
 	let (tx, rx) = oneshot::channel();
diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs
index 175b4732724d2ba22158e24800ba377316a8fd3f..0cb1c40cdcb357a4cdced1599e27725534c33554 100644
--- a/polkadot/node/core/backing/src/lib.rs
+++ b/polkadot/node/core/backing/src/lib.rs
@@ -32,7 +32,7 @@ use futures::{
 use sp_keystore::SyncCryptoStorePtr;
 use polkadot_primitives::v1::{
 	CommittedCandidateReceipt, BackedCandidate, Id as ParaId, ValidatorId,
-	ValidatorIndex, SigningContext, PoV,
+	ValidatorIndex, SigningContext, PoV, CandidateHash,
 	CandidateDescriptor, AvailableData, ValidatorSignature, Hash, CandidateReceipt,
 	CandidateCommitments, CoreState, CoreIndex, CollatorId, ValidationOutputs,
 };
@@ -103,12 +103,12 @@ struct CandidateBackingJob {
 	/// The collator required to author the candidate, if any.
 	required_collator: Option<CollatorId>,
 	/// We issued `Valid` or `Invalid` statements on about these candidates.
-	issued_statements: HashSet<Hash>,
+	issued_statements: HashSet<CandidateHash>,
 	/// `Some(h)` if this job has already issues `Seconded` statemt for some candidate with `h` hash.
-	seconded: Option<Hash>,
+	seconded: Option<CandidateHash>,
 	/// The candidates that are includable, by hash. Each entry here indicates
 	/// that we've sent the provisioner the backed candidate.
-	backed: HashSet<Hash>,
+	backed: HashSet<CandidateHash>,
 	/// We have already reported misbehaviors for these validators.
 	reported_misbehavior_for: HashSet<ValidatorIndex>,
 	keystore: SyncCryptoStorePtr,
@@ -131,12 +131,12 @@ struct TableContext {
 
 impl TableContextTrait for TableContext {
 	type AuthorityId = ValidatorIndex;
-	type Digest = Hash;
+	type Digest = CandidateHash;
 	type GroupId = ParaId;
 	type Signature = ValidatorSignature;
 	type Candidate = CommittedCandidateReceipt;
 
-	fn candidate_digest(candidate: &CommittedCandidateReceipt) -> Hash {
+	fn candidate_digest(candidate: &CommittedCandidateReceipt) -> CandidateHash {
 		candidate.hash()
 	}
 
@@ -341,6 +341,7 @@ impl CandidateBackingJob {
 				// the collator, do not make available and report the collator.
 				let commitments_check = self.make_pov_available(
 					pov,
+					candidate_hash,
 					validation_data,
 					outputs,
 					|commitments| if commitments.hash() == candidate.commitments_hash {
@@ -525,7 +526,7 @@ impl CandidateBackingJob {
 		&mut self,
 		summary: TableSummary,
 	) -> Result<(), Error> {
-		let candidate_hash = summary.candidate.clone();
+		let candidate_hash = summary.candidate;
 
 		if self.issued_statements.contains(&candidate_hash) {
 			return Ok(())
@@ -559,6 +560,7 @@ impl CandidateBackingJob {
 				// If validation produces a new set of commitments, we vote the candidate as invalid.
 				let commitments_check = self.make_pov_available(
 					(&*pov).clone(),
+					candidate_hash,
 					validation_data,
 					outputs,
 					|commitments| if commitments == expected_commitments {
@@ -667,12 +669,13 @@ impl CandidateBackingJob {
 		&mut self,
 		id: Option<ValidatorIndex>,
 		n_validators: u32,
+		candidate_hash: CandidateHash,
 		available_data: AvailableData,
 	) -> Result<(), Error> {
 		let (tx, rx) = oneshot::channel();
 		self.tx_from.send(FromJob::AvailabilityStore(
 				AvailabilityStoreMessage::StoreAvailableData(
-					self.parent,
+					candidate_hash,
 					id,
 					n_validators,
 					available_data,
@@ -694,6 +697,7 @@ impl CandidateBackingJob {
 	async fn make_pov_available<T, E>(
 		&mut self,
 		pov: PoV,
+		candidate_hash: CandidateHash,
 		validation_data: polkadot_primitives::v1::PersistedValidationData,
 		outputs: ValidationOutputs,
 		with_commitments: impl FnOnce(CandidateCommitments) -> Result<T, E>,
@@ -727,6 +731,7 @@ impl CandidateBackingJob {
 		self.store_available_data(
 			self.table_context.validator.as_ref().map(|v| v.index()),
 			self.table_context.validators.len() as u32,
+			candidate_hash,
 			available_data,
 		).await?;
 
@@ -1206,8 +1211,8 @@ mod tests {
 			assert_matches!(
 				virtual_overseer.recv().await,
 				AllMessages::AvailabilityStore(
-					AvailabilityStoreMessage::StoreAvailableData(parent_hash, _, _, _, tx)
-				) if parent_hash == test_state.relay_parent => {
+					AvailabilityStoreMessage::StoreAvailableData(candidate_hash, _, _, _, tx)
+				) if candidate_hash == candidate.hash() => {
 					tx.send(Ok(())).unwrap();
 				}
 			);
@@ -1333,8 +1338,8 @@ mod tests {
 			assert_matches!(
 				virtual_overseer.recv().await,
 				AllMessages::AvailabilityStore(
-					AvailabilityStoreMessage::StoreAvailableData(parent_hash, _, _, _, tx)
-				) if parent_hash == test_state.relay_parent => {
+					AvailabilityStoreMessage::StoreAvailableData(candidate_hash, _, _, _, tx)
+				) if candidate_hash == candidate_a.hash() => {
 					tx.send(Ok(())).unwrap();
 				}
 			);
@@ -1482,8 +1487,8 @@ mod tests {
 			assert_matches!(
 				virtual_overseer.recv().await,
 				AllMessages::AvailabilityStore(
-					AvailabilityStoreMessage::StoreAvailableData(parent_hash, _, _, _, tx)
-					) if parent_hash == test_state.relay_parent => {
+					AvailabilityStoreMessage::StoreAvailableData(candidate_hash, _, _, _, tx)
+				) if candidate_hash == candidate_a.hash() => {
 						tx.send(Ok(())).unwrap();
 					}
 			);
@@ -1665,8 +1670,8 @@ mod tests {
 			assert_matches!(
 				virtual_overseer.recv().await,
 				AllMessages::AvailabilityStore(
-					AvailabilityStoreMessage::StoreAvailableData(parent_hash, _, _, _, tx)
-				) if parent_hash == test_state.relay_parent => {
+					AvailabilityStoreMessage::StoreAvailableData(candidate_hash, _, _, _, tx)
+				) if candidate_hash == candidate_b.hash() => {
 					tx.send(Ok(())).unwrap();
 				}
 			);
diff --git a/polkadot/node/core/bitfield-signing/src/lib.rs b/polkadot/node/core/bitfield-signing/src/lib.rs
index f91bd3ba7b3e5379aa3ac9992e7469cfc8b3c61f..8a351805802da2bffa3e58b59546cedd8af73877 100644
--- a/polkadot/node/core/bitfield-signing/src/lib.rs
+++ b/polkadot/node/core/bitfield-signing/src/lib.rs
@@ -174,7 +174,7 @@ async fn get_core_availability(
 			.await
 			.send(
 				AvailabilityStoreMessage::QueryChunkAvailability(
-					committed_candidate_receipt.descriptor.pov_hash,
+					committed_candidate_receipt.hash(),
 					validator_idx,
 					tx,
 				).into(),
diff --git a/polkadot/node/network/availability-distribution/src/lib.rs b/polkadot/node/network/availability-distribution/src/lib.rs
index 9badc682f68f064cd51a493180e84624730ab84f..3c86911b257819fd85eab267e03e790c6ba2ec64 100644
--- a/polkadot/node/network/availability-distribution/src/lib.rs
+++ b/polkadot/node/network/availability-distribution/src/lib.rs
@@ -38,7 +38,7 @@ use polkadot_node_network_protocol::{
 use polkadot_node_subsystem_util::metrics::{self, prometheus};
 use polkadot_primitives::v1::{
 	BlakeTwo256, CommittedCandidateReceipt, CoreState, ErasureChunk, Hash, HashT, Id as ParaId,
-	SessionIndex, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID,
+	SessionIndex, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, CandidateHash,
 };
 use polkadot_subsystem::messages::{
 	AllMessages, AvailabilityDistributionMessage, AvailabilityStoreMessage, ChainApiMessage,
@@ -130,7 +130,7 @@ const BENEFIT_VALID_MESSAGE: Rep = Rep::new(10, "Valid message");
 #[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, Hash)]
 pub struct AvailabilityGossipMessage {
 	/// Anchor hash of the candidate the `ErasureChunk` is associated to.
-	pub candidate_hash: Hash,
+	pub candidate_hash: CandidateHash,
 	/// The erasure chunk, a encoded information part of `AvailabilityData`.
 	pub erasure_chunk: ErasureChunk,
 }
@@ -149,13 +149,13 @@ struct ProtocolState {
 	/// Caches a mapping of relay parents or ancestor to live candidate receipts.
 	/// Allows fast intersection of live candidates with views and consecutive unioning.
 	/// Maps relay parent / ancestor -> live candidate receipts + its hash.
-	receipts: HashMap<Hash, HashSet<(Hash, CommittedCandidateReceipt)>>,
+	receipts: HashMap<Hash, HashSet<(CandidateHash, CommittedCandidateReceipt)>>,
 
 	/// Allow reverse caching of view checks.
 	/// Maps candidate hash -> relay parent for extracting meta information from `PerRelayParent`.
 	/// Note that the presence of this is not sufficient to determine if deletion is OK, i.e.
 	/// two histories could cover this.
-	reverse: HashMap<Hash, Hash>,
+	reverse: HashMap<CandidateHash, Hash>,
 
 	/// Keeps track of which candidate receipts are required due to ancestors of which relay parents
 	/// of our view.
@@ -166,7 +166,7 @@ struct ProtocolState {
 	per_relay_parent: HashMap<Hash, PerRelayParent>,
 
 	/// Track data that is specific to a candidate.
-	per_candidate: HashMap<Hash, PerCandidate>,
+	per_candidate: HashMap<CandidateHash, PerCandidate>,
 }
 
 #[derive(Debug, Clone, Default)]
@@ -176,11 +176,11 @@ struct PerCandidate {
 	/// candidate hash + erasure chunk index -> gossip message
 	message_vault: HashMap<u32, AvailabilityGossipMessage>,
 
-	/// Track received candidate hashes and chunk indices from peers.
-	received_messages: HashMap<PeerId, HashSet<(Hash, ValidatorIndex)>>,
+	/// Track received candidate hashes and validator indices from peers.
+	received_messages: HashMap<PeerId, HashSet<(CandidateHash, ValidatorIndex)>>,
 
 	/// Track already sent candidate hashes and the erasure chunk index to the peers.
-	sent_messages: HashMap<PeerId, HashSet<(Hash, ValidatorIndex)>>,
+	sent_messages: HashMap<PeerId, HashSet<(CandidateHash, ValidatorIndex)>>,
 
 	/// The set of validators.
 	validators: Vec<ValidatorId>,
@@ -221,7 +221,7 @@ impl ProtocolState {
 	fn cached_live_candidates_unioned<'a>(
 		&'a self,
 		relay_parents: impl IntoIterator<Item = &'a Hash> + 'a,
-	) -> HashMap<Hash, CommittedCandidateReceipt> {
+	) -> HashMap<CandidateHash, CommittedCandidateReceipt> {
 		let relay_parents_and_ancestors = self.extend_with_ancestors(relay_parents);
 		relay_parents_and_ancestors
 			.into_iter()
@@ -229,7 +229,7 @@ impl ProtocolState {
 			.map(|receipt_set| receipt_set.into_iter())
 			.flatten()
 			.map(|(receipt_hash, receipt)| (receipt_hash.clone(), receipt.clone()))
-			.collect::<HashMap<Hash, CommittedCandidateReceipt>>()
+			.collect()
 	}
 
 	async fn add_relay_parent<Context>(
@@ -296,8 +296,9 @@ impl ProtocolState {
 				// remove from the ancestry index
 				self.ancestry.remove(relay_parent);
 				// and also remove the actual receipt
-				self.receipts.remove(relay_parent);
-				self.per_candidate.remove(relay_parent);
+				if let Some(candidates) = self.receipts.remove(relay_parent) {
+					candidates.into_iter().for_each(|c| { self.per_candidate.remove(&c.0); });
+				}
 			}
 		}
 		if let Some(per_relay_parent) = self.per_relay_parent.remove(relay_parent) {
@@ -313,8 +314,9 @@ impl ProtocolState {
 						// remove from the ancestry index
 						self.ancestry.remove(&ancestor);
 						// and also remove the actual receipt
-						self.receipts.remove(&ancestor);
-						self.per_candidate.remove(&ancestor);
+						if let Some(candidates) = self.receipts.remove(&ancestor) {
+							candidates.into_iter().for_each(|c| { self.per_candidate.remove(&c.0); });
+						}
 					}
 				}
 			}
@@ -645,8 +647,7 @@ where
 	let live_candidates = state.cached_live_candidates_unioned(state.view.0.iter());
 
 	// check if the candidate is of interest
-	let live_candidate = if let Some(live_candidate) = live_candidates.get(&message.candidate_hash)
-	{
+	let live_candidate = if let Some(live_candidate) = live_candidates.get(&message.candidate_hash) {
 		live_candidate
 	} else {
 		return modify_reputation(ctx, origin, COST_NOT_A_LIVE_CANDIDATE).await;
@@ -862,7 +863,7 @@ async fn query_live_candidates<Context>(
 	ctx: &mut Context,
 	state: &mut ProtocolState,
 	relay_parents: impl IntoIterator<Item = Hash>,
-) -> Result<HashMap<Hash, (Hash, CommittedCandidateReceipt)>>
+) -> Result<HashMap<Hash, (CandidateHash, CommittedCandidateReceipt)>>
 where
 	Context: SubsystemContext<Message = AvailabilityDistributionMessage>,
 {
@@ -871,7 +872,7 @@ where
 
 	let capacity = hint.1.unwrap_or(hint.0) * (1 + AvailabilityDistributionSubsystem::K);
 	let mut live_candidates =
-		HashMap::<Hash, (Hash, CommittedCandidateReceipt)>::with_capacity(capacity);
+		HashMap::<Hash, (CandidateHash, CommittedCandidateReceipt)>::with_capacity(capacity);
 
 	for relay_parent in iter {
 		// register one of relay parents (not the ancestors)
@@ -969,7 +970,7 @@ where
 }
 
 /// Query the proof of validity for a particular candidate hash.
-async fn query_data_availability<Context>(ctx: &mut Context, candidate_hash: Hash) -> Result<bool>
+async fn query_data_availability<Context>(ctx: &mut Context, candidate_hash: CandidateHash) -> Result<bool>
 where
 	Context: SubsystemContext<Message = AvailabilityDistributionMessage>,
 {
@@ -985,7 +986,7 @@ where
 
 async fn query_chunk<Context>(
 	ctx: &mut Context,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	validator_index: ValidatorIndex,
 ) -> Result<Option<ErasureChunk>>
 where
@@ -1002,7 +1003,7 @@ where
 
 async fn store_chunk<Context>(
 	ctx: &mut Context,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	relay_parent: Hash,
 	validator_index: ValidatorIndex,
 	erasure_chunk: ErasureChunk,
@@ -1012,18 +1013,18 @@ where
 {
 	let (tx, rx) = oneshot::channel();
 	ctx.send_message(
-        AllMessages::AvailabilityStore(
-                AvailabilityStoreMessage::StoreChunk {
-                candidate_hash,
-                relay_parent,
-                validator_index,
-                chunk: erasure_chunk,
-                tx,
-            }
-        )).await
-        .map_err(|e| Error::StoreChunkSendQuery(e))?;
-
-    rx.await.map_err(|e| Error::StoreChunkResponseChannel(e))
+		AllMessages::AvailabilityStore(
+				AvailabilityStoreMessage::StoreChunk {
+				candidate_hash,
+				relay_parent,
+				validator_index,
+				chunk: erasure_chunk,
+				tx,
+			}
+		)).await
+		.map_err(|e| Error::StoreChunkSendQuery(e))?;
+
+	rx.await.map_err(|e| Error::StoreChunkResponseChannel(e))
 }
 
 /// Request the head data for a particular para.
diff --git a/polkadot/node/network/availability-distribution/src/tests.rs b/polkadot/node/network/availability-distribution/src/tests.rs
index 8012da546b09e34dc08e7edd36c332fd4db894fc..e3e60e1f9e5cd7ba614f8ff78e76642d9ae0ea92 100644
--- a/polkadot/node/network/availability-distribution/src/tests.rs
+++ b/polkadot/node/network/availability-distribution/src/tests.rs
@@ -254,7 +254,7 @@ fn make_erasure_root(test: &TestState, pov: PoV) -> Hash {
 
 fn make_valid_availability_gossip(
 	test: &TestState,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	erasure_chunk_index: u32,
 	pov: PoV,
 ) -> AvailabilityGossipMessage {
@@ -320,7 +320,7 @@ fn helper_integrity() {
 	.build();
 
 	let message =
-		make_valid_availability_gossip(&test_state, dbg!(candidate.hash()), 2, pov_block.clone());
+		make_valid_availability_gossip(&test_state, candidate.hash(), 2, pov_block.clone());
 
 	let root = dbg!(&candidate.commitments.erasure_root);
 
diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs
index 31ec729d8b8663ea8abac49612fd073908fdb487..cd3aca73be9fb730fe90e9dc3bf5c5ede6048727 100644
--- a/polkadot/node/network/protocol/src/lib.rs
+++ b/polkadot/node/network/protocol/src/lib.rs
@@ -186,7 +186,7 @@ impl View {
 pub mod v1 {
 	use polkadot_primitives::v1::{
 		Hash, CollatorId, Id as ParaId, ErasureChunk, CandidateReceipt,
-		SignedAvailabilityBitfield, PoV,
+		SignedAvailabilityBitfield, PoV, CandidateHash,
 	};
 	use polkadot_node_primitives::SignedFullStatement;
 	use parity_scale_codec::{Encode, Decode};
@@ -198,7 +198,7 @@ pub mod v1 {
 	pub enum AvailabilityDistributionMessage {
 		/// An erasure chunk for a given candidate hash.
 		#[codec(index = "0")]
-		Chunk(Hash, ErasureChunk),
+		Chunk(CandidateHash, ErasureChunk),
 	}
 
 	/// Network messages used by the bitfield distribution subsystem.
diff --git a/polkadot/node/network/statement-distribution/src/lib.rs b/polkadot/node/network/statement-distribution/src/lib.rs
index 180b66eee1a4ba6fca1d9746923a156ea82d31c8..bb4c192c4ff4132972261ac5ccf6dd1502699146 100644
--- a/polkadot/node/network/statement-distribution/src/lib.rs
+++ b/polkadot/node/network/statement-distribution/src/lib.rs
@@ -35,7 +35,7 @@ use polkadot_node_subsystem_util::{
 };
 use node_primitives::SignedFullStatement;
 use polkadot_primitives::v1::{
-	Hash, CompactStatement, ValidatorIndex, ValidatorId, SigningContext, ValidatorSignature,
+	Hash, CompactStatement, ValidatorIndex, ValidatorId, SigningContext, ValidatorSignature, CandidateHash,
 };
 use polkadot_node_network_protocol::{
 	v1 as protocol_v1, View, PeerId, ReputationChange as Rep, NetworkBridgeEvent,
@@ -102,32 +102,32 @@ impl StatementDistribution {
 /// via other means.
 #[derive(Default)]
 struct VcPerPeerTracker {
-	local_observed: arrayvec::ArrayVec<[Hash; VC_THRESHOLD]>,
-	remote_observed: arrayvec::ArrayVec<[Hash; VC_THRESHOLD]>,
+	local_observed: arrayvec::ArrayVec<[CandidateHash; VC_THRESHOLD]>,
+	remote_observed: arrayvec::ArrayVec<[CandidateHash; VC_THRESHOLD]>,
 }
 
 impl VcPerPeerTracker {
-	// Note that the remote should now be aware that a validator has seconded a given candidate (by hash)
-	// based on a message that we have sent it from our local pool.
-	fn note_local(&mut self, h: Hash) {
+	/// Note that the remote should now be aware that a validator has seconded a given candidate (by hash)
+	/// based on a message that we have sent it from our local pool.
+	fn note_local(&mut self, h: CandidateHash) {
 		if !note_hash(&mut self.local_observed, h) {
 			log::warn!("Statement distribution is erroneously attempting to distribute more \
 				than {} candidate(s) per validator index. Ignoring", VC_THRESHOLD);
 		}
 	}
 
-	// Note that the remote should now be aware that a validator has seconded a given candidate (by hash)
-	// based on a message that it has sent us.
-	//
-	// Returns `true` if the peer was allowed to send us such a message, `false` otherwise.
-	fn note_remote(&mut self, h: Hash) -> bool {
+	/// Note that the remote should now be aware that a validator has seconded a given candidate (by hash)
+	/// based on a message that it has sent us.
+	///
+	/// Returns `true` if the peer was allowed to send us such a message, `false` otherwise.
+	fn note_remote(&mut self, h: CandidateHash) -> bool {
 		note_hash(&mut self.remote_observed, h)
 	}
 }
 
 fn note_hash(
-	observed: &mut arrayvec::ArrayVec<[Hash; VC_THRESHOLD]>,
-	h: Hash,
+	observed: &mut arrayvec::ArrayVec<[CandidateHash; VC_THRESHOLD]>,
+	h: CandidateHash,
 ) -> bool {
 	if observed.contains(&h) { return true; }
 
@@ -139,7 +139,7 @@ fn note_hash(
 struct PeerRelayParentKnowledge {
 	/// candidates that the peer is aware of. This indicates that we can
 	/// send other statements pertaining to that candidate.
-	known_candidates: HashSet<Hash>,
+	known_candidates: HashSet<CandidateHash>,
 	/// fingerprints of all statements a peer should be aware of: those that
 	/// were sent to the peer by us.
 	sent_statements: HashSet<(CompactStatement, ValidatorIndex)>,
@@ -149,7 +149,7 @@ struct PeerRelayParentKnowledge {
 	/// How many candidates this peer is aware of for each given validator index.
 	seconded_counts: HashMap<ValidatorIndex, VcPerPeerTracker>,
 	/// How many statements we've received for each candidate that we're aware of.
-	received_message_count: HashMap<Hash, usize>,
+	received_message_count: HashMap<CandidateHash, usize>,
 }
 
 impl PeerRelayParentKnowledge {
@@ -246,7 +246,7 @@ impl PeerRelayParentKnowledge {
 
 		{
 			let received_per_candidate = self.received_message_count
-				.entry(candidate_hash.clone())
+				.entry(*candidate_hash)
 				.or_insert(0);
 
 			if *received_per_candidate >= max_message_count {
@@ -372,7 +372,7 @@ enum NotedStatement<'a> {
 
 struct ActiveHeadData {
 	/// All candidates we are aware of for this head, keyed by hash.
-	candidates: HashSet<Hash>,
+	candidates: HashSet<CandidateHash>,
 	/// Stored statements for circulation to peers.
 	///
 	/// These are iterable in insertion order, and `Seconded` statements are always
@@ -464,7 +464,7 @@ impl ActiveHeadData {
 	}
 
 	/// Get an iterator over all statements for the active head that are for a particular candidate.
-	fn statements_about(&self, candidate_hash: Hash)
+	fn statements_about(&self, candidate_hash: CandidateHash)
 		-> impl Iterator<Item = &'_ StoredStatement> + '_
 	{
 		self.statements().filter(move |s| s.compact().candidate_hash() == &candidate_hash)
@@ -521,10 +521,10 @@ async fn circulate_statement_and_dependents(
 
 		// First circulate the statement directly to all peers needing it.
 		// The borrow of `active_head` needs to encompass only this (Rust) statement.
-		let outputs: Option<(Hash, Vec<PeerId>)> = {
+		let outputs: Option<(CandidateHash, Vec<PeerId>)> = {
 			match active_head.note_statement(statement) {
 				NotedStatement::Fresh(stored) => Some((
-					stored.compact().candidate_hash().clone(),
+					*stored.compact().candidate_hash(),
 					circulate_statement(peers, ctx, relay_parent, stored).await?,
 				)),
 				_ => None,
@@ -602,7 +602,7 @@ async fn send_statements_about(
 	peer_data: &mut PeerData,
 	ctx: &mut impl SubsystemContext<Message = StatementDistributionMessage>,
 	relay_parent: Hash,
-	candidate_hash: Hash,
+	candidate_hash: CandidateHash,
 	active_head: &ActiveHeadData,
 	metrics: &Metrics,
 ) -> SubsystemResult<()> {
@@ -1110,8 +1110,8 @@ mod tests {
 
 	#[test]
 	fn note_local_works() {
-		let hash_a: Hash = [1; 32].into();
-		let hash_b: Hash = [2; 32].into();
+		let hash_a = CandidateHash([1; 32].into());
+		let hash_b = CandidateHash([2; 32].into());
 
 		let mut per_peer_tracker = VcPerPeerTracker::default();
 		per_peer_tracker.note_local(hash_a.clone());
@@ -1126,9 +1126,9 @@ mod tests {
 
 	#[test]
 	fn note_remote_works() {
-		let hash_a: Hash = [1; 32].into();
-		let hash_b: Hash = [2; 32].into();
-		let hash_c: Hash = [3; 32].into();
+		let hash_a = CandidateHash([1; 32].into());
+		let hash_b = CandidateHash([2; 32].into());
+		let hash_c = CandidateHash([3; 32].into());
 
 		let mut per_peer_tracker = VcPerPeerTracker::default();
 		assert!(per_peer_tracker.note_remote(hash_a.clone()));
@@ -1148,7 +1148,7 @@ mod tests {
 	fn per_peer_relay_parent_knowledge_send() {
 		let mut knowledge = PeerRelayParentKnowledge::default();
 
-		let hash_a: Hash = [1; 32].into();
+		let hash_a = CandidateHash([1; 32].into());
 
 		// Sending an un-pinned statement should not work and should have no effect.
 		assert!(knowledge.send(&(CompactStatement::Valid(hash_a), 0)).is_none());
@@ -1180,7 +1180,7 @@ mod tests {
 	fn cant_send_after_receiving() {
 		let mut knowledge = PeerRelayParentKnowledge::default();
 
-		let hash_a: Hash = [1; 32].into();
+		let hash_a = CandidateHash([1; 32].into());
 		assert!(knowledge.receive(&(CompactStatement::Candidate(hash_a), 0), 3).unwrap());
 		assert!(knowledge.send(&(CompactStatement::Candidate(hash_a), 0)).is_none());
 	}
@@ -1189,7 +1189,7 @@ mod tests {
 	fn per_peer_relay_parent_knowledge_receive() {
 		let mut knowledge = PeerRelayParentKnowledge::default();
 
-		let hash_a: Hash = [1; 32].into();
+		let hash_a = CandidateHash([1; 32].into());
 
 		assert_eq!(
 			knowledge.receive(&(CompactStatement::Valid(hash_a), 0), 3),
@@ -1226,8 +1226,8 @@ mod tests {
 		assert_eq!(knowledge.received_statements.len(), 3); // number of prior `Ok`s.
 
 		// Now make sure that the seconding limit is respected.
-		let hash_b: Hash = [2; 32].into();
-		let hash_c: Hash = [3; 32].into();
+		let hash_b = CandidateHash([2; 32].into());
+		let hash_c = CandidateHash([3; 32].into());
 
 		assert_eq!(
 			knowledge.receive(&(CompactStatement::Candidate(hash_b), 0), 3),
diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs
index a506376772d0793c1c9b4596c3b4e77db0245823..01e19bc65b5eed7c74df57b49b7123d10f2fd23c 100644
--- a/polkadot/node/overseer/src/lib.rs
+++ b/polkadot/node/overseer/src/lib.rs
@@ -1635,7 +1635,7 @@ mod tests {
 	use std::collections::HashMap;
 	use futures::{executor, pin_mut, select, channel::mpsc, FutureExt};
 
-	use polkadot_primitives::v1::{BlockData, CollatorPair, PoV};
+	use polkadot_primitives::v1::{BlockData, CollatorPair, PoV, CandidateHash};
 	use polkadot_subsystem::messages::RuntimeApiRequest;
 	use polkadot_node_primitives::{Collation, CollationGenerationConfig};
 	use polkadot_node_network_protocol::{PeerId, ReputationChange, NetworkBridgeEvent};
@@ -2273,7 +2273,7 @@ mod tests {
 
 	fn test_availability_store_msg() -> AvailabilityStoreMessage {
 		let (sender, _) = oneshot::channel();
-		AvailabilityStoreMessage::QueryAvailableData(Default::default(), sender)
+		AvailabilityStoreMessage::QueryAvailableData(CandidateHash(Default::default()), sender)
 	}
 
 	fn test_network_bridge_msg() -> NetworkBridgeMessage {
diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs
index ddcec114ebe2510f9d147742e82da1b101792acf..45affc5756a37d468cde4183e5adf08627b61de6 100644
--- a/polkadot/node/primitives/src/lib.rs
+++ b/polkadot/node/primitives/src/lib.rs
@@ -28,7 +28,7 @@ use polkadot_primitives::v1::{
 	Hash, CommittedCandidateReceipt, CandidateReceipt, CompactStatement,
 	EncodeAs, Signed, SigningContext, ValidatorIndex, ValidatorId,
 	UpwardMessage, ValidationCode, PersistedValidationData, ValidationData,
-	HeadData, PoV, CollatorPair, Id as ParaId, ValidationOutputs,
+	HeadData, PoV, CollatorPair, Id as ParaId, ValidationOutputs, CandidateHash,
 };
 use polkadot_statement_table::{
 	generic::{
@@ -54,10 +54,10 @@ pub enum Statement {
 	Seconded(CommittedCandidateReceipt),
 	/// A statement that a validator has deemed a candidate valid.
 	#[codec(index = "2")]
-	Valid(Hash),
+	Valid(CandidateHash),
 	/// A statement that a validator has deemed a candidate invalid.
 	#[codec(index = "3")]
-	Invalid(Hash),
+	Invalid(CandidateHash),
 }
 
 impl Statement {
diff --git a/polkadot/node/subsystem/src/messages.rs b/polkadot/node/subsystem/src/messages.rs
index 7cc078bd3f3e0e74c16d608f2be61af6f09265e8..9198d46c5eb383b46e422715d074c5a5cb6226cd 100644
--- a/polkadot/node/subsystem/src/messages.rs
+++ b/polkadot/node/subsystem/src/messages.rs
@@ -36,7 +36,7 @@ use polkadot_primitives::v1::{
 	CollatorId, CommittedCandidateReceipt, CoreState, ErasureChunk,
 	GroupRotationInfo, Hash, Id as ParaId, OccupiedCoreAssumption,
 	PersistedValidationData, PoV, SessionIndex, SignedAvailabilityBitfield,
-	ValidationCode, ValidatorId, ValidationData,
+	ValidationCode, ValidatorId, ValidationData, CandidateHash,
 	ValidatorIndex, ValidatorSignature, InboundDownwardMessage,
 };
 use std::sync::Arc;
@@ -289,31 +289,31 @@ impl BitfieldSigningMessage {
 #[derive(Debug)]
 pub enum AvailabilityStoreMessage {
 	/// Query a `AvailableData` from the AV store.
-	QueryAvailableData(Hash, oneshot::Sender<Option<AvailableData>>),
+	QueryAvailableData(CandidateHash, oneshot::Sender<Option<AvailableData>>),
 
 	/// Query whether a `AvailableData` exists within the AV Store.
 	///
 	/// This is useful in cases when existence
 	/// matters, but we don't want to necessarily pass around multiple
 	/// megabytes of data to get a single bit of information.
-	QueryDataAvailability(Hash, oneshot::Sender<bool>),
+	QueryDataAvailability(CandidateHash, oneshot::Sender<bool>),
 
 	/// Query an `ErasureChunk` from the AV store by the candidate hash and validator index.
-	QueryChunk(Hash, ValidatorIndex, oneshot::Sender<Option<ErasureChunk>>),
+	QueryChunk(CandidateHash, ValidatorIndex, oneshot::Sender<Option<ErasureChunk>>),
 
 	/// Query whether an `ErasureChunk` exists within the AV Store.
 	///
 	/// This is useful in cases like bitfield signing, when existence
 	/// matters, but we don't want to necessarily pass around large
 	/// quantities of data to get a single bit of information.
-	QueryChunkAvailability(Hash, ValidatorIndex, oneshot::Sender<bool>),
+	QueryChunkAvailability(CandidateHash, ValidatorIndex, oneshot::Sender<bool>),
 
 	/// Store an `ErasureChunk` in the AV store.
 	///
 	/// Return `Ok(())` if the store operation succeeded, `Err(())` if it failed.
 	StoreChunk {
 		/// A hash of the candidate this chunk belongs to.
-		candidate_hash: Hash,
+		candidate_hash: CandidateHash,
 		/// A relevant relay parent.
 		relay_parent: Hash,
 		/// The index of the validator this chunk belongs to.
@@ -328,7 +328,7 @@ pub enum AvailabilityStoreMessage {
 	/// If `ValidatorIndex` is present store corresponding chunk also.
 	///
 	/// Return `Ok(())` if the store operation succeeded, `Err(())` if it failed.
-	StoreAvailableData(Hash, Option<ValidatorIndex>, u32, AvailableData, oneshot::Sender<Result<(), ()>>),
+	StoreAvailableData(CandidateHash, Option<ValidatorIndex>, u32, AvailableData, oneshot::Sender<Result<(), ()>>),
 }
 
 impl AvailabilityStoreMessage {
diff --git a/polkadot/primitives/src/v0.rs b/polkadot/primitives/src/v0.rs
index a01b3c743fa9fdd3088d11ed895aa6563a6288c1..5f6e3ad544a98f3f58d5df0c6a232c6c2e963786 100644
--- a/polkadot/primitives/src/v0.rs
+++ b/polkadot/primitives/src/v0.rs
@@ -633,18 +633,18 @@ pub struct ErasureChunk {
 pub enum CompactStatement {
 	/// Proposal of a parachain candidate.
 	#[codec(index = "1")]
-	Candidate(Hash),
+	Candidate(CandidateHash),
 	/// State that a parachain candidate is valid.
 	#[codec(index = "2")]
-	Valid(Hash),
+	Valid(CandidateHash),
 	/// State that a parachain candidate is invalid.
 	#[codec(index = "3")]
-	Invalid(Hash),
+	Invalid(CandidateHash),
 }
 
 impl CompactStatement {
 	/// Get the underlying candidate hash this references.
-	pub fn candidate_hash(&self) -> &Hash {
+	pub fn candidate_hash(&self) -> &CandidateHash {
 		match *self {
 			CompactStatement::Candidate(ref h)
 				| CompactStatement::Valid(ref h)
@@ -684,7 +684,7 @@ impl ValidityAttestation {
 	/// which should be known in context.
 	pub fn signed_payload<H: Encode>(
 		&self,
-		candidate_hash: Hash,
+		candidate_hash: CandidateHash,
 		signing_context: &SigningContext<H>,
 	) -> Vec<u8> {
 		match *self {
diff --git a/polkadot/primitives/src/v1.rs b/polkadot/primitives/src/v1.rs
index 3533263d2083aa9fe4664b52bc8f52df9d677f3f..6b3a2342f1d40f5be3a947acb85291fd9dd985b0 100644
--- a/polkadot/primitives/src/v1.rs
+++ b/polkadot/primitives/src/v1.rs
@@ -31,7 +31,7 @@ pub use runtime_primitives::traits::{BlakeTwo256, Hash as HashT};
 pub use polkadot_core_primitives::v1::{
 	BlockNumber, Moment, Signature, AccountPublic, AccountId, AccountIndex,
 	ChainId, Hash, Nonce, Balance, Header, Block, BlockId, UncheckedExtrinsic,
-	Remark, DownwardMessage, InboundDownwardMessage,
+	Remark, DownwardMessage, InboundDownwardMessage, CandidateHash,
 };
 
 // Export some polkadot-parachain primitives
@@ -148,8 +148,8 @@ impl<H> CandidateReceipt<H> {
 	}
 
 	/// Computes the blake2-256 hash of the receipt.
-	pub fn hash(&self) -> Hash where H: Encode {
-		BlakeTwo256::hash_of(self)
+	pub fn hash(&self) -> CandidateHash where H: Encode {
+		CandidateHash(BlakeTwo256::hash_of(self))
 	}
 }
 
@@ -196,7 +196,7 @@ impl<H: Clone> CommittedCandidateReceipt<H> {
 	///
 	/// This computes the canonical hash, not the hash of the directly encoded data.
 	/// Thus this is a shortcut for `candidate.to_plain().hash()`.
-	pub fn hash(&self) -> Hash where H: Encode {
+	pub fn hash(&self) -> CandidateHash where H: Encode {
 		self.to_plain().hash()
 	}
 }
@@ -421,7 +421,7 @@ pub fn check_candidate_backing<H: AsRef<[u8]> + Clone + Encode>(
 	}
 
 	// this is known, even in runtime, to be blake2-256.
-	let hash: Hash = backed.candidate.hash();
+	let hash = backed.candidate.hash();
 
 	let mut signed = 0;
 	for ((val_in_group_idx, _), attestation) in backed.validator_indices.iter().enumerate()
diff --git a/polkadot/roadmap/implementers-guide/src/types/network.md b/polkadot/roadmap/implementers-guide/src/types/network.md
index 75f251613f2524361380f97e99de39178c37c334..a5472a4071537dbd0b765beb762fe6f16bf88212 100644
--- a/polkadot/roadmap/implementers-guide/src/types/network.md
+++ b/polkadot/roadmap/implementers-guide/src/types/network.md
@@ -23,7 +23,7 @@ enum ObservedRole {
 ```rust
 enum AvailabilityDistributionV1Message {
 	/// An erasure chunk for a given candidate hash.
-	Chunk(Hash, ErasureChunk),
+	Chunk(CandidateHash, ErasureChunk),
 }
 ```
 
diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md
index 484d53b70f4aae0d9e8c6eb2eac700b8bbdec502..e0ab023a16dc32cabaf7655e5feced4159408178 100644
--- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md
+++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md
@@ -136,18 +136,18 @@ Messages to and from the availability store.
 ```rust
 enum AvailabilityStoreMessage {
 	/// Query the `AvailableData` of a candidate by hash.
-	QueryAvailableData(Hash, ResponseChannel<Option<AvailableData>>),
+	QueryAvailableData(CandidateHash, ResponseChannel<Option<AvailableData>>),
 	/// Query whether an `AvailableData` exists within the AV Store.
-	QueryDataAvailability(Hash, ResponseChannel<bool>),
+	QueryDataAvailability(CandidateHash, ResponseChannel<bool>),
 	/// Query a specific availability chunk of the candidate's erasure-coding by validator index.
 	/// Returns the chunk and its inclusion proof against the candidate's erasure-root.
-	QueryChunk(Hash, ValidatorIndex, ResponseChannel<Option<AvailabilityChunkAndProof>>),
+	QueryChunk(CandidateHash, ValidatorIndex, ResponseChannel<Option<AvailabilityChunkAndProof>>),
 	/// Store a specific chunk of the candidate's erasure-coding by validator index, with an
 	/// accompanying proof.
-	StoreChunk(Hash, ValidatorIndex, AvailabilityChunkAndProof, ResponseChannel<Result<()>>),
+	StoreChunk(CandidateHash, ValidatorIndex, AvailabilityChunkAndProof, ResponseChannel<Result<()>>),
 	/// Store `AvailableData`. If `ValidatorIndex` is provided, also store this validator's
 	/// `AvailabilityChunkAndProof`.
-	StoreAvailableData(Hash, Option<ValidatorIndex>, u32, AvailableData, ResponseChannel<Result<()>>),
+	StoreAvailableData(CandidateHash, Option<ValidatorIndex>, u32, AvailableData, ResponseChannel<Result<()>>),
 }
 ```
 
diff --git a/polkadot/statement-table/src/lib.rs b/polkadot/statement-table/src/lib.rs
index fed60ded0da2a08a60e82d37ec80c3951e1a810b..a00b582b7dc7d6ab0fdd4c5b22995cb7023ccf6a 100644
--- a/polkadot/statement-table/src/lib.rs
+++ b/polkadot/statement-table/src/lib.rs
@@ -18,63 +18,21 @@ pub mod generic;
 
 pub use generic::{Table, Context};
 
-/// Concrete instantiations suitable for v0 primitives.
-pub mod v0 {
-	use crate::generic;
-	use primitives::v0::{
-		Hash,
-		Id, AbridgedCandidateReceipt, CompactStatement as PrimitiveStatement, ValidatorSignature, ValidatorIndex,
-	};
-
-	/// Statements about candidates on the network.
-	pub type Statement = generic::Statement<AbridgedCandidateReceipt, Hash>;
-
-	/// Signed statements about candidates.
-	pub type SignedStatement = generic::SignedStatement<
-		AbridgedCandidateReceipt,
-		Hash,
-		ValidatorIndex,
-		ValidatorSignature,
-	>;
-
-	/// Kinds of misbehavior, along with proof.
-	pub type Misbehavior = generic::Misbehavior<
-		AbridgedCandidateReceipt,
-		Hash,
-		ValidatorIndex,
-		ValidatorSignature,
-	>;
-
-	/// A summary of import of a statement.
-	pub type Summary = generic::Summary<Hash, Id>;
-
-	impl<'a> From<&'a Statement> for PrimitiveStatement {
-		fn from(s: &'a Statement) -> PrimitiveStatement {
-			match *s {
-				generic::Statement::Valid(s) => PrimitiveStatement::Valid(s),
-				generic::Statement::Invalid(s) => PrimitiveStatement::Invalid(s),
-				generic::Statement::Candidate(ref s) => PrimitiveStatement::Candidate(s.hash()),
-			}
-		}
-	}
-}
-
 /// Concrete instantiations suitable for v1 primitives.
 pub mod v1 {
 	use crate::generic;
 	use primitives::v1::{
-		Hash,
-		Id, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement,
+		CandidateHash, Id, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement,
 		ValidatorSignature, ValidatorIndex,
 	};
 
 	/// Statements about candidates on the network.
-	pub type Statement = generic::Statement<CommittedCandidateReceipt, Hash>;
+	pub type Statement = generic::Statement<CommittedCandidateReceipt, CandidateHash>;
 
 	/// Signed statements about candidates.
 	pub type SignedStatement = generic::SignedStatement<
 		CommittedCandidateReceipt,
-		Hash,
+		CandidateHash,
 		ValidatorIndex,
 		ValidatorSignature,
 	>;
@@ -82,13 +40,13 @@ pub mod v1 {
 	/// Kinds of misbehavior, along with proof.
 	pub type Misbehavior = generic::Misbehavior<
 		CommittedCandidateReceipt,
-		Hash,
+		CandidateHash,
 		ValidatorIndex,
 		ValidatorSignature,
 	>;
 
 	/// A summary of import of a statement.
-	pub type Summary = generic::Summary<Hash, Id>;
+	pub type Summary = generic::Summary<CandidateHash, Id>;
 
 	impl<'a> From<&'a Statement> for PrimitiveStatement {
 		fn from(s: &'a Statement) -> PrimitiveStatement {