1use crate::{
23 configuration::{self, HostConfiguration},
24 disputes, dmp, hrmp,
25 paras::{self, UpgradeStrategy},
26 scheduler,
27 shared::{self, AllowedRelayParentsTracker},
28 util::make_persisted_validation_data_with_parent,
29};
30use alloc::{
31 collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque},
32 vec,
33 vec::Vec,
34};
35use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
36use codec::{Decode, DecodeWithMemTracking, Encode};
37use core::fmt;
38use frame_support::{
39 defensive,
40 pallet_prelude::*,
41 traits::{EnqueueMessage, Footprint, QueueFootprint, QueueFootprintQuery},
42 BoundedSlice,
43};
44use frame_system::pallet_prelude::*;
45use pallet_message_queue::OnQueueChanged;
46use polkadot_primitives::{
47 effective_minimum_backing_votes, skip_ump_signals, supermajority_threshold, well_known_keys,
48 BackedCandidate, CandidateCommitments, CandidateDescriptorV2 as CandidateDescriptor,
49 CandidateHash, CandidateReceiptV2 as CandidateReceipt,
50 CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreIndex, GroupIndex, HeadData,
51 Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId,
52 ValidatorIndex, ValidityAttestation,
53};
54use scale_info::TypeInfo;
55use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating};
56
57pub use pallet::*;
58
59#[cfg(test)]
60pub(crate) mod tests;
61
62#[cfg(feature = "runtime-benchmarks")]
63mod benchmarking;
64
65pub mod migration;
66
67pub trait WeightInfo {
68 fn enact_candidate(u: u32, h: u32, c: u32) -> Weight;
74}
75
76pub struct TestWeightInfo;
77impl WeightInfo for TestWeightInfo {
78 fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight {
79 Weight::zero()
80 }
81}
82
83impl WeightInfo for () {
84 fn enact_candidate(_u: u32, _h: u32, _c: u32) -> Weight {
85 Weight::zero()
86 }
87}
88
89pub const MAX_UPWARD_MESSAGE_SIZE_BOUND: u32 = 128 * 1024;
94
95#[derive(Encode, Decode, PartialEq, TypeInfo, Clone)]
97#[cfg_attr(test, derive(Debug))]
98pub struct CandidatePendingAvailability<H, N> {
99 core: CoreIndex,
101 hash: CandidateHash,
103 descriptor: CandidateDescriptor<H>,
105 commitments: CandidateCommitments,
107 availability_votes: BitVec<u8, BitOrderLsb0>,
109 backers: BitVec<u8, BitOrderLsb0>,
111 relay_parent_number: N,
113 backed_in_number: N,
115 backing_group: GroupIndex,
117}
118
119impl<H, N> CandidatePendingAvailability<H, N> {
120 pub(crate) fn availability_votes(&self) -> &BitVec<u8, BitOrderLsb0> {
122 &self.availability_votes
123 }
124
125 pub(crate) fn backed_in_number(&self) -> N
127 where
128 N: Clone,
129 {
130 self.backed_in_number.clone()
131 }
132
133 pub(crate) fn core_occupied(&self) -> CoreIndex {
135 self.core
136 }
137
138 pub(crate) fn candidate_hash(&self) -> CandidateHash {
140 self.hash
141 }
142
143 pub(crate) fn candidate_descriptor(&self) -> &CandidateDescriptor<H> {
145 &self.descriptor
146 }
147
148 pub(crate) fn candidate_commitments(&self) -> &CandidateCommitments {
150 &self.commitments
151 }
152
153 pub(crate) fn relay_parent_number(&self) -> N
155 where
156 N: Clone,
157 {
158 self.relay_parent_number.clone()
159 }
160
161 #[cfg(any(feature = "runtime-benchmarks", test))]
162 pub(crate) fn new(
163 core: CoreIndex,
164 hash: CandidateHash,
165 descriptor: CandidateDescriptor<H>,
166 commitments: CandidateCommitments,
167 availability_votes: BitVec<u8, BitOrderLsb0>,
168 backers: BitVec<u8, BitOrderLsb0>,
169 relay_parent_number: N,
170 backed_in_number: N,
171 backing_group: GroupIndex,
172 ) -> Self {
173 Self {
174 core,
175 hash,
176 descriptor,
177 commitments,
178 availability_votes,
179 backers,
180 relay_parent_number,
181 backed_in_number,
182 backing_group,
183 }
184 }
185}
186
187pub trait RewardValidators {
189 fn reward_backing(validators: impl IntoIterator<Item = ValidatorIndex>);
191 fn reward_bitfields(validators: impl IntoIterator<Item = ValidatorIndex>);
195}
196
197impl RewardValidators for () {
198 fn reward_backing(_: impl IntoIterator<Item = ValidatorIndex>) {}
199 fn reward_bitfields(_: impl IntoIterator<Item = ValidatorIndex>) {}
200}
201
202pub trait QueueFootprinter {
204 type Origin;
205
206 fn message_count(origin: Self::Origin) -> u64;
207}
208
209impl QueueFootprinter for () {
210 type Origin = UmpQueueId;
211
212 fn message_count(_: Self::Origin) -> u64 {
213 0
214 }
215}
216
217#[derive(
222 Encode,
223 Decode,
224 DecodeWithMemTracking,
225 Clone,
226 MaxEncodedLen,
227 Eq,
228 PartialEq,
229 RuntimeDebug,
230 TypeInfo,
231)]
232pub enum AggregateMessageOrigin {
233 #[codec(index = 0)]
235 Ump(UmpQueueId),
236}
237
238#[derive(
243 Encode,
244 Decode,
245 DecodeWithMemTracking,
246 Clone,
247 MaxEncodedLen,
248 Eq,
249 PartialEq,
250 RuntimeDebug,
251 TypeInfo,
252)]
253pub enum UmpQueueId {
254 #[codec(index = 0)]
256 Para(ParaId),
257}
258
259#[cfg(feature = "runtime-benchmarks")]
260impl From<u32> for AggregateMessageOrigin {
261 fn from(n: u32) -> Self {
262 Self::Ump(UmpQueueId::Para(n.into()))
264 }
265}
266
267pub type MaxUmpMessageLenOf<T> =
269 <<T as Config>::MessageQueue as EnqueueMessage<AggregateMessageOrigin>>::MaxMessageLen;
270
271#[frame_support::pallet]
272pub mod pallet {
273 use super::*;
274
275 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
276 #[pallet::pallet]
277 #[pallet::without_storage_info]
278 #[pallet::storage_version(STORAGE_VERSION)]
279 pub struct Pallet<T>(_);
280
281 #[pallet::config]
282 pub trait Config:
283 frame_system::Config
284 + shared::Config
285 + paras::Config
286 + dmp::Config
287 + hrmp::Config
288 + configuration::Config
289 + scheduler::Config
290 {
291 #[allow(deprecated)]
292 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
293 type DisputesHandler: disputes::DisputesHandler<BlockNumberFor<Self>>;
294 type RewardValidators: RewardValidators;
295
296 type MessageQueue: EnqueueMessage<AggregateMessageOrigin>
302 + QueueFootprintQuery<AggregateMessageOrigin, MaxMessageLen = MaxUmpMessageLenOf<Self>>;
303
304 type WeightInfo: WeightInfo;
306 }
307
308 #[pallet::event]
309 #[pallet::generate_deposit(pub(super) fn deposit_event)]
310 pub enum Event<T: Config> {
311 CandidateBacked(CandidateReceipt<T::Hash>, HeadData, CoreIndex, GroupIndex),
313 CandidateIncluded(CandidateReceipt<T::Hash>, HeadData, CoreIndex, GroupIndex),
315 CandidateTimedOut(CandidateReceipt<T::Hash>, HeadData, CoreIndex),
317 UpwardMessagesReceived { from: ParaId, count: u32 },
319 }
320
321 #[pallet::error]
322 pub enum Error<T> {
323 ValidatorIndexOutOfBounds,
325 UnscheduledCandidate,
327 HeadDataTooLarge,
329 PrematureCodeUpgrade,
331 NewCodeTooLarge,
333 DisallowedRelayParent,
336 InvalidAssignment,
339 InvalidGroupIndex,
341 InsufficientBacking,
343 InvalidBacking,
345 ValidationDataHashMismatch,
347 IncorrectDownwardMessageHandling,
349 InvalidUpwardMessages,
351 HrmpWatermarkMishandling,
353 InvalidOutboundHrmp,
355 InvalidValidationCodeHash,
357 ParaHeadMismatch,
360 }
361
362 #[pallet::storage]
368 #[pallet::storage_prefix = "V1"]
369 pub(crate) type PendingAvailability<T: Config> = StorageMap<
370 _,
371 Twox64Concat,
372 ParaId,
373 VecDeque<CandidatePendingAvailability<T::Hash, BlockNumberFor<T>>>,
374 >;
375
376 #[pallet::call]
377 impl<T: Config> Pallet<T> {}
378}
379
380const LOG_TARGET: &str = "runtime::inclusion";
381
382#[derive(Debug)]
384enum AcceptanceCheckErr {
385 HeadDataTooLarge,
386 PrematureCodeUpgrade,
388 NewCodeTooLarge,
390 ProcessedDownwardMessages,
392 UpwardMessages,
394 HrmpWatermark,
396 OutboundHrmp,
398}
399
400impl From<dmp::ProcessedDownwardMessagesAcceptanceErr> for AcceptanceCheckErr {
401 fn from(_: dmp::ProcessedDownwardMessagesAcceptanceErr) -> Self {
402 Self::ProcessedDownwardMessages
403 }
404}
405
406impl From<UmpAcceptanceCheckErr> for AcceptanceCheckErr {
407 fn from(_: UmpAcceptanceCheckErr) -> Self {
408 Self::UpwardMessages
409 }
410}
411
412impl<BlockNumber> From<hrmp::HrmpWatermarkAcceptanceErr<BlockNumber>> for AcceptanceCheckErr {
413 fn from(_: hrmp::HrmpWatermarkAcceptanceErr<BlockNumber>) -> Self {
414 Self::HrmpWatermark
415 }
416}
417
418impl From<hrmp::OutboundHrmpAcceptanceErr> for AcceptanceCheckErr {
419 fn from(_: hrmp::OutboundHrmpAcceptanceErr) -> Self {
420 Self::OutboundHrmp
421 }
422}
423
424#[cfg_attr(test, derive(PartialEq))]
427#[allow(dead_code)]
428pub(crate) enum UmpAcceptanceCheckErr {
429 MoreMessagesThanPermitted { sent: u32, permitted: u32 },
431 MessageSize { idx: u32, msg_size: u32, max_size: u32 },
433 CapacityExceeded { count: u64, limit: u64 },
435 TotalSizeExceeded { total_size: u64, limit: u64 },
437 IsOffboarding,
439}
440
441impl fmt::Debug for UmpAcceptanceCheckErr {
442 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
443 match *self {
444 UmpAcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted } => write!(
445 fmt,
446 "more upward messages than permitted by config ({} > {})",
447 sent, permitted,
448 ),
449 UmpAcceptanceCheckErr::MessageSize { idx, msg_size, max_size } => write!(
450 fmt,
451 "upward message idx {} larger than permitted by config ({} > {})",
452 idx, msg_size, max_size,
453 ),
454 UmpAcceptanceCheckErr::CapacityExceeded { count, limit } => write!(
455 fmt,
456 "the ump queue would have more items than permitted by config ({} > {})",
457 count, limit,
458 ),
459 UmpAcceptanceCheckErr::TotalSizeExceeded { total_size, limit } => write!(
460 fmt,
461 "the ump queue would have grown past the max size permitted by config ({} > {})",
462 total_size, limit,
463 ),
464 UmpAcceptanceCheckErr::IsOffboarding => {
465 write!(fmt, "upward message rejected because the para is off-boarding")
466 },
467 }
468 }
469}
470
471impl<T: Config> Pallet<T> {
472 pub(crate) fn initializer_initialize(_now: BlockNumberFor<T>) -> Weight {
474 Weight::zero()
475 }
476
477 pub(crate) fn initializer_finalize() {}
479
480 pub(crate) fn initializer_on_new_session(
482 _notification: &crate::initializer::SessionChangeNotification<BlockNumberFor<T>>,
483 outgoing_paras: &[ParaId],
484 ) {
485 for _ in PendingAvailability::<T>::drain() {}
488
489 Self::cleanup_outgoing_ump_dispatch_queues(outgoing_paras);
490 }
491
492 pub(crate) fn cleanup_outgoing_ump_dispatch_queues(outgoing: &[ParaId]) {
493 for outgoing_para in outgoing {
494 Self::cleanup_outgoing_ump_dispatch_queue(*outgoing_para);
495 }
496 }
497
498 pub(crate) fn cleanup_outgoing_ump_dispatch_queue(para: ParaId) {
499 T::MessageQueue::sweep_queue(AggregateMessageOrigin::Ump(UmpQueueId::Para(para)));
500 }
501
502 pub(crate) fn get_occupied_cores(
503 ) -> impl Iterator<Item = (CoreIndex, CandidatePendingAvailability<T::Hash, BlockNumberFor<T>>)>
504 {
505 PendingAvailability::<T>::iter_values().flat_map(|pending_candidates| {
506 pending_candidates.into_iter().map(|c| (c.core, c.clone()))
507 })
508 }
509
510 pub(crate) fn update_pending_availability_and_get_freed_cores(
519 validators: &[ValidatorId],
520 signed_bitfields: SignedAvailabilityBitfields,
521 ) -> (Weight, Vec<(CoreIndex, CandidateHash)>) {
522 let threshold = availability_threshold(validators.len());
523
524 let mut votes_per_core: BTreeMap<CoreIndex, BTreeSet<ValidatorIndex>> = BTreeMap::new();
525
526 for (checked_bitfield, validator_index) in
527 signed_bitfields.into_iter().map(|signed_bitfield| {
528 let validator_idx = signed_bitfield.validator_index();
529 let checked_bitfield = signed_bitfield.into_payload();
530 (checked_bitfield, validator_idx)
531 }) {
532 for (bit_idx, _) in checked_bitfield.0.iter().enumerate().filter(|(_, is_av)| **is_av) {
533 let core_index = CoreIndex(bit_idx as u32);
534 votes_per_core
535 .entry(core_index)
536 .or_insert_with(|| BTreeSet::new())
537 .insert(validator_index);
538 }
539 }
540
541 let mut freed_cores = vec![];
542 let mut weight = Weight::zero();
543
544 let pending_paraids: Vec<_> = PendingAvailability::<T>::iter_keys().collect();
545 for paraid in pending_paraids {
546 PendingAvailability::<T>::mutate(paraid, |candidates| {
547 if let Some(candidates) = candidates {
548 let mut last_enacted_index: Option<usize> = None;
549
550 for (candidate_index, candidate) in candidates.iter_mut().enumerate() {
551 if let Some(validator_indices) = votes_per_core.remove(&candidate.core) {
552 for validator_index in validator_indices.iter() {
553 if let Some(mut bit) =
557 candidate.availability_votes.get_mut(validator_index.0 as usize)
558 {
559 *bit = true;
560 }
561 }
562 }
563
564 if candidate.availability_votes.count_ones() >= threshold {
569 let can_enact = if candidate_index == 0 {
572 last_enacted_index == None
573 } else {
574 let prev_candidate_index = usize::try_from(candidate_index - 1)
575 .expect("Previous `if` would have caught a 0 candidate index.");
576 matches!(last_enacted_index, Some(old_index) if old_index == prev_candidate_index)
577 };
578
579 if can_enact {
580 last_enacted_index = Some(candidate_index);
581 }
582 }
583 }
584
585 if let Some(last_enacted_index) = last_enacted_index {
588 let evicted_candidates = candidates.drain(0..=last_enacted_index);
589 for candidate in evicted_candidates {
590 freed_cores.push((candidate.core, candidate.hash));
591
592 let receipt = CommittedCandidateReceipt {
593 descriptor: candidate.descriptor,
594 commitments: candidate.commitments,
595 };
596
597 let has_runtime_upgrade =
598 receipt.commitments.new_validation_code.as_ref().map_or(0, |_| 1);
599 let u = receipt.commitments.upward_messages.len() as u32;
600 let h = receipt.commitments.horizontal_messages.len() as u32;
601 let enact_weight = <T as Config>::WeightInfo::enact_candidate(
602 u,
603 h,
604 has_runtime_upgrade,
605 );
606 Self::enact_candidate(
607 candidate.relay_parent_number,
608 receipt,
609 candidate.backers,
610 candidate.availability_votes,
611 candidate.core,
612 candidate.backing_group,
613 );
614 weight.saturating_accrue(enact_weight);
615 }
616 }
617 }
618 });
619 }
620 (weight.set_proof_size(0), freed_cores)
626 }
627
628 pub(crate) fn process_candidates<GV>(
636 allowed_relay_parents: &AllowedRelayParentsTracker<T::Hash, BlockNumberFor<T>>,
637 candidates: &BTreeMap<ParaId, Vec<(BackedCandidate<T::Hash>, CoreIndex)>>,
638 group_validators: GV,
639 ) -> Result<
640 Vec<(CandidateReceipt<T::Hash>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
641 DispatchError,
642 >
643 where
644 GV: Fn(GroupIndex) -> Option<Vec<ValidatorIndex>>,
645 {
646 if candidates.is_empty() {
647 return Ok(Default::default())
648 }
649
650 let now = frame_system::Pallet::<T>::block_number();
651 let validators = shared::ActiveValidatorKeys::<T>::get();
652
653 let mut candidate_receipt_with_backing_validator_indices =
655 Vec::with_capacity(candidates.len());
656
657 for (para_id, para_candidates) in candidates {
658 let mut latest_head_data = match Self::para_latest_head_data(para_id) {
659 None => {
660 defensive!("Latest included head data for paraid {:?} is None", para_id);
661 continue
662 },
663 Some(latest_head_data) => latest_head_data,
664 };
665
666 for (candidate, core) in para_candidates.iter() {
667 let candidate_hash = candidate.candidate().hash();
668
669 let check_ctx = CandidateCheckContext::<T>::new(None);
672 let relay_parent_number = check_ctx.verify_backed_candidate(
673 &allowed_relay_parents,
674 candidate.candidate(),
675 latest_head_data.clone(),
676 )?;
677
678 let group_idx = scheduler::Pallet::<T>::group_assigned_to_core(
683 *core,
684 relay_parent_number + One::one(),
685 )
686 .ok_or_else(|| {
687 log::warn!(
688 target: LOG_TARGET,
689 "Failed to compute group index for candidate {:?}",
690 candidate_hash
691 );
692 Error::<T>::InvalidAssignment
693 })?;
694 let group_vals =
695 group_validators(group_idx).ok_or_else(|| Error::<T>::InvalidGroupIndex)?;
696
697 let (backers, backer_idx_and_attestation) =
699 Self::check_backing_votes(candidate, &validators, group_vals)?;
700
701 latest_head_data = candidate.candidate().commitments.head_data.clone();
703 candidate_receipt_with_backing_validator_indices
704 .push((candidate.receipt(), backer_idx_and_attestation));
705
706 PendingAvailability::<T>::mutate(¶_id, |pending_availability| {
708 let new_candidate = CandidatePendingAvailability {
709 core: *core,
710 hash: candidate_hash,
711 descriptor: candidate.candidate().descriptor.clone(),
712 commitments: candidate.candidate().commitments.clone(),
713 availability_votes: bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()],
715 relay_parent_number,
716 backers: backers.to_bitvec(),
717 backed_in_number: now,
718 backing_group: group_idx,
719 };
720
721 if let Some(pending_availability) = pending_availability {
722 pending_availability.push_back(new_candidate);
723 } else {
724 *pending_availability =
725 Some([new_candidate].into_iter().collect::<VecDeque<_>>())
726 }
727 });
728
729 Self::deposit_event(Event::<T>::CandidateBacked(
731 candidate.candidate().to_plain(),
732 candidate.candidate().commitments.head_data.clone(),
733 *core,
734 group_idx,
735 ));
736 }
737 }
738
739 Ok(candidate_receipt_with_backing_validator_indices)
740 }
741
742 pub(crate) fn para_latest_head_data(para_id: &ParaId) -> Option<HeadData> {
744 match PendingAvailability::<T>::get(para_id).and_then(|pending_candidates| {
745 pending_candidates.back().map(|x| x.commitments.head_data.clone())
746 }) {
747 Some(head_data) => Some(head_data),
748 None => paras::Heads::<T>::get(para_id),
749 }
750 }
751
752 pub(crate) fn para_most_recent_context(para_id: &ParaId) -> Option<BlockNumberFor<T>> {
754 match PendingAvailability::<T>::get(para_id)
755 .and_then(|pending_candidates| pending_candidates.back().map(|x| x.relay_parent_number))
756 {
757 Some(relay_parent_number) => Some(relay_parent_number),
758 None => paras::MostRecentContext::<T>::get(para_id),
759 }
760 }
761
762 fn check_backing_votes(
763 backed_candidate: &BackedCandidate<T::Hash>,
764 validators: &[ValidatorId],
765 group_vals: Vec<ValidatorIndex>,
766 ) -> Result<(BitVec<u8, BitOrderLsb0>, Vec<(ValidatorIndex, ValidityAttestation)>), Error<T>> {
767 let minimum_backing_votes = configuration::ActiveConfig::<T>::get().minimum_backing_votes;
768
769 let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()];
770 let signing_context = SigningContext {
771 parent_hash: backed_candidate.descriptor().relay_parent(),
772 session_index: shared::CurrentSessionIndex::<T>::get(),
773 };
774
775 let (validator_indices, _) = backed_candidate.validator_indices_and_core_index();
776
777 let maybe_amount_validated = polkadot_primitives::check_candidate_backing(
779 backed_candidate.candidate().hash(),
780 backed_candidate.validity_votes(),
781 validator_indices,
782 &signing_context,
783 group_vals.len(),
784 |intra_group_vi| {
785 group_vals
786 .get(intra_group_vi)
787 .and_then(|vi| validators.get(vi.0 as usize))
788 .map(|v| v.clone())
789 },
790 );
791
792 match maybe_amount_validated {
793 Ok(amount_validated) => ensure!(
794 amount_validated >=
795 effective_minimum_backing_votes(group_vals.len(), minimum_backing_votes),
796 Error::<T>::InsufficientBacking,
797 ),
798 Err(()) => {
799 Err(Error::<T>::InvalidBacking)?;
800 },
801 }
802
803 let mut backer_idx_and_attestation =
804 Vec::<(ValidatorIndex, ValidityAttestation)>::with_capacity(
805 validator_indices.count_ones(),
806 );
807
808 for ((bit_idx, _), attestation) in validator_indices
809 .iter()
810 .enumerate()
811 .filter(|(_, signed)| **signed)
812 .zip(backed_candidate.validity_votes().iter().cloned())
813 {
814 let val_idx = group_vals.get(bit_idx).expect("this query succeeded above; qed");
815 backer_idx_and_attestation.push((*val_idx, attestation));
816
817 backers.set(val_idx.0 as _, true);
818 }
819
820 Ok((backers, backer_idx_and_attestation))
821 }
822
823 pub(crate) fn check_validation_outputs_for_runtime_api(
825 para_id: ParaId,
826 relay_parent_number: BlockNumberFor<T>,
827 validation_outputs: polkadot_primitives::CandidateCommitments,
828 ) -> bool {
829 let prev_context = Self::para_most_recent_context(¶_id);
830 let check_ctx = CandidateCheckContext::<T>::new(prev_context);
831
832 if let Err(err) = check_ctx.check_validation_outputs(
833 para_id,
834 relay_parent_number,
835 &validation_outputs.head_data,
836 &validation_outputs.new_validation_code,
837 validation_outputs.processed_downward_messages,
838 &validation_outputs.upward_messages,
839 BlockNumberFor::<T>::from(validation_outputs.hrmp_watermark),
840 &validation_outputs.horizontal_messages,
841 ) {
842 log::debug!(
843 target: LOG_TARGET,
844 "Validation outputs checking for parachain `{}` failed, error: {:?}",
845 u32::from(para_id), err
846 );
847 false
848 } else {
849 true
850 }
851 }
852
853 fn enact_candidate(
854 relay_parent_number: BlockNumberFor<T>,
855 receipt: CommittedCandidateReceipt<T::Hash>,
856 backers: BitVec<u8, BitOrderLsb0>,
857 availability_votes: BitVec<u8, BitOrderLsb0>,
858 core_index: CoreIndex,
859 backing_group: GroupIndex,
860 ) {
861 let plain = receipt.to_plain();
862 let commitments = receipt.commitments;
863 let config = configuration::ActiveConfig::<T>::get();
864
865 T::RewardValidators::reward_backing(
866 backers
867 .iter()
868 .enumerate()
869 .filter(|(_, backed)| **backed)
870 .map(|(i, _)| ValidatorIndex(i as _)),
871 );
872
873 T::RewardValidators::reward_bitfields(
874 availability_votes
875 .iter()
876 .enumerate()
877 .filter(|(_, voted)| **voted)
878 .map(|(i, _)| ValidatorIndex(i as _)),
879 );
880
881 if let Some(new_code) = commitments.new_validation_code {
882 let now = frame_system::Pallet::<T>::block_number();
884
885 paras::Pallet::<T>::schedule_code_upgrade(
886 receipt.descriptor.para_id(),
887 new_code,
888 now,
889 &config,
890 UpgradeStrategy::SetGoAheadSignal,
891 );
892 }
893
894 dmp::Pallet::<T>::prune_dmq(
896 receipt.descriptor.para_id(),
897 commitments.processed_downward_messages,
898 );
899 Self::receive_upward_messages(
900 receipt.descriptor.para_id(),
901 commitments.upward_messages.as_slice(),
902 );
903 hrmp::Pallet::<T>::prune_hrmp(
904 receipt.descriptor.para_id(),
905 BlockNumberFor::<T>::from(commitments.hrmp_watermark),
906 );
907 hrmp::Pallet::<T>::queue_outbound_hrmp(
908 receipt.descriptor.para_id(),
909 commitments.horizontal_messages,
910 );
911
912 Self::deposit_event(Event::<T>::CandidateIncluded(
913 plain,
914 commitments.head_data.clone(),
915 core_index,
916 backing_group,
917 ));
918
919 paras::Pallet::<T>::note_new_head(
920 receipt.descriptor.para_id(),
921 commitments.head_data,
922 relay_parent_number,
923 );
924 }
925
926 pub(crate) fn relay_dispatch_queue_size(para_id: ParaId) -> (u32, u32) {
927 let fp = T::MessageQueue::footprint(AggregateMessageOrigin::Ump(UmpQueueId::Para(para_id)));
928 (fp.storage.count as u32, fp.storage.size as u32)
929 }
930
931 pub(crate) fn check_upward_messages(
933 config: &HostConfiguration<BlockNumberFor<T>>,
934 para: ParaId,
935 upward_messages: &[UpwardMessage],
936 ) -> Result<(), UmpAcceptanceCheckErr> {
937 let upward_messages = skip_ump_signals(upward_messages.iter()).collect::<Vec<_>>();
939
940 if paras::Pallet::<T>::is_offboarding(para) {
942 ensure!(upward_messages.is_empty(), UmpAcceptanceCheckErr::IsOffboarding);
943 }
944
945 let additional_msgs = upward_messages.len() as u32;
946 if additional_msgs > config.max_upward_message_num_per_candidate {
947 return Err(UmpAcceptanceCheckErr::MoreMessagesThanPermitted {
948 sent: additional_msgs,
949 permitted: config.max_upward_message_num_per_candidate,
950 })
951 }
952
953 let (para_queue_count, mut para_queue_size) = Self::relay_dispatch_queue_size(para);
954
955 if para_queue_count.saturating_add(additional_msgs) > config.max_upward_queue_count {
956 return Err(UmpAcceptanceCheckErr::CapacityExceeded {
957 count: para_queue_count.saturating_add(additional_msgs).into(),
958 limit: config.max_upward_queue_count.into(),
959 })
960 }
961
962 for (idx, msg) in upward_messages.into_iter().enumerate() {
963 let msg_size = msg.len() as u32;
964 if msg_size > config.max_upward_message_size {
965 return Err(UmpAcceptanceCheckErr::MessageSize {
966 idx: idx as u32,
967 msg_size,
968 max_size: config.max_upward_message_size,
969 })
970 }
971 if para_queue_size.saturating_add(msg_size) > config.max_upward_queue_size {
975 return Err(UmpAcceptanceCheckErr::TotalSizeExceeded {
976 total_size: para_queue_size.saturating_add(msg_size).into(),
977 limit: config.max_upward_queue_size.into(),
978 })
979 }
980 para_queue_size.saturating_accrue(msg_size);
981 }
982
983 Ok(())
984 }
985
986 pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec<u8>]) {
992 let bounded = skip_ump_signals(upward_messages.iter())
993 .filter_map(|d| {
994 BoundedSlice::try_from(&d[..])
995 .inspect_err(|_| {
996 defensive!("Accepted candidate contains too long msg, len=", d.len());
997 })
998 .ok()
999 })
1000 .collect();
1001 Self::receive_bounded_upward_messages(para, bounded)
1002 }
1003
1004 pub(crate) fn receive_bounded_upward_messages(
1006 para: ParaId,
1007 messages: Vec<BoundedSlice<'_, u8, MaxUmpMessageLenOf<T>>>,
1008 ) {
1009 let count = messages.len() as u32;
1010 if count == 0 {
1011 return
1012 }
1013
1014 T::MessageQueue::enqueue_messages(
1015 messages.into_iter(),
1016 AggregateMessageOrigin::Ump(UmpQueueId::Para(para)),
1017 );
1018 Self::deposit_event(Event::UpwardMessagesReceived { from: para, count });
1019 }
1020
1021 pub(crate) fn free_timedout() -> Vec<CoreIndex> {
1025 let timeout_pred = scheduler::Pallet::<T>::availability_timeout_predicate();
1026
1027 let timed_out: Vec<_> = Self::free_failed_cores(
1028 |candidate| timeout_pred(candidate.backed_in_number).timed_out,
1029 None,
1030 )
1031 .collect();
1032
1033 let mut timed_out_cores = Vec::with_capacity(timed_out.len());
1034 for candidate in timed_out.iter() {
1035 timed_out_cores.push(candidate.core);
1036
1037 let receipt = CandidateReceipt {
1038 descriptor: candidate.descriptor.clone(),
1039 commitments_hash: candidate.commitments.hash(),
1040 };
1041
1042 Self::deposit_event(Event::<T>::CandidateTimedOut(
1043 receipt,
1044 candidate.commitments.head_data.clone(),
1045 candidate.core,
1046 ));
1047 }
1048
1049 timed_out_cores
1050 }
1051
1052 pub(crate) fn free_disputed(
1057 disputed: &BTreeSet<CandidateHash>,
1058 ) -> Vec<(CoreIndex, CandidateHash)> {
1059 Self::free_failed_cores(
1060 |candidate| disputed.contains(&candidate.hash),
1061 Some(disputed.len()),
1062 )
1063 .map(|candidate| (candidate.core, candidate.hash))
1064 .collect()
1065 }
1066
1067 fn free_failed_cores<
1071 P: Fn(&CandidatePendingAvailability<T::Hash, BlockNumberFor<T>>) -> bool,
1072 >(
1073 pred: P,
1074 capacity_hint: Option<usize>,
1075 ) -> impl Iterator<Item = CandidatePendingAvailability<T::Hash, BlockNumberFor<T>>> {
1076 let mut earliest_dropped_indices: BTreeMap<ParaId, usize> = BTreeMap::new();
1077
1078 for (para_id, pending_candidates) in PendingAvailability::<T>::iter() {
1079 let mut earliest_dropped_idx = None;
1082 for (index, candidate) in pending_candidates.iter().enumerate() {
1083 if pred(candidate) {
1084 earliest_dropped_idx = Some(index);
1085 break;
1088 }
1089 }
1090
1091 if let Some(earliest_dropped_idx) = earliest_dropped_idx {
1092 earliest_dropped_indices.insert(para_id, earliest_dropped_idx);
1093 }
1094 }
1095
1096 let mut cleaned_up_cores =
1097 if let Some(capacity) = capacity_hint { Vec::with_capacity(capacity) } else { vec![] };
1098
1099 for (para_id, earliest_dropped_idx) in earliest_dropped_indices {
1100 PendingAvailability::<T>::mutate(¶_id, |record| {
1102 if let Some(record) = record {
1103 let cleaned_up = record.drain(earliest_dropped_idx..);
1104 cleaned_up_cores.extend(cleaned_up);
1105 }
1106 });
1107 }
1108
1109 cleaned_up_cores.into_iter()
1110 }
1111
1112 pub(crate) fn force_enact(para: ParaId) {
1120 PendingAvailability::<T>::mutate(¶, |candidates| {
1121 if let Some(candidates) = candidates {
1122 for candidate in candidates.drain(..) {
1123 let receipt = CommittedCandidateReceipt {
1124 descriptor: candidate.descriptor,
1125 commitments: candidate.commitments,
1126 };
1127
1128 Self::enact_candidate(
1129 candidate.relay_parent_number,
1130 receipt,
1131 candidate.backers,
1132 candidate.availability_votes,
1133 candidate.core,
1134 candidate.backing_group,
1135 );
1136 }
1137 }
1138 });
1139 }
1140
1141 pub(crate) fn first_candidate_pending_availability(
1146 para: ParaId,
1147 ) -> Option<CommittedCandidateReceipt<T::Hash>> {
1148 PendingAvailability::<T>::get(¶).and_then(|p| {
1149 p.get(0).map(|p| CommittedCandidateReceipt {
1150 descriptor: p.descriptor.clone(),
1151 commitments: p.commitments.clone(),
1152 })
1153 })
1154 }
1155
1156 pub(crate) fn candidates_pending_availability(
1159 para: ParaId,
1160 ) -> Vec<CommittedCandidateReceipt<T::Hash>> {
1161 <PendingAvailability<T>>::get(¶)
1162 .map(|candidates| {
1163 candidates
1164 .into_iter()
1165 .map(|candidate| CommittedCandidateReceipt {
1166 descriptor: candidate.descriptor.clone(),
1167 commitments: candidate.commitments.clone(),
1168 })
1169 .collect()
1170 })
1171 .unwrap_or_default()
1172 }
1173}
1174
1175const fn availability_threshold(n_validators: usize) -> usize {
1176 supermajority_threshold(n_validators)
1177}
1178
1179impl AcceptanceCheckErr {
1180 fn strip_into_dispatch_err<T: Config>(self) -> Error<T> {
1183 use AcceptanceCheckErr::*;
1184 match self {
1185 HeadDataTooLarge => Error::<T>::HeadDataTooLarge,
1186 PrematureCodeUpgrade => Error::<T>::PrematureCodeUpgrade,
1187 NewCodeTooLarge => Error::<T>::NewCodeTooLarge,
1188 ProcessedDownwardMessages => Error::<T>::IncorrectDownwardMessageHandling,
1189 UpwardMessages => Error::<T>::InvalidUpwardMessages,
1190 HrmpWatermark => Error::<T>::HrmpWatermarkMishandling,
1191 OutboundHrmp => Error::<T>::InvalidOutboundHrmp,
1192 }
1193 }
1194}
1195
1196impl<T: Config> OnQueueChanged<AggregateMessageOrigin> for Pallet<T> {
1197 fn on_queue_changed(origin: AggregateMessageOrigin, fp: QueueFootprint) {
1199 let para = match origin {
1200 AggregateMessageOrigin::Ump(UmpQueueId::Para(p)) => p,
1201 };
1202 let QueueFootprint { storage: Footprint { count, size }, .. } = fp;
1203 let (count, size) = (count.saturated_into(), size.saturated_into());
1204 #[allow(deprecated)]
1206 well_known_keys::relay_dispatch_queue_size_typed(para).set((count, size));
1207
1208 let config = configuration::ActiveConfig::<T>::get();
1209 let remaining_count = config.max_upward_queue_count.saturating_sub(count);
1210 let remaining_size = config.max_upward_queue_size.saturating_sub(size);
1211 well_known_keys::relay_dispatch_queue_remaining_capacity(para)
1212 .set((remaining_count, remaining_size));
1213 }
1214}
1215
1216pub(crate) struct CandidateCheckContext<T: Config> {
1218 config: configuration::HostConfiguration<BlockNumberFor<T>>,
1219 prev_context: Option<BlockNumberFor<T>>,
1220}
1221
1222impl<T: Config> CandidateCheckContext<T> {
1223 pub(crate) fn new(prev_context: Option<BlockNumberFor<T>>) -> Self {
1224 Self { config: configuration::ActiveConfig::<T>::get(), prev_context }
1225 }
1226
1227 pub(crate) fn verify_backed_candidate(
1236 &self,
1237 allowed_relay_parents: &AllowedRelayParentsTracker<T::Hash, BlockNumberFor<T>>,
1238 backed_candidate_receipt: &CommittedCandidateReceipt<<T as frame_system::Config>::Hash>,
1239 parent_head_data: HeadData,
1240 ) -> Result<BlockNumberFor<T>, Error<T>> {
1241 let para_id = backed_candidate_receipt.descriptor.para_id();
1242 let relay_parent = backed_candidate_receipt.descriptor.relay_parent();
1243
1244 let (state_root, relay_parent_number) = {
1246 match allowed_relay_parents.acquire_info(relay_parent, self.prev_context) {
1247 None => return Err(Error::<T>::DisallowedRelayParent),
1248 Some((info, relay_parent_number)) => (info.state_root, relay_parent_number),
1249 }
1250 };
1251
1252 {
1253 let persisted_validation_data = make_persisted_validation_data_with_parent::<T>(
1254 relay_parent_number,
1255 state_root,
1256 parent_head_data,
1257 );
1258
1259 let expected = persisted_validation_data.hash();
1260
1261 ensure!(
1262 expected == backed_candidate_receipt.descriptor.persisted_validation_data_hash(),
1263 Error::<T>::ValidationDataHashMismatch,
1264 );
1265 }
1266
1267 let validation_code_hash = paras::CurrentCodeHash::<T>::get(para_id)
1268 .ok_or_else(|| Error::<T>::UnscheduledCandidate)?;
1270 ensure!(
1271 backed_candidate_receipt.descriptor.validation_code_hash() == validation_code_hash,
1272 Error::<T>::InvalidValidationCodeHash,
1273 );
1274
1275 ensure!(
1276 backed_candidate_receipt.descriptor.para_head() ==
1277 backed_candidate_receipt.commitments.head_data.hash(),
1278 Error::<T>::ParaHeadMismatch,
1279 );
1280
1281 if let Err(err) = self.check_validation_outputs(
1282 para_id,
1283 relay_parent_number,
1284 &backed_candidate_receipt.commitments.head_data,
1285 &backed_candidate_receipt.commitments.new_validation_code,
1286 backed_candidate_receipt.commitments.processed_downward_messages,
1287 &backed_candidate_receipt.commitments.upward_messages,
1288 BlockNumberFor::<T>::from(backed_candidate_receipt.commitments.hrmp_watermark),
1289 &backed_candidate_receipt.commitments.horizontal_messages,
1290 ) {
1291 log::debug!(
1292 target: LOG_TARGET,
1293 "Validation outputs checking during inclusion of a candidate {:?} for parachain `{}` failed, error: {:?}",
1294 backed_candidate_receipt.hash(),
1295 u32::from(para_id),
1296 err
1297 );
1298 Err(err.strip_into_dispatch_err::<T>())?;
1299 };
1300 Ok(relay_parent_number)
1301 }
1302
1303 fn check_validation_outputs(
1320 &self,
1321 para_id: ParaId,
1322 relay_parent_number: BlockNumberFor<T>,
1323 head_data: &HeadData,
1324 new_validation_code: &Option<polkadot_primitives::ValidationCode>,
1325 processed_downward_messages: u32,
1326 upward_messages: &[polkadot_primitives::UpwardMessage],
1327 hrmp_watermark: BlockNumberFor<T>,
1328 horizontal_messages: &[polkadot_primitives::OutboundHrmpMessage<ParaId>],
1329 ) -> Result<(), AcceptanceCheckErr> {
1330 let max_head_data_size = usize::try_from(self.config.max_head_data_size)
1332 .map_err(|_| AcceptanceCheckErr::HeadDataTooLarge)?;
1333 ensure!(head_data.0.len() <= max_head_data_size, AcceptanceCheckErr::HeadDataTooLarge);
1334
1335 if let Some(new_validation_code) = new_validation_code {
1337 let max_code_size = usize::try_from(self.config.max_code_size)
1339 .map_err(|_| AcceptanceCheckErr::NewCodeTooLarge)?;
1340
1341 ensure!(
1342 paras::Pallet::<T>::can_upgrade_validation_code(para_id),
1343 AcceptanceCheckErr::PrematureCodeUpgrade,
1344 );
1345 ensure!(
1346 new_validation_code.0.len() <= max_code_size,
1347 AcceptanceCheckErr::NewCodeTooLarge,
1348 );
1349 }
1350
1351 dmp::Pallet::<T>::check_processed_downward_messages(
1353 para_id,
1354 relay_parent_number,
1355 processed_downward_messages,
1356 )
1357 .map_err(|e| {
1358 log::debug!(
1359 target: LOG_TARGET,
1360 "Check processed downward messages for parachain `{}` on relay parent number `{:?}` failed, error: {:?}",
1361 u32::from(para_id),
1362 relay_parent_number,
1363 e
1364 );
1365 e
1366 })?;
1367 Pallet::<T>::check_upward_messages(&self.config, para_id, upward_messages).map_err(
1368 |e| {
1369 log::debug!(
1370 target: LOG_TARGET,
1371 "Check upward messages for parachain `{}` failed, error: {:?}",
1372 u32::from(para_id),
1373 e
1374 );
1375 e
1376 },
1377 )?;
1378 hrmp::Pallet::<T>::check_hrmp_watermark(para_id, relay_parent_number, hrmp_watermark)
1379 .map_err(|e| {
1380 log::debug!(
1381 target: LOG_TARGET,
1382 "Check hrmp watermark for parachain `{}` on relay parent number `{:?}` failed, error: {:?}",
1383 u32::from(para_id),
1384 relay_parent_number,
1385 e
1386 );
1387 e
1388 })?;
1389 hrmp::Pallet::<T>::check_outbound_hrmp(&self.config, para_id, horizontal_messages)
1390 .map_err(|e| {
1391 log::debug!(
1392 target: LOG_TARGET,
1393 "Check outbound hrmp for parachain `{}` failed, error: {:?}",
1394 u32::from(para_id),
1395 e
1396 );
1397 e
1398 })?;
1399
1400 Ok(())
1401 }
1402}
1403
1404impl<T: Config> QueueFootprinter for Pallet<T> {
1405 type Origin = UmpQueueId;
1406
1407 fn message_count(origin: Self::Origin) -> u64 {
1408 T::MessageQueue::footprint(AggregateMessageOrigin::Ump(origin)).storage.count
1409 }
1410}