mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 11:41:02 +00:00
Include a reference to the validation data in the candidate descriptor (#1442)
* rename GlobalValidationSchedule to GlobalValidationData * guide: update candidate descriptor to contain validation data hash * guide: add note in inclusion module about checking validation data hash * primitives: update CandidateDescriptor to contain new hash * fix payload computation * add helpers for computing validation data to runtime modules * guide: note routines * inclusion: check validation data hash and fix local_validation_data bug * add a case to candidate_checks and improve that test substantially * bump versions * address review comments * add a test for including code upgrade * bump kusama version * bump westend & polkadot versions
This commit is contained in:
committed by
GitHub
parent
1ed17cd467
commit
09f602f8de
@@ -22,6 +22,7 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use primitives::v1::{
|
||||
validation_data_hash,
|
||||
ValidatorId, CandidateCommitments, CandidateDescriptor, ValidatorIndex, Id as ParaId,
|
||||
AvailabilityBitfield as AvailabilityBitfield, SignedAvailabilityBitfields, SigningContext,
|
||||
BackedCandidate, CoreIndex, GroupIndex, CoreAssignment, CommittedCandidateReceipt,
|
||||
@@ -145,6 +146,8 @@ decl_error! {
|
||||
InvalidBacking,
|
||||
/// Collator did not sign PoV.
|
||||
NotCollatorSigned,
|
||||
/// The validation data hash does not match expected.
|
||||
ValidationDataHashMismatch,
|
||||
/// Internal error only returned when compiled with debug assertions.
|
||||
InternalError,
|
||||
}
|
||||
@@ -399,14 +402,21 @@ impl<T: Trait> Module<T> {
|
||||
Error::<T>::CandidateNotInParentContext,
|
||||
);
|
||||
|
||||
let code_upgrade_allowed = <paras::Module<T>>::last_code_upgrade(para_id, true)
|
||||
.map_or(
|
||||
true,
|
||||
|last| last <= relay_parent_number &&
|
||||
relay_parent_number.saturating_sub(last) >= config.validation_upgrade_frequency,
|
||||
);
|
||||
// if any, the code upgrade attempt is allowed.
|
||||
let valid_upgrade_attempt =
|
||||
candidate.candidate.commitments.new_validation_code.is_none() ||
|
||||
<paras::Module<T>>::last_code_upgrade(para_id, true)
|
||||
.map_or(
|
||||
true,
|
||||
|last| last <= relay_parent_number &&
|
||||
relay_parent_number.saturating_sub(last)
|
||||
>= config.validation_upgrade_frequency,
|
||||
);
|
||||
|
||||
ensure!(code_upgrade_allowed, Error::<T>::PrematureCodeUpgrade);
|
||||
ensure!(
|
||||
valid_upgrade_attempt,
|
||||
Error::<T>::PrematureCodeUpgrade,
|
||||
);
|
||||
ensure!(
|
||||
candidate.descriptor().check_collator_signature().is_ok(),
|
||||
Error::<T>::NotCollatorSigned,
|
||||
@@ -423,6 +433,32 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// this should never fail because the para is registered
|
||||
let (global_validation_data, local_validation_data) = (
|
||||
<configuration::Module<T>>::global_validation_data(),
|
||||
match <paras::Module<T>>::local_validation_data(para_id) {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
// We don't want to error out here because it will
|
||||
// brick the relay-chain. So we return early without
|
||||
// doing anything.
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let expected = validation_data_hash(
|
||||
&global_validation_data,
|
||||
&local_validation_data,
|
||||
);
|
||||
|
||||
ensure!(
|
||||
expected == candidate.descriptor().validation_data_hash,
|
||||
Error::<T>::ValidationDataHashMismatch,
|
||||
);
|
||||
}
|
||||
|
||||
ensure!(
|
||||
<PendingAvailability<T>>::get(¶_id).is_none() &&
|
||||
<PendingAvailabilityCommitments>::get(¶_id).is_none(),
|
||||
@@ -686,6 +722,7 @@ mod tests {
|
||||
let payload = primitives::v1::collator_signature_payload(
|
||||
&candidate.descriptor.relay_parent,
|
||||
&candidate.descriptor.para_id,
|
||||
&candidate.descriptor.validation_data_hash,
|
||||
&candidate.descriptor.pov_hash,
|
||||
);
|
||||
|
||||
@@ -814,6 +851,7 @@ mod tests {
|
||||
head_data: HeadData,
|
||||
pov_hash: Hash,
|
||||
relay_parent: Hash,
|
||||
validation_data_hash: Hash,
|
||||
new_validation_code: Option<ValidationCode>,
|
||||
}
|
||||
|
||||
@@ -824,6 +862,7 @@ mod tests {
|
||||
para_id: self.para_id,
|
||||
pov_hash: self.pov_hash,
|
||||
relay_parent: self.relay_parent,
|
||||
validation_data_hash: self.validation_data_hash,
|
||||
..Default::default()
|
||||
},
|
||||
commitments: CandidateCommitments {
|
||||
@@ -835,6 +874,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_vdata_hash(para_id: ParaId) -> Option<Hash> {
|
||||
let global_validation_data = Configuration::global_validation_data();
|
||||
let local_validation_data = Paras::local_validation_data(para_id)?;
|
||||
Some(validation_data_hash(&global_validation_data, &local_validation_data))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collect_pending_cleans_up_pending() {
|
||||
let chain_a = ParaId::from(1);
|
||||
@@ -1261,6 +1306,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1276,11 +1322,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::UnscheduledCandidate.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidates out of order.
|
||||
@@ -1289,12 +1338,14 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
let mut candidate_b = TestCandidateBuilder {
|
||||
para_id: chain_b,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([2; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_b).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1324,11 +1375,15 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed_b, backed_a],
|
||||
vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
// out-of-order manifests as unscheduled.
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed_b, backed_a],
|
||||
vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::UnscheduledCandidate.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not backed.
|
||||
@@ -1337,6 +1392,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1352,11 +1408,14 @@ mod tests {
|
||||
BackingKind::Lacking,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::InsufficientBacking.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not in parent context.
|
||||
@@ -1368,6 +1427,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: wrong_parent_hash,
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1383,11 +1443,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateNotInParentContext.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate has wrong collator.
|
||||
@@ -1396,6 +1459,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1413,15 +1477,18 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
chain_b_assignment.clone(),
|
||||
thread_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
chain_b_assignment.clone(),
|
||||
thread_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::WrongCollator.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// candidate not well-signed by collator.
|
||||
@@ -1430,6 +1497,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1450,11 +1518,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::NotCollatorSigned.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// para occupied - reject.
|
||||
@@ -1463,6 +1534,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1489,11 +1561,14 @@ mod tests {
|
||||
});
|
||||
<PendingAvailabilityCommitments>::insert(&chain_a, candidate.commitments);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateScheduledBeforeParaFree.into()),
|
||||
);
|
||||
|
||||
<PendingAvailability<Test>>::remove(&chain_a);
|
||||
<PendingAvailabilityCommitments>::remove(&chain_a);
|
||||
@@ -1505,6 +1580,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1524,11 +1600,14 @@ mod tests {
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::CandidateScheduledBeforeParaFree.into()),
|
||||
);
|
||||
|
||||
<PendingAvailabilityCommitments>::remove(&chain_a);
|
||||
}
|
||||
@@ -1540,6 +1619,7 @@ mod tests {
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
new_validation_code: Some(vec![5, 6, 7, 8].into()),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
@@ -1564,11 +1644,47 @@ mod tests {
|
||||
|
||||
assert_eq!(Paras::last_code_upgrade(chain_a, true), Some(10));
|
||||
|
||||
assert!(Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![thread_a_assignment.clone()],
|
||||
&group_validators,
|
||||
).is_err());
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::PrematureCodeUpgrade.into()),
|
||||
);
|
||||
}
|
||||
|
||||
// Bad validation data hash - reject
|
||||
{
|
||||
let mut candidate = TestCandidateBuilder {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: [42u8; 32].into(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
|
||||
collator_sign_candidate(
|
||||
Sr25519Keyring::One,
|
||||
&mut candidate,
|
||||
);
|
||||
|
||||
let backed = back_candidate(
|
||||
candidate,
|
||||
&validators,
|
||||
group_validators(GroupIndex::from(0)).unwrap().as_ref(),
|
||||
&signing_context,
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Inclusion::process_candidates(
|
||||
vec![backed],
|
||||
vec![chain_a_assignment.clone()],
|
||||
&group_validators,
|
||||
),
|
||||
Err(Error::<Test>::ValidationDataHashMismatch.into()),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1634,6 +1750,7 @@ mod tests {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1645,6 +1762,7 @@ mod tests {
|
||||
para_id: chain_b,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([2; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_b).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1656,6 +1774,7 @@ mod tests {
|
||||
para_id: thread_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([3; 32]),
|
||||
validation_data_hash: make_vdata_hash(thread_a).unwrap(),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
@@ -1746,6 +1865,91 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_include_candidate_with_ok_code_upgrade() {
|
||||
let chain_a = ParaId::from(1);
|
||||
|
||||
let paras = vec![(chain_a, true)];
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave,
|
||||
Sr25519Keyring::Ferdie,
|
||||
];
|
||||
let validator_public = validator_pubkeys(&validators);
|
||||
|
||||
new_test_ext(genesis_config(paras)).execute_with(|| {
|
||||
Validators::set(validator_public.clone());
|
||||
CurrentSessionIndex::set(5);
|
||||
|
||||
run_to_block(5, |_| None);
|
||||
|
||||
let signing_context = SigningContext {
|
||||
parent_hash: System::parent_hash(),
|
||||
session_index: 5,
|
||||
};
|
||||
|
||||
let group_validators = |group_index: GroupIndex| match group_index {
|
||||
group_index if group_index == GroupIndex::from(0) => Some(vec![0, 1, 2, 3, 4]),
|
||||
_ => panic!("Group index out of bounds for 1 parachain"),
|
||||
};
|
||||
|
||||
let chain_a_assignment = CoreAssignment {
|
||||
core: CoreIndex::from(0),
|
||||
para_id: chain_a,
|
||||
kind: AssignmentKind::Parachain,
|
||||
group_idx: GroupIndex::from(0),
|
||||
};
|
||||
|
||||
let mut candidate_a = TestCandidateBuilder {
|
||||
para_id: chain_a,
|
||||
relay_parent: System::parent_hash(),
|
||||
pov_hash: Hash::from([1; 32]),
|
||||
validation_data_hash: make_vdata_hash(chain_a).unwrap(),
|
||||
new_validation_code: Some(vec![1, 2, 3].into()),
|
||||
..Default::default()
|
||||
}.build();
|
||||
collator_sign_candidate(
|
||||
Sr25519Keyring::One,
|
||||
&mut candidate_a,
|
||||
);
|
||||
|
||||
let backed_a = back_candidate(
|
||||
candidate_a.clone(),
|
||||
&validators,
|
||||
group_validators(GroupIndex::from(0)).unwrap().as_ref(),
|
||||
&signing_context,
|
||||
BackingKind::Threshold,
|
||||
);
|
||||
|
||||
let occupied_cores = Inclusion::process_candidates(
|
||||
vec![backed_a],
|
||||
vec![
|
||||
chain_a_assignment.clone(),
|
||||
],
|
||||
&group_validators,
|
||||
).expect("candidates scheduled, in order, and backed");
|
||||
|
||||
assert_eq!(occupied_cores, vec![CoreIndex::from(0)]);
|
||||
|
||||
assert_eq!(
|
||||
<PendingAvailability<Test>>::get(&chain_a),
|
||||
Some(CandidatePendingAvailability {
|
||||
core: CoreIndex::from(0),
|
||||
descriptor: candidate_a.descriptor,
|
||||
availability_votes: default_availability_votes(),
|
||||
relay_parent_number: System::block_number() - 1,
|
||||
backed_in_number: System::block_number(),
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
<PendingAvailabilityCommitments>::get(&chain_a),
|
||||
Some(candidate_a.commitments),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_change_wipes_and_updates_session_info() {
|
||||
let chain_a = ParaId::from(1);
|
||||
|
||||
Reference in New Issue
Block a user