diff --git a/parachain/src/primitives.rs b/parachain/src/primitives.rs index d8677e7bb759..c6920a6a9558 100644 --- a/parachain/src/primitives.rs +++ b/parachain/src/primitives.rs @@ -45,7 +45,6 @@ pub use polkadot_core_primitives::BlockNumber as RelayChainBlockNumber; #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Default, Hash, MallocSizeOf))] pub struct HeadData(#[cfg_attr(feature = "std", serde(with = "bytes"))] pub Vec); -#[cfg(feature = "std")] impl HeadData { /// Returns the hash of this head data. pub fn hash(&self) -> Hash { diff --git a/runtime/parachains/src/inclusion.rs b/runtime/parachains/src/inclusion.rs index 5c43c2308696..4d92e90df734 100644 --- a/runtime/parachains/src/inclusion.rs +++ b/runtime/parachains/src/inclusion.rs @@ -194,6 +194,9 @@ pub mod pallet { InvalidOutboundHrmp, /// The validation code hash of the candidate is not valid. InvalidValidationCodeHash, + /// The `para_head` hash in the candidate descriptor doesn't match the hash of the actual para head in the + /// commitments. + ParaHeadMismatch, } /// The latest bitfield for each validator, referred to by their index in the validator set. @@ -465,6 +468,12 @@ impl Pallet { Error::::InvalidValidationCodeHash, ); + ensure!( + candidate.descriptor().para_head == + candidate.candidate.commitments.head_data.hash(), + Error::::ParaHeadMismatch, + ); + if let Err(err) = check_cx.check_validation_outputs( para_id, &candidate.candidate.commitments.head_data, @@ -1169,6 +1178,7 @@ mod tests { struct TestCandidateBuilder { para_id: ParaId, head_data: HeadData, + para_head_hash: Option, pov_hash: Hash, relay_parent: Hash, persisted_validation_data_hash: Hash, @@ -1186,6 +1196,7 @@ mod tests { relay_parent: self.relay_parent, persisted_validation_data_hash: self.persisted_validation_data_hash, validation_code_hash: self.validation_code.hash(), + para_head: self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), ..Default::default() }, commitments: CandidateCommitments { @@ -2214,6 +2225,41 @@ mod tests { Err(Error::::InvalidValidationCodeHash.into()), ); } + + // Para head hash in descriptor doesn't match head data + { + let mut candidate = TestCandidateBuilder { + para_id: chain_a, + relay_parent: System::parent_hash(), + pov_hash: Hash::repeat_byte(1), + persisted_validation_data_hash: make_vdata_hash(chain_a).unwrap(), + hrmp_watermark: RELAY_PARENT_NUM, + para_head_hash: Some(Hash::random()), + ..Default::default() + } + .build(); + + collator_sign_candidate(Sr25519Keyring::One, &mut candidate); + + let backed = block_on(back_candidate( + candidate, + &validators, + group_validators(GroupIndex::from(0)).unwrap().as_ref(), + &keystore, + &signing_context, + BackingKind::Threshold, + )); + + assert_eq!( + ParaInclusion::process_candidates( + Default::default(), + vec![backed], + vec![chain_a_assignment.clone()], + &group_validators, + ), + Err(Error::::ParaHeadMismatch.into()), + ); + } }); }