Unverified Commit e8f89b30 authored by Bastian Köcher's avatar Bastian Köcher Committed by GitHub
Browse files

Ensure that `para_head` hash matches the actual head (#3808)

* Ensure that `para_head` hash matches the actual head

This ensures that the `para_head` hash in the `CandidateDescriptor`
matches the actual `head` in the candidate commitments.

* Enable the code for `no_std`

* Formatting
parent 1876963f
Pipeline #155940 canceled with stages
in 10 minutes and 7 seconds
......@@ -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<u8>);
#[cfg(feature = "std")]
impl HeadData {
/// Returns the hash of this head data.
pub fn hash(&self) -> Hash {
......
......@@ -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<T: Config> Pallet<T> {
Error::<T>::InvalidValidationCodeHash,
);
ensure!(
candidate.descriptor().para_head ==
candidate.candidate.commitments.head_data.hash(),
Error::<T>::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<Hash>,
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::<Test>::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::<Test>::ParaHeadMismatch.into()),
);
}
});
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment