use crate::{
configuration, disputes, dmp, hrmp, paras, paras_inherent::DisputedBitfield,
scheduler::CoreAssignment, shared, ump,
};
use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
use frame_support::pallet_prelude::*;
use parity_scale_codec::{Decode, Encode};
use primitives::v2::{
AvailabilityBitfield, BackedCandidate, CandidateCommitments, CandidateDescriptor,
CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash,
HeadData, Id as ParaId, SigningContext, UncheckedSignedAvailabilityBitfields, ValidatorId,
ValidatorIndex, ValidityAttestation,
};
use scale_info::TypeInfo;
use sp_runtime::{traits::One, DispatchError};
use sp_std::{collections::btree_set::BTreeSet, prelude::*};
pub use pallet::*;
#[cfg(test)]
pub(crate) mod tests;
#[derive(Encode, Decode, TypeInfo)]
#[cfg_attr(test, derive(Debug))]
pub struct AvailabilityBitfieldRecord<N> {
bitfield: AvailabilityBitfield, submitted_at: N, }
#[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub(crate) enum FullCheck {
Yes,
Skip,
}
#[derive(Encode, Decode, PartialEq, TypeInfo)]
#[cfg_attr(test, derive(Debug))]
pub struct CandidatePendingAvailability<H, N> {
core: CoreIndex,
hash: CandidateHash,
descriptor: CandidateDescriptor<H>,
availability_votes: BitVec<u8, BitOrderLsb0>,
backers: BitVec<u8, BitOrderLsb0>,
relay_parent_number: N,
backed_in_number: N,
backing_group: GroupIndex,
}
impl<H, N> CandidatePendingAvailability<H, N> {
pub(crate) fn availability_votes(&self) -> &BitVec<u8, BitOrderLsb0> {
&self.availability_votes
}
pub(crate) fn backed_in_number(&self) -> &N {
&self.backed_in_number
}
pub(crate) fn core_occupied(&self) -> CoreIndex {
self.core
}
pub(crate) fn candidate_hash(&self) -> CandidateHash {
self.hash
}
pub(crate) fn candidate_descriptor(&self) -> &CandidateDescriptor<H> {
&self.descriptor
}
#[cfg(any(feature = "runtime-benchmarks", test))]
pub(crate) fn new(
core: CoreIndex,
hash: CandidateHash,
descriptor: CandidateDescriptor<H>,
availability_votes: BitVec<u8, BitOrderLsb0>,
backers: BitVec<u8, BitOrderLsb0>,
relay_parent_number: N,
backed_in_number: N,
backing_group: GroupIndex,
) -> Self {
Self {
core,
hash,
descriptor,
availability_votes,
backers,
relay_parent_number,
backed_in_number,
backing_group,
}
}
}
pub trait RewardValidators {
fn reward_backing(validators: impl IntoIterator<Item = ValidatorIndex>);
fn reward_bitfields(validators: impl IntoIterator<Item = ValidatorIndex>);
}
#[derive(Encode, Decode, PartialEq, TypeInfo)]
#[cfg_attr(test, derive(Debug))]
pub(crate) struct ProcessedCandidates<H = Hash> {
pub(crate) core_indices: Vec<CoreIndex>,
pub(crate) candidate_receipt_with_backing_validator_indices:
Vec<(CandidateReceipt<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
}
impl<H> Default for ProcessedCandidates<H> {
fn default() -> Self {
Self {
core_indices: Vec::new(),
candidate_receipt_with_backing_validator_indices: Vec::new(),
}
}
}
pub fn minimum_backing_votes(n_validators: usize) -> usize {
sp_std::cmp::min(n_validators, 2)
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config:
frame_system::Config
+ shared::Config
+ paras::Config
+ dmp::Config
+ ump::Config
+ hrmp::Config
+ configuration::Config
{
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type DisputesHandler: disputes::DisputesHandler<Self::BlockNumber>;
type RewardValidators: RewardValidators;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
CandidateBacked(CandidateReceipt<T::Hash>, HeadData, CoreIndex, GroupIndex),
CandidateIncluded(CandidateReceipt<T::Hash>, HeadData, CoreIndex, GroupIndex),
CandidateTimedOut(CandidateReceipt<T::Hash>, HeadData, CoreIndex),
}
#[pallet::error]
pub enum Error<T> {
UnsortedOrDuplicateValidatorIndices,
UnsortedOrDuplicateDisputeStatementSet,
UnsortedOrDuplicateBackedCandidates,
UnexpectedRelayParent,
WrongBitfieldSize,
BitfieldAllZeros,
BitfieldDuplicateOrUnordered,
ValidatorIndexOutOfBounds,
InvalidBitfieldSignature,
UnscheduledCandidate,
CandidateScheduledBeforeParaFree,
WrongCollator,
ScheduledOutOfOrder,
HeadDataTooLarge,
PrematureCodeUpgrade,
NewCodeTooLarge,
CandidateNotInParentContext,
InvalidGroupIndex,
InsufficientBacking,
InvalidBacking,
NotCollatorSigned,
ValidationDataHashMismatch,
IncorrectDownwardMessageHandling,
InvalidUpwardMessages,
HrmpWatermarkMishandling,
InvalidOutboundHrmp,
InvalidValidationCodeHash,
ParaHeadMismatch,
BitfieldReferencesFreedCore,
}
#[pallet::storage]
pub(crate) type AvailabilityBitfields<T: Config> =
StorageMap<_, Twox64Concat, ValidatorIndex, AvailabilityBitfieldRecord<T::BlockNumber>>;
#[pallet::storage]
pub(crate) type PendingAvailability<T: Config> =
StorageMap<_, Twox64Concat, ParaId, CandidatePendingAvailability<T::Hash, T::BlockNumber>>;
#[pallet::storage]
pub(crate) type PendingAvailabilityCommitments<T: Config> =
StorageMap<_, Twox64Concat, ParaId, CandidateCommitments>;
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
const LOG_TARGET: &str = "runtime::inclusion";
impl<T: Config> Pallet<T> {
pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight {
Weight::zero()
}
pub(crate) fn initializer_finalize() {}
pub(crate) fn initializer_on_new_session(
_notification: &crate::initializer::SessionChangeNotification<T::BlockNumber>,
) {
for _ in <PendingAvailabilityCommitments<T>>::drain() {}
for _ in <PendingAvailability<T>>::drain() {}
for _ in <AvailabilityBitfields<T>>::drain() {}
}
pub(crate) fn update_pending_availability_and_get_freed_cores<F>(
expected_bits: usize,
validators: &[ValidatorId],
signed_bitfields: UncheckedSignedAvailabilityBitfields,
core_lookup: F,
enact_candidate: bool,
) -> Vec<(CoreIndex, CandidateHash)>
where
F: Fn(CoreIndex) -> Option<ParaId>,
{
let mut assigned_paras_record = (0..expected_bits)
.map(|bit_index| core_lookup(CoreIndex::from(bit_index as u32)))
.map(|opt_para_id| {
opt_para_id.map(|para_id| (para_id, PendingAvailability::<T>::get(¶_id)))
})
.collect::<Vec<_>>();
let now = <frame_system::Pallet<T>>::block_number();
for (checked_bitfield, validator_index) in
signed_bitfields.into_iter().map(|signed_bitfield| {
let validator_idx = signed_bitfield.unchecked_validator_index();
let checked_bitfield = signed_bitfield.unchecked_into_payload();
(checked_bitfield, validator_idx)
}) {
for (bit_idx, _) in checked_bitfield.0.iter().enumerate().filter(|(_, is_av)| **is_av) {
let pending_availability = if let Some((_, pending_availability)) =
assigned_paras_record[bit_idx].as_mut()
{
pending_availability
} else {
continue
};
let validator_index = validator_index.0 as usize;
if let Some(mut bit) =
pending_availability.as_mut().and_then(|candidate_pending_availability| {
candidate_pending_availability.availability_votes.get_mut(validator_index)
}) {
*bit = true;
}
}
let record =
AvailabilityBitfieldRecord { bitfield: checked_bitfield, submitted_at: now };
<AvailabilityBitfields<T>>::insert(&validator_index, record);
}
let threshold = availability_threshold(validators.len());
let mut freed_cores = Vec::with_capacity(expected_bits);
for (para_id, pending_availability) in assigned_paras_record
.into_iter()
.flatten()
.filter_map(|(id, p)| p.map(|p| (id, p)))
{
if pending_availability.availability_votes.count_ones() >= threshold {
<PendingAvailability<T>>::remove(¶_id);
let commitments = match PendingAvailabilityCommitments::<T>::take(¶_id) {
Some(commitments) => commitments,
None => {
log::warn!(
target: LOG_TARGET,
"Inclusion::process_bitfields: PendingAvailability and PendingAvailabilityCommitments
are out of sync, did someone mess with the storage?",
);
continue
},
};
if enact_candidate {
let receipt = CommittedCandidateReceipt {
descriptor: pending_availability.descriptor,
commitments,
};
let _weight = Self::enact_candidate(
pending_availability.relay_parent_number,
receipt,
pending_availability.backers,
pending_availability.availability_votes,
pending_availability.core,
pending_availability.backing_group,
);
}
freed_cores.push((pending_availability.core, pending_availability.hash));
} else {
<PendingAvailability<T>>::insert(¶_id, &pending_availability);
}
}
freed_cores
}
pub(crate) fn process_bitfields(
expected_bits: usize,
signed_bitfields: UncheckedSignedAvailabilityBitfields,
disputed_bitfield: DisputedBitfield,
core_lookup: impl Fn(CoreIndex) -> Option<ParaId>,
full_check: FullCheck,
) -> Result<Vec<(CoreIndex, CandidateHash)>, crate::inclusion::Error<T>> {
let validators = shared::Pallet::<T>::active_validator_keys();
let session_index = shared::Pallet::<T>::session_index();
let parent_hash = frame_system::Pallet::<T>::parent_hash();
let checked_bitfields = crate::paras_inherent::assure_sanity_bitfields::<T>(
signed_bitfields,
disputed_bitfield,
expected_bits,
parent_hash,
session_index,
&validators[..],
full_check,
)?;
let freed_cores = Self::update_pending_availability_and_get_freed_cores::<_>(
expected_bits,
&validators[..],
checked_bitfields,
core_lookup,
true,
);
Ok(freed_cores)
}
pub(crate) fn process_candidates<GV>(
parent_storage_root: T::Hash,
candidates: Vec<BackedCandidate<T::Hash>>,
scheduled: Vec<CoreAssignment>,
group_validators: GV,
) -> Result<ProcessedCandidates<T::Hash>, DispatchError>
where
GV: Fn(GroupIndex) -> Option<Vec<ValidatorIndex>>,
{
ensure!(candidates.len() <= scheduled.len(), Error::<T>::UnscheduledCandidate);
if scheduled.is_empty() {
return Ok(ProcessedCandidates::default())
}
let validators = shared::Pallet::<T>::active_validator_keys();
let parent_hash = <frame_system::Pallet<T>>::parent_hash();
let now = <frame_system::Pallet<T>>::block_number();
let relay_parent_number = now - One::one();
let check_ctx = CandidateCheckContext::<T>::new(now, relay_parent_number);
let mut candidate_receipt_with_backing_validator_indices =
Vec::with_capacity(candidates.len());
let core_indices_and_backers = {
let mut skip = 0;
let mut core_indices_and_backers = Vec::with_capacity(candidates.len());
let mut last_core = None;
let mut check_assignment_in_order = |assignment: &CoreAssignment| -> DispatchResult {
ensure!(
last_core.map_or(true, |core| assignment.core > core),
Error::<T>::ScheduledOutOfOrder,
);
last_core = Some(assignment.core);
Ok(())
};
let signing_context =
SigningContext { parent_hash, session_index: shared::Pallet::<T>::session_index() };
'next_backed_candidate: for (candidate_idx, backed_candidate) in
candidates.iter().enumerate()
{
match check_ctx.verify_backed_candidate(
parent_hash,
parent_storage_root,
candidate_idx,
backed_candidate,
)? {
Err(FailedToCreatePVD) => {
log::debug!(
target: LOG_TARGET,
"Failed to create PVD for candidate {} on relay parent {:?}",
candidate_idx,
parent_hash,
);
return Ok(ProcessedCandidates::default())
},
Ok(rpn) => rpn,
}
let para_id = backed_candidate.descriptor().para_id;
let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()];
for (i, assignment) in scheduled[skip..].iter().enumerate() {
check_assignment_in_order(assignment)?;
if para_id == assignment.para_id {
if let Some(required_collator) = assignment.required_collator() {
ensure!(
required_collator == &backed_candidate.descriptor().collator,
Error::<T>::WrongCollator,
);
}
ensure!(
<PendingAvailability<T>>::get(¶_id).is_none() &&
<PendingAvailabilityCommitments<T>>::get(¶_id).is_none(),
Error::<T>::CandidateScheduledBeforeParaFree,
);
skip = i + skip + 1;
let group_vals = group_validators(assignment.group_idx)
.ok_or_else(|| Error::<T>::InvalidGroupIndex)?;
{
let maybe_amount_validated = primitives::v2::check_candidate_backing(
&backed_candidate,
&signing_context,
group_vals.len(),
|intra_group_vi| {
group_vals
.get(intra_group_vi)
.and_then(|vi| validators.get(vi.0 as usize))
.map(|v| v.clone())
},
);
match maybe_amount_validated {
Ok(amount_validated) => ensure!(
amount_validated >= minimum_backing_votes(group_vals.len()),
Error::<T>::InsufficientBacking,
),
Err(()) => {
Err(Error::<T>::InvalidBacking)?;
},
}
let mut backer_idx_and_attestation =
Vec::<(ValidatorIndex, ValidityAttestation)>::with_capacity(
backed_candidate.validator_indices.count_ones(),
);
let candidate_receipt = backed_candidate.receipt();
for ((bit_idx, _), attestation) in backed_candidate
.validator_indices
.iter()
.enumerate()
.filter(|(_, signed)| **signed)
.zip(backed_candidate.validity_votes.iter().cloned())
{
let val_idx = group_vals
.get(bit_idx)
.expect("this query succeeded above; qed");
backer_idx_and_attestation.push((*val_idx, attestation));
backers.set(val_idx.0 as _, true);
}
candidate_receipt_with_backing_validator_indices
.push((candidate_receipt, backer_idx_and_attestation));
}
core_indices_and_backers.push((
assignment.core,
backers,
assignment.group_idx,
));
continue 'next_backed_candidate
}
}
ensure!(false, Error::<T>::UnscheduledCandidate);
}
for assignment in scheduled[skip..].iter() {
check_assignment_in_order(assignment)?;
}
core_indices_and_backers
};
let core_indices = core_indices_and_backers.iter().map(|&(ref c, _, _)| *c).collect();
for (candidate, (core, backers, group)) in
candidates.into_iter().zip(core_indices_and_backers)
{
let para_id = candidate.descriptor().para_id;
let availability_votes: BitVec<u8, BitOrderLsb0> =
bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()];
Self::deposit_event(Event::<T>::CandidateBacked(
candidate.candidate.to_plain(),
candidate.candidate.commitments.head_data.clone(),
core,
group,
));
let candidate_hash = candidate.candidate.hash();
let (descriptor, commitments) =
(candidate.candidate.descriptor, candidate.candidate.commitments);
<PendingAvailability<T>>::insert(
¶_id,
CandidatePendingAvailability {
core,
hash: candidate_hash,
descriptor,
availability_votes,
relay_parent_number,
backers: backers.to_bitvec(),
backed_in_number: check_ctx.now,
backing_group: group,
},
);
<PendingAvailabilityCommitments<T>>::insert(¶_id, commitments);
}
Ok(ProcessedCandidates::<T::Hash> {
core_indices,
candidate_receipt_with_backing_validator_indices,
})
}
pub(crate) fn check_validation_outputs_for_runtime_api(
para_id: ParaId,
validation_outputs: primitives::v2::CandidateCommitments,
) -> bool {
let now = <frame_system::Pallet<T>>::block_number();
let relay_parent_number = now;
let check_ctx = CandidateCheckContext::<T>::new(now, relay_parent_number);
if let Err(err) = check_ctx.check_validation_outputs(
para_id,
&validation_outputs.head_data,
&validation_outputs.new_validation_code,
validation_outputs.processed_downward_messages,
&validation_outputs.upward_messages,
T::BlockNumber::from(validation_outputs.hrmp_watermark),
&validation_outputs.horizontal_messages,
) {
log::debug!(
target: LOG_TARGET,
"Validation outputs checking for parachain `{}` failed: {:?}",
u32::from(para_id),
err,
);
false
} else {
true
}
}
fn enact_candidate(
relay_parent_number: T::BlockNumber,
receipt: CommittedCandidateReceipt<T::Hash>,
backers: BitVec<u8, BitOrderLsb0>,
availability_votes: BitVec<u8, BitOrderLsb0>,
core_index: CoreIndex,
backing_group: GroupIndex,
) -> Weight {
let plain = receipt.to_plain();
let commitments = receipt.commitments;
let config = <configuration::Pallet<T>>::config();
T::RewardValidators::reward_backing(
backers
.iter()
.enumerate()
.filter(|(_, backed)| **backed)
.map(|(i, _)| ValidatorIndex(i as _)),
);
T::RewardValidators::reward_bitfields(
availability_votes
.iter()
.enumerate()
.filter(|(_, voted)| **voted)
.map(|(i, _)| ValidatorIndex(i as _)),
);
let mut weight = T::DbWeight::get().reads_writes(1, 0);
if let Some(new_code) = commitments.new_validation_code {
weight += <paras::Pallet<T>>::schedule_code_upgrade(
receipt.descriptor.para_id,
new_code,
relay_parent_number,
&config,
);
}
weight += <dmp::Pallet<T>>::prune_dmq(
receipt.descriptor.para_id,
commitments.processed_downward_messages,
);
weight += <ump::Pallet<T>>::receive_upward_messages(
receipt.descriptor.para_id,
commitments.upward_messages,
);
weight += <hrmp::Pallet<T>>::prune_hrmp(
receipt.descriptor.para_id,
T::BlockNumber::from(commitments.hrmp_watermark),
);
weight += <hrmp::Pallet<T>>::queue_outbound_hrmp(
receipt.descriptor.para_id,
commitments.horizontal_messages,
);
Self::deposit_event(Event::<T>::CandidateIncluded(
plain,
commitments.head_data.clone(),
core_index,
backing_group,
));
weight +
<paras::Pallet<T>>::note_new_head(
receipt.descriptor.para_id,
commitments.head_data,
relay_parent_number,
)
}
pub(crate) fn collect_pending(
pred: impl Fn(CoreIndex, T::BlockNumber) -> bool,
) -> Vec<CoreIndex> {
let mut cleaned_up_ids = Vec::new();
let mut cleaned_up_cores = Vec::new();
for (para_id, pending_record) in <PendingAvailability<T>>::iter() {
if pred(pending_record.core, pending_record.backed_in_number) {
cleaned_up_ids.push(para_id);
cleaned_up_cores.push(pending_record.core);
}
}
for para_id in cleaned_up_ids {
let pending = <PendingAvailability<T>>::take(¶_id);
let commitments = <PendingAvailabilityCommitments<T>>::take(¶_id);
if let (Some(pending), Some(commitments)) = (pending, commitments) {
let candidate = CandidateReceipt {
descriptor: pending.descriptor,
commitments_hash: commitments.hash(),
};
Self::deposit_event(Event::<T>::CandidateTimedOut(
candidate,
commitments.head_data,
pending.core,
));
}
}
cleaned_up_cores
}
pub(crate) fn collect_disputed(disputed: &BTreeSet<CandidateHash>) -> Vec<CoreIndex> {
let mut cleaned_up_ids = Vec::new();
let mut cleaned_up_cores = Vec::new();
for (para_id, pending_record) in <PendingAvailability<T>>::iter() {
if disputed.contains(&pending_record.hash) {
cleaned_up_ids.push(para_id);
cleaned_up_cores.push(pending_record.core);
}
}
for para_id in cleaned_up_ids {
let _ = <PendingAvailability<T>>::take(¶_id);
let _ = <PendingAvailabilityCommitments<T>>::take(¶_id);
}
cleaned_up_cores
}
pub(crate) fn force_enact(para: ParaId) {
let pending = <PendingAvailability<T>>::take(¶);
let commitments = <PendingAvailabilityCommitments<T>>::take(¶);
if let (Some(pending), Some(commitments)) = (pending, commitments) {
let candidate =
CommittedCandidateReceipt { descriptor: pending.descriptor, commitments };
Self::enact_candidate(
pending.relay_parent_number,
candidate,
pending.backers,
pending.availability_votes,
pending.core,
pending.backing_group,
);
}
}
pub(crate) fn candidate_pending_availability(
para: ParaId,
) -> Option<CommittedCandidateReceipt<T::Hash>> {
<PendingAvailability<T>>::get(¶)
.map(|p| p.descriptor)
.and_then(|d| <PendingAvailabilityCommitments<T>>::get(¶).map(move |c| (d, c)))
.map(|(d, c)| CommittedCandidateReceipt { descriptor: d, commitments: c })
}
pub(crate) fn pending_availability(
para: ParaId,
) -> Option<CandidatePendingAvailability<T::Hash, T::BlockNumber>> {
<PendingAvailability<T>>::get(¶)
}
}
const fn availability_threshold(n_validators: usize) -> usize {
let mut threshold = (n_validators * 2) / 3;
threshold += (n_validators * 2) % 3;
threshold
}
#[derive(derive_more::From, Debug)]
enum AcceptanceCheckErr<BlockNumber> {
HeadDataTooLarge,
PrematureCodeUpgrade,
NewCodeTooLarge,
ProcessedDownwardMessages(dmp::ProcessedDownwardMessagesAcceptanceErr),
UpwardMessages(ump::AcceptanceCheckErr),
HrmpWatermark(hrmp::HrmpWatermarkAcceptanceErr<BlockNumber>),
OutboundHrmp(hrmp::OutboundHrmpAcceptanceErr),
}
impl<BlockNumber> AcceptanceCheckErr<BlockNumber> {
fn strip_into_dispatch_err<T: Config>(self) -> Error<T> {
use AcceptanceCheckErr::*;
match self {
HeadDataTooLarge => Error::<T>::HeadDataTooLarge,
PrematureCodeUpgrade => Error::<T>::PrematureCodeUpgrade,
NewCodeTooLarge => Error::<T>::NewCodeTooLarge,
ProcessedDownwardMessages(_) => Error::<T>::IncorrectDownwardMessageHandling,
UpwardMessages(_) => Error::<T>::InvalidUpwardMessages,
HrmpWatermark(_) => Error::<T>::HrmpWatermarkMishandling,
OutboundHrmp(_) => Error::<T>::InvalidOutboundHrmp,
}
}
}
pub(crate) struct CandidateCheckContext<T: Config> {
config: configuration::HostConfiguration<T::BlockNumber>,
now: T::BlockNumber,
relay_parent_number: T::BlockNumber,
}
pub(crate) struct FailedToCreatePVD;
impl<T: Config> CandidateCheckContext<T> {
pub(crate) fn new(now: T::BlockNumber, relay_parent_number: T::BlockNumber) -> Self {
Self { config: <configuration::Pallet<T>>::config(), now, relay_parent_number }
}
pub(crate) fn verify_backed_candidate(
&self,
parent_hash: <T as frame_system::Config>::Hash,
parent_storage_root: T::Hash,
candidate_idx: usize,
backed_candidate: &BackedCandidate<<T as frame_system::Config>::Hash>,
) -> Result<Result<(), FailedToCreatePVD>, Error<T>> {
let para_id = backed_candidate.descriptor().para_id;
let now = <frame_system::Pallet<T>>::block_number();
let relay_parent_number = now - One::one();
{
let persisted_validation_data = match crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
parent_storage_root,
) {
Some(l) => l,
None => return Ok(Err(FailedToCreatePVD)),
};
let expected = persisted_validation_data.hash();
ensure!(
expected == backed_candidate.descriptor().persisted_validation_data_hash,
Error::<T>::ValidationDataHashMismatch,
);
}
ensure!(
backed_candidate.descriptor().relay_parent == parent_hash,
Error::<T>::CandidateNotInParentContext,
);
ensure!(
backed_candidate.descriptor().check_collator_signature().is_ok(),
Error::<T>::NotCollatorSigned,
);
let validation_code_hash = <paras::Pallet<T>>::current_code_hash(para_id)
.ok_or_else(|| Error::<T>::UnscheduledCandidate)?;
ensure!(
backed_candidate.descriptor().validation_code_hash == validation_code_hash,
Error::<T>::InvalidValidationCodeHash,
);
ensure!(
backed_candidate.descriptor().para_head ==
backed_candidate.candidate.commitments.head_data.hash(),
Error::<T>::ParaHeadMismatch,
);
if let Err(err) = self.check_validation_outputs(
para_id,
&backed_candidate.candidate.commitments.head_data,
&backed_candidate.candidate.commitments.new_validation_code,
backed_candidate.candidate.commitments.processed_downward_messages,
&backed_candidate.candidate.commitments.upward_messages,
T::BlockNumber::from(backed_candidate.candidate.commitments.hrmp_watermark),
&backed_candidate.candidate.commitments.horizontal_messages,
) {
log::debug!(
target: LOG_TARGET,
"Validation outputs checking during inclusion of a candidate {} for parachain `{}` failed: {:?}",
candidate_idx,
u32::from(para_id),
err,
);
Err(err.strip_into_dispatch_err::<T>())?;
};
Ok(Ok(()))
}
fn check_validation_outputs(
&self,
para_id: ParaId,
head_data: &HeadData,
new_validation_code: &Option<primitives::v2::ValidationCode>,
processed_downward_messages: u32,
upward_messages: &[primitives::v2::UpwardMessage],
hrmp_watermark: T::BlockNumber,
horizontal_messages: &[primitives::v2::OutboundHrmpMessage<ParaId>],
) -> Result<(), AcceptanceCheckErr<T::BlockNumber>> {
ensure!(
head_data.0.len() <= self.config.max_head_data_size as _,
AcceptanceCheckErr::HeadDataTooLarge,
);
if let Some(new_validation_code) = new_validation_code {
ensure!(
<paras::Pallet<T>>::can_upgrade_validation_code(para_id),
AcceptanceCheckErr::PrematureCodeUpgrade,
);
ensure!(
new_validation_code.0.len() <= self.config.max_code_size as _,
AcceptanceCheckErr::NewCodeTooLarge,
);
}
<dmp::Pallet<T>>::check_processed_downward_messages(para_id, processed_downward_messages)?;
<ump::Pallet<T>>::check_upward_messages(&self.config, para_id, upward_messages)?;
<hrmp::Pallet<T>>::check_hrmp_watermark(para_id, self.relay_parent_number, hrmp_watermark)?;
<hrmp::Pallet<T>>::check_outbound_hrmp(&self.config, para_id, horizontal_messages)?;
Ok(())
}
}