1use polkadot_primitives::{
84 async_backing::Constraints as PrimitiveConstraints, skip_ump_signals, BlockNumber,
85 CandidateCommitments, Hash, HeadData, Id as ParaId, PersistedValidationData,
86 UpgradeRestriction, ValidationCodeHash,
87};
88use std::{collections::HashMap, sync::Arc};
89
90#[derive(Debug, Clone, PartialEq)]
92pub struct InboundHrmpLimitations {
93 pub valid_watermarks: Vec<BlockNumber>,
95}
96
97#[derive(Debug, Clone, PartialEq)]
99pub struct OutboundHrmpChannelLimitations {
100 pub bytes_remaining: usize,
102 pub messages_remaining: usize,
104}
105
106#[derive(Debug, Clone, PartialEq)]
110pub struct Constraints {
111 pub min_relay_parent_number: BlockNumber,
113 pub max_pov_size: usize,
115 pub max_code_size: usize,
117 pub max_head_data_size: usize,
119 pub ump_remaining: usize,
121 pub ump_remaining_bytes: usize,
123 pub max_ump_num_per_candidate: usize,
125 pub dmp_remaining_messages: Vec<BlockNumber>,
127 pub hrmp_inbound: InboundHrmpLimitations,
129 pub hrmp_channels_out: HashMap<ParaId, OutboundHrmpChannelLimitations>,
131 pub max_hrmp_num_per_candidate: usize,
133 pub required_parent: HeadData,
135 pub validation_code_hash: ValidationCodeHash,
137 pub upgrade_restriction: Option<UpgradeRestriction>,
139 pub future_validation_code: Option<(BlockNumber, ValidationCodeHash)>,
142}
143
144impl From<PrimitiveConstraints> for Constraints {
145 fn from(c: PrimitiveConstraints) -> Self {
146 Constraints {
147 min_relay_parent_number: c.min_relay_parent_number,
148 max_pov_size: c.max_pov_size as _,
149 max_code_size: c.max_code_size as _,
150 max_head_data_size: c.max_head_data_size as _,
151 ump_remaining: c.ump_remaining as _,
152 ump_remaining_bytes: c.ump_remaining_bytes as _,
153 max_ump_num_per_candidate: c.max_ump_num_per_candidate as _,
154 dmp_remaining_messages: c.dmp_remaining_messages,
155 hrmp_inbound: InboundHrmpLimitations {
156 valid_watermarks: c.hrmp_inbound.valid_watermarks,
157 },
158 hrmp_channels_out: c
159 .hrmp_channels_out
160 .into_iter()
161 .map(|(para_id, limits)| {
162 (
163 para_id,
164 OutboundHrmpChannelLimitations {
165 bytes_remaining: limits.bytes_remaining as _,
166 messages_remaining: limits.messages_remaining as _,
167 },
168 )
169 })
170 .collect(),
171 max_hrmp_num_per_candidate: c.max_hrmp_num_per_candidate as _,
172 required_parent: c.required_parent,
173 validation_code_hash: c.validation_code_hash,
174 upgrade_restriction: c.upgrade_restriction,
175 future_validation_code: c.future_validation_code,
176 }
177 }
178}
179
180#[derive(Debug, Clone, PartialEq)]
182pub enum ModificationError {
183 DisallowedHrmpWatermark(BlockNumber),
185 NoSuchHrmpChannel(ParaId),
187 HrmpMessagesOverflow {
189 para_id: ParaId,
191 messages_remaining: usize,
193 messages_submitted: usize,
195 },
196 HrmpBytesOverflow {
198 para_id: ParaId,
200 bytes_remaining: usize,
202 bytes_submitted: usize,
204 },
205 UmpMessagesOverflow {
207 messages_remaining: usize,
209 messages_submitted: usize,
211 },
212 UmpBytesOverflow {
214 bytes_remaining: usize,
216 bytes_submitted: usize,
218 },
219 DmpMessagesUnderflow {
221 messages_remaining: usize,
223 messages_processed: usize,
225 },
226 AppliedNonexistentCodeUpgrade,
228}
229
230impl Constraints {
231 pub fn check_modifications(
233 &self,
234 modifications: &ConstraintModifications,
235 ) -> Result<(), ModificationError> {
236 if let Some(HrmpWatermarkUpdate::Trunk(hrmp_watermark)) = modifications.hrmp_watermark {
237 if !self.hrmp_inbound.valid_watermarks.contains(&hrmp_watermark) {
239 return Err(ModificationError::DisallowedHrmpWatermark(hrmp_watermark));
240 }
241 }
242
243 for (id, outbound_hrmp_mod) in &modifications.outbound_hrmp {
244 if let Some(outbound) = self.hrmp_channels_out.get(&id) {
245 outbound.bytes_remaining.checked_sub(outbound_hrmp_mod.bytes_submitted).ok_or(
246 ModificationError::HrmpBytesOverflow {
247 para_id: *id,
248 bytes_remaining: outbound.bytes_remaining,
249 bytes_submitted: outbound_hrmp_mod.bytes_submitted,
250 },
251 )?;
252
253 outbound
254 .messages_remaining
255 .checked_sub(outbound_hrmp_mod.messages_submitted)
256 .ok_or(ModificationError::HrmpMessagesOverflow {
257 para_id: *id,
258 messages_remaining: outbound.messages_remaining,
259 messages_submitted: outbound_hrmp_mod.messages_submitted,
260 })?;
261 } else {
262 return Err(ModificationError::NoSuchHrmpChannel(*id));
263 }
264 }
265
266 self.ump_remaining.checked_sub(modifications.ump_messages_sent).ok_or(
267 ModificationError::UmpMessagesOverflow {
268 messages_remaining: self.ump_remaining,
269 messages_submitted: modifications.ump_messages_sent,
270 },
271 )?;
272
273 self.ump_remaining_bytes.checked_sub(modifications.ump_bytes_sent).ok_or(
274 ModificationError::UmpBytesOverflow {
275 bytes_remaining: self.ump_remaining_bytes,
276 bytes_submitted: modifications.ump_bytes_sent,
277 },
278 )?;
279
280 self.dmp_remaining_messages
281 .len()
282 .checked_sub(modifications.dmp_messages_processed)
283 .ok_or(ModificationError::DmpMessagesUnderflow {
284 messages_remaining: self.dmp_remaining_messages.len(),
285 messages_processed: modifications.dmp_messages_processed,
286 })?;
287
288 if self.future_validation_code.is_none() && modifications.code_upgrade_applied {
289 return Err(ModificationError::AppliedNonexistentCodeUpgrade);
290 }
291
292 Ok(())
293 }
294
295 pub fn apply_modifications(
298 &self,
299 modifications: &ConstraintModifications,
300 ) -> Result<Self, ModificationError> {
301 let mut new = self.clone();
302
303 if let Some(required_parent) = modifications.required_parent.as_ref() {
304 new.required_parent = required_parent.clone();
305 }
306
307 if let Some(ref hrmp_watermark) = modifications.hrmp_watermark {
308 match new.hrmp_inbound.valid_watermarks.binary_search(&hrmp_watermark.watermark()) {
309 Ok(pos) => {
310 let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
312 },
313 Err(pos) => match hrmp_watermark {
314 HrmpWatermarkUpdate::Head(_) => {
315 let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
317 },
318 HrmpWatermarkUpdate::Trunk(n) => {
319 return Err(ModificationError::DisallowedHrmpWatermark(*n));
321 },
322 },
323 }
324 }
325
326 for (id, outbound_hrmp_mod) in &modifications.outbound_hrmp {
327 if let Some(outbound) = new.hrmp_channels_out.get_mut(&id) {
328 outbound.bytes_remaining = outbound
329 .bytes_remaining
330 .checked_sub(outbound_hrmp_mod.bytes_submitted)
331 .ok_or(ModificationError::HrmpBytesOverflow {
332 para_id: *id,
333 bytes_remaining: outbound.bytes_remaining,
334 bytes_submitted: outbound_hrmp_mod.bytes_submitted,
335 })?;
336
337 outbound.messages_remaining = outbound
338 .messages_remaining
339 .checked_sub(outbound_hrmp_mod.messages_submitted)
340 .ok_or(ModificationError::HrmpMessagesOverflow {
341 para_id: *id,
342 messages_remaining: outbound.messages_remaining,
343 messages_submitted: outbound_hrmp_mod.messages_submitted,
344 })?;
345 } else {
346 return Err(ModificationError::NoSuchHrmpChannel(*id));
347 }
348 }
349
350 new.ump_remaining = new.ump_remaining.checked_sub(modifications.ump_messages_sent).ok_or(
351 ModificationError::UmpMessagesOverflow {
352 messages_remaining: new.ump_remaining,
353 messages_submitted: modifications.ump_messages_sent,
354 },
355 )?;
356
357 new.ump_remaining_bytes = new
358 .ump_remaining_bytes
359 .checked_sub(modifications.ump_bytes_sent)
360 .ok_or(ModificationError::UmpBytesOverflow {
361 bytes_remaining: new.ump_remaining_bytes,
362 bytes_submitted: modifications.ump_bytes_sent,
363 })?;
364
365 if modifications.dmp_messages_processed > new.dmp_remaining_messages.len() {
366 return Err(ModificationError::DmpMessagesUnderflow {
367 messages_remaining: new.dmp_remaining_messages.len(),
368 messages_processed: modifications.dmp_messages_processed,
369 });
370 } else {
371 new.dmp_remaining_messages =
372 new.dmp_remaining_messages[modifications.dmp_messages_processed..].to_vec();
373 }
374
375 if modifications.code_upgrade_applied {
376 new.validation_code_hash = new
377 .future_validation_code
378 .take()
379 .ok_or(ModificationError::AppliedNonexistentCodeUpgrade)?
380 .1;
381 }
382
383 Ok(new)
384 }
385}
386
387#[derive(Debug, Clone, PartialEq)]
389pub struct RelayChainBlockInfo {
390 pub hash: Hash,
392 pub number: BlockNumber,
394 pub storage_root: Hash,
396}
397
398#[derive(Debug, Clone, PartialEq, Default)]
400pub struct OutboundHrmpChannelModification {
401 pub bytes_submitted: usize,
403 pub messages_submitted: usize,
405}
406
407#[derive(Debug, Clone, PartialEq)]
409pub enum HrmpWatermarkUpdate {
410 Head(BlockNumber),
413 Trunk(BlockNumber),
417}
418
419impl HrmpWatermarkUpdate {
420 fn watermark(&self) -> BlockNumber {
421 match *self {
422 HrmpWatermarkUpdate::Head(n) | HrmpWatermarkUpdate::Trunk(n) => n,
423 }
424 }
425}
426
427#[derive(Debug, Clone, PartialEq)]
429pub struct ConstraintModifications {
430 pub required_parent: Option<HeadData>,
432 pub hrmp_watermark: Option<HrmpWatermarkUpdate>,
434 pub outbound_hrmp: HashMap<ParaId, OutboundHrmpChannelModification>,
436 pub ump_messages_sent: usize,
438 pub ump_bytes_sent: usize,
440 pub dmp_messages_processed: usize,
442 pub code_upgrade_applied: bool,
444}
445
446impl ConstraintModifications {
447 pub fn identity() -> Self {
450 ConstraintModifications {
451 required_parent: None,
452 hrmp_watermark: None,
453 outbound_hrmp: HashMap::new(),
454 ump_messages_sent: 0,
455 ump_bytes_sent: 0,
456 dmp_messages_processed: 0,
457 code_upgrade_applied: false,
458 }
459 }
460
461 pub fn stack(&mut self, other: &Self) {
468 if let Some(ref new_parent) = other.required_parent {
469 self.required_parent = Some(new_parent.clone());
470 }
471 if let Some(ref new_hrmp_watermark) = other.hrmp_watermark {
472 self.hrmp_watermark = Some(new_hrmp_watermark.clone());
473 }
474
475 for (id, mods) in &other.outbound_hrmp {
476 let record = self.outbound_hrmp.entry(*id).or_default();
477 record.messages_submitted += mods.messages_submitted;
478 record.bytes_submitted += mods.bytes_submitted;
479 }
480
481 self.ump_messages_sent += other.ump_messages_sent;
482 self.ump_bytes_sent += other.ump_bytes_sent;
483 self.dmp_messages_processed += other.dmp_messages_processed;
484 self.code_upgrade_applied |= other.code_upgrade_applied;
485 }
486}
487
488#[derive(Debug, Clone, PartialEq)]
495pub struct ProspectiveCandidate {
496 pub commitments: CandidateCommitments,
498 pub persisted_validation_data: PersistedValidationData,
500 pub pov_hash: Hash,
502 pub validation_code_hash: ValidationCodeHash,
504}
505
506#[derive(Debug, Clone, PartialEq)]
508pub enum FragmentValidityError {
509 ValidationCodeMismatch(ValidationCodeHash, ValidationCodeHash),
514 PersistedValidationDataMismatch(PersistedValidationData, PersistedValidationData),
518 OutputsInvalid(ModificationError),
521 CodeSizeTooLarge(usize, usize),
525 HeadDataTooLarge(usize, usize),
529 RelayParentTooOld(BlockNumber, BlockNumber),
533 DmpAdvancementRule,
535 UmpMessagesPerCandidateOverflow {
537 messages_allowed: usize,
539 messages_submitted: usize,
541 },
542 HrmpMessagesPerCandidateOverflow {
544 messages_allowed: usize,
546 messages_submitted: usize,
548 },
549 CodeUpgradeRestricted,
551 HrmpMessagesDescendingOrDuplicate(usize),
556}
557
558#[derive(Debug, Clone, PartialEq)]
563pub struct Fragment {
564 relay_parent: RelayChainBlockInfo,
566 operating_constraints: Constraints,
568 candidate: Arc<ProspectiveCandidate>,
570 modifications: ConstraintModifications,
573}
574
575impl Fragment {
576 pub fn new(
585 relay_parent: RelayChainBlockInfo,
586 operating_constraints: Constraints,
587 candidate: Arc<ProspectiveCandidate>,
588 ) -> Result<Self, FragmentValidityError> {
589 let modifications = Self::check_against_constraints(
590 &relay_parent,
591 &operating_constraints,
592 &candidate.commitments,
593 &candidate.validation_code_hash,
594 &candidate.persisted_validation_data,
595 )?;
596
597 Ok(Fragment { relay_parent, operating_constraints, candidate, modifications })
598 }
599
600 pub fn check_against_constraints(
603 relay_parent: &RelayChainBlockInfo,
604 operating_constraints: &Constraints,
605 commitments: &CandidateCommitments,
606 validation_code_hash: &ValidationCodeHash,
607 persisted_validation_data: &PersistedValidationData,
608 ) -> Result<ConstraintModifications, FragmentValidityError> {
609 let upward_messages =
611 skip_ump_signals(commitments.upward_messages.iter()).collect::<Vec<_>>();
612
613 let ump_messages_sent = upward_messages.len();
614 let ump_bytes_sent = upward_messages.iter().map(|msg| msg.len()).sum();
615
616 let modifications = {
617 ConstraintModifications {
618 required_parent: Some(commitments.head_data.clone()),
619 hrmp_watermark: Some({
620 if commitments.hrmp_watermark == relay_parent.number {
621 HrmpWatermarkUpdate::Head(commitments.hrmp_watermark)
622 } else {
623 HrmpWatermarkUpdate::Trunk(commitments.hrmp_watermark)
624 }
625 }),
626 outbound_hrmp: {
627 let mut outbound_hrmp = HashMap::<_, OutboundHrmpChannelModification>::new();
628
629 let mut last_recipient = None::<ParaId>;
630 for (i, message) in commitments.horizontal_messages.iter().enumerate() {
631 if let Some(last) = last_recipient {
632 if last >= message.recipient {
633 return Err(
634 FragmentValidityError::HrmpMessagesDescendingOrDuplicate(i),
635 );
636 }
637 }
638
639 last_recipient = Some(message.recipient);
640 let record = outbound_hrmp.entry(message.recipient).or_default();
641
642 record.bytes_submitted += message.data.len();
643 record.messages_submitted += 1;
644 }
645
646 outbound_hrmp
647 },
648 ump_messages_sent,
649 ump_bytes_sent,
650 dmp_messages_processed: commitments.processed_downward_messages as _,
651 code_upgrade_applied: operating_constraints
652 .future_validation_code
653 .map_or(false, |(at, _)| relay_parent.number >= at),
654 }
655 };
656
657 validate_against_constraints(
658 &operating_constraints,
659 &relay_parent,
660 commitments,
661 persisted_validation_data,
662 validation_code_hash,
663 &modifications,
664 )?;
665
666 Ok(modifications)
667 }
668
669 pub fn relay_parent(&self) -> &RelayChainBlockInfo {
671 &self.relay_parent
672 }
673
674 pub fn operating_constraints(&self) -> &Constraints {
676 &self.operating_constraints
677 }
678
679 pub fn candidate(&self) -> &ProspectiveCandidate {
681 &self.candidate
682 }
683
684 pub fn candidate_clone(&self) -> Arc<ProspectiveCandidate> {
686 self.candidate.clone()
687 }
688
689 pub fn constraint_modifications(&self) -> &ConstraintModifications {
691 &self.modifications
692 }
693}
694
695pub fn validate_commitments(
697 constraints: &Constraints,
698 relay_parent: &RelayChainBlockInfo,
699 commitments: &CandidateCommitments,
700 validation_code_hash: &ValidationCodeHash,
701) -> Result<(), FragmentValidityError> {
702 if constraints.validation_code_hash != *validation_code_hash {
703 return Err(FragmentValidityError::ValidationCodeMismatch(
704 constraints.validation_code_hash,
705 *validation_code_hash,
706 ));
707 }
708
709 if commitments.head_data.0.len() > constraints.max_head_data_size {
710 return Err(FragmentValidityError::HeadDataTooLarge(
711 constraints.max_head_data_size,
712 commitments.head_data.0.len(),
713 ));
714 }
715
716 if relay_parent.number < constraints.min_relay_parent_number {
717 return Err(FragmentValidityError::RelayParentTooOld(
718 constraints.min_relay_parent_number,
719 relay_parent.number,
720 ));
721 }
722
723 if commitments.new_validation_code.is_some() {
724 match constraints.upgrade_restriction {
725 None => {},
726 Some(UpgradeRestriction::Present) => {
727 return Err(FragmentValidityError::CodeUpgradeRestricted)
728 },
729 }
730 }
731
732 let announced_code_size =
733 commitments.new_validation_code.as_ref().map_or(0, |code| code.0.len());
734
735 if announced_code_size > constraints.max_code_size {
736 return Err(FragmentValidityError::CodeSizeTooLarge(
737 constraints.max_code_size,
738 announced_code_size,
739 ));
740 }
741
742 if commitments.horizontal_messages.len() > constraints.max_hrmp_num_per_candidate {
743 return Err(FragmentValidityError::HrmpMessagesPerCandidateOverflow {
744 messages_allowed: constraints.max_hrmp_num_per_candidate,
745 messages_submitted: commitments.horizontal_messages.len(),
746 });
747 }
748
749 Ok(())
750}
751
752fn validate_against_constraints(
753 constraints: &Constraints,
754 relay_parent: &RelayChainBlockInfo,
755 commitments: &CandidateCommitments,
756 persisted_validation_data: &PersistedValidationData,
757 validation_code_hash: &ValidationCodeHash,
758 modifications: &ConstraintModifications,
759) -> Result<(), FragmentValidityError> {
760 validate_commitments(constraints, relay_parent, commitments, validation_code_hash)?;
761
762 let expected_pvd = PersistedValidationData {
763 parent_head: constraints.required_parent.clone(),
764 relay_parent_number: relay_parent.number,
765 relay_parent_storage_root: relay_parent.storage_root,
766 max_pov_size: constraints.max_pov_size as u32,
767 };
768
769 if expected_pvd != *persisted_validation_data {
770 return Err(FragmentValidityError::PersistedValidationDataMismatch(
771 expected_pvd,
772 persisted_validation_data.clone(),
773 ));
774 }
775 if modifications.dmp_messages_processed == 0 {
776 if constraints
777 .dmp_remaining_messages
778 .get(0)
779 .map_or(false, |&msg_sent_at| msg_sent_at <= relay_parent.number)
780 {
781 return Err(FragmentValidityError::DmpAdvancementRule);
782 }
783 }
784
785 if modifications.ump_messages_sent > constraints.max_ump_num_per_candidate {
786 return Err(FragmentValidityError::UmpMessagesPerCandidateOverflow {
787 messages_allowed: constraints.max_ump_num_per_candidate,
788 messages_submitted: commitments.upward_messages.len(),
789 });
790 }
791 constraints
792 .check_modifications(&modifications)
793 .map_err(FragmentValidityError::OutputsInvalid)
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799 use codec::Encode;
800 use polkadot_primitives::{
801 ClaimQueueOffset, CoreSelector, HorizontalMessages, OutboundHrmpMessage, UMPSignal,
802 ValidationCode, UMP_SEPARATOR,
803 };
804
805 #[test]
806 fn stack_modifications() {
807 let para_a = ParaId::from(1u32);
808 let para_b = ParaId::from(2u32);
809 let para_c = ParaId::from(3u32);
810
811 let a = ConstraintModifications {
812 required_parent: None,
813 hrmp_watermark: None,
814 outbound_hrmp: {
815 let mut map = HashMap::new();
816 map.insert(
817 para_a,
818 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
819 );
820
821 map.insert(
822 para_b,
823 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
824 );
825
826 map
827 },
828 ump_messages_sent: 6,
829 ump_bytes_sent: 1000,
830 dmp_messages_processed: 5,
831 code_upgrade_applied: true,
832 };
833
834 let b = ConstraintModifications {
835 required_parent: None,
836 hrmp_watermark: None,
837 outbound_hrmp: {
838 let mut map = HashMap::new();
839 map.insert(
840 para_b,
841 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
842 );
843
844 map.insert(
845 para_c,
846 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
847 );
848
849 map
850 },
851 ump_messages_sent: 6,
852 ump_bytes_sent: 1000,
853 dmp_messages_processed: 5,
854 code_upgrade_applied: true,
855 };
856
857 let mut c = a.clone();
858 c.stack(&b);
859
860 assert_eq!(
861 c,
862 ConstraintModifications {
863 required_parent: None,
864 hrmp_watermark: None,
865 outbound_hrmp: {
866 let mut map = HashMap::new();
867 map.insert(
868 para_a,
869 OutboundHrmpChannelModification {
870 bytes_submitted: 100,
871 messages_submitted: 5,
872 },
873 );
874
875 map.insert(
876 para_b,
877 OutboundHrmpChannelModification {
878 bytes_submitted: 200,
879 messages_submitted: 10,
880 },
881 );
882
883 map.insert(
884 para_c,
885 OutboundHrmpChannelModification {
886 bytes_submitted: 100,
887 messages_submitted: 5,
888 },
889 );
890
891 map
892 },
893 ump_messages_sent: 12,
894 ump_bytes_sent: 2000,
895 dmp_messages_processed: 10,
896 code_upgrade_applied: true,
897 },
898 );
899
900 let mut d = ConstraintModifications::identity();
901 d.stack(&a);
902 d.stack(&b);
903
904 assert_eq!(c, d);
905 }
906
907 fn make_constraints() -> Constraints {
908 let para_a = ParaId::from(1u32);
909 let para_b = ParaId::from(2u32);
910 let para_c = ParaId::from(3u32);
911
912 Constraints {
913 min_relay_parent_number: 5,
914 max_pov_size: 1000,
915 max_code_size: 1000,
916 ump_remaining: 10,
917 ump_remaining_bytes: 1024,
918 max_ump_num_per_candidate: 5,
919 dmp_remaining_messages: Vec::new(),
920 hrmp_inbound: InboundHrmpLimitations { valid_watermarks: vec![6, 8] },
921 hrmp_channels_out: {
922 let mut map = HashMap::new();
923
924 map.insert(
925 para_a,
926 OutboundHrmpChannelLimitations { messages_remaining: 5, bytes_remaining: 512 },
927 );
928
929 map.insert(
930 para_b,
931 OutboundHrmpChannelLimitations {
932 messages_remaining: 10,
933 bytes_remaining: 1024,
934 },
935 );
936
937 map.insert(
938 para_c,
939 OutboundHrmpChannelLimitations { messages_remaining: 1, bytes_remaining: 128 },
940 );
941
942 map
943 },
944 max_hrmp_num_per_candidate: 5,
945 required_parent: HeadData::from(vec![1, 2, 3]),
946 validation_code_hash: ValidationCode(vec![4, 5, 6]).hash(),
947 upgrade_restriction: None,
948 future_validation_code: None,
949 max_head_data_size: 1024,
950 }
951 }
952
953 #[test]
954 fn constraints_check_trunk_watermark() {
955 let constraints = make_constraints();
956 let mut modifications = ConstraintModifications::identity();
957
958 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(6));
960 assert!(constraints.check_modifications(&modifications).is_ok());
961 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
962 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![6, 8]);
963
964 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(7));
965 assert_eq!(
966 constraints.check_modifications(&modifications),
967 Err(ModificationError::DisallowedHrmpWatermark(7)),
968 );
969 assert_eq!(
970 constraints.apply_modifications(&modifications),
971 Err(ModificationError::DisallowedHrmpWatermark(7)),
972 );
973
974 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(8));
975 assert!(constraints.check_modifications(&modifications).is_ok());
976 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
977 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![8]);
978 }
979
980 #[test]
981 fn constraints_check_head_watermark() {
982 let constraints = make_constraints();
983 let mut modifications = ConstraintModifications::identity();
984
985 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Head(5));
986 assert!(constraints.check_modifications(&modifications).is_ok());
987 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
988 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![6, 8]);
989
990 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Head(7));
991 assert!(constraints.check_modifications(&modifications).is_ok());
992 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
993 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![8]);
994 }
995
996 #[test]
997 fn constraints_no_such_hrmp_channel() {
998 let constraints = make_constraints();
999 let mut modifications = ConstraintModifications::identity();
1000 let bad_para = ParaId::from(100u32);
1001 modifications.outbound_hrmp.insert(
1002 bad_para,
1003 OutboundHrmpChannelModification { bytes_submitted: 0, messages_submitted: 0 },
1004 );
1005
1006 assert_eq!(
1007 constraints.check_modifications(&modifications),
1008 Err(ModificationError::NoSuchHrmpChannel(bad_para)),
1009 );
1010
1011 assert_eq!(
1012 constraints.apply_modifications(&modifications),
1013 Err(ModificationError::NoSuchHrmpChannel(bad_para)),
1014 );
1015 }
1016
1017 #[test]
1018 fn constraints_hrmp_messages_overflow() {
1019 let constraints = make_constraints();
1020 let mut modifications = ConstraintModifications::identity();
1021 let para_a = ParaId::from(1u32);
1022 modifications.outbound_hrmp.insert(
1023 para_a,
1024 OutboundHrmpChannelModification { bytes_submitted: 0, messages_submitted: 6 },
1025 );
1026
1027 assert_eq!(
1028 constraints.check_modifications(&modifications),
1029 Err(ModificationError::HrmpMessagesOverflow {
1030 para_id: para_a,
1031 messages_remaining: 5,
1032 messages_submitted: 6,
1033 }),
1034 );
1035
1036 assert_eq!(
1037 constraints.apply_modifications(&modifications),
1038 Err(ModificationError::HrmpMessagesOverflow {
1039 para_id: para_a,
1040 messages_remaining: 5,
1041 messages_submitted: 6,
1042 }),
1043 );
1044 }
1045
1046 #[test]
1047 fn constraints_hrmp_bytes_overflow() {
1048 let constraints = make_constraints();
1049 let mut modifications = ConstraintModifications::identity();
1050 let para_a = ParaId::from(1u32);
1051 modifications.outbound_hrmp.insert(
1052 para_a,
1053 OutboundHrmpChannelModification { bytes_submitted: 513, messages_submitted: 1 },
1054 );
1055
1056 assert_eq!(
1057 constraints.check_modifications(&modifications),
1058 Err(ModificationError::HrmpBytesOverflow {
1059 para_id: para_a,
1060 bytes_remaining: 512,
1061 bytes_submitted: 513,
1062 }),
1063 );
1064
1065 assert_eq!(
1066 constraints.apply_modifications(&modifications),
1067 Err(ModificationError::HrmpBytesOverflow {
1068 para_id: para_a,
1069 bytes_remaining: 512,
1070 bytes_submitted: 513,
1071 }),
1072 );
1073 }
1074
1075 #[test]
1076 fn constraints_ump_messages_overflow() {
1077 let constraints = make_constraints();
1078 let mut modifications = ConstraintModifications::identity();
1079 modifications.ump_messages_sent = 11;
1080
1081 assert_eq!(
1082 constraints.check_modifications(&modifications),
1083 Err(ModificationError::UmpMessagesOverflow {
1084 messages_remaining: 10,
1085 messages_submitted: 11,
1086 }),
1087 );
1088
1089 assert_eq!(
1090 constraints.apply_modifications(&modifications),
1091 Err(ModificationError::UmpMessagesOverflow {
1092 messages_remaining: 10,
1093 messages_submitted: 11,
1094 }),
1095 );
1096 }
1097
1098 #[test]
1099 fn constraints_ump_bytes_overflow() {
1100 let constraints = make_constraints();
1101 let mut modifications = ConstraintModifications::identity();
1102 modifications.ump_bytes_sent = 1025;
1103
1104 assert_eq!(
1105 constraints.check_modifications(&modifications),
1106 Err(ModificationError::UmpBytesOverflow {
1107 bytes_remaining: 1024,
1108 bytes_submitted: 1025,
1109 }),
1110 );
1111
1112 assert_eq!(
1113 constraints.apply_modifications(&modifications),
1114 Err(ModificationError::UmpBytesOverflow {
1115 bytes_remaining: 1024,
1116 bytes_submitted: 1025,
1117 }),
1118 );
1119 }
1120
1121 #[test]
1122 fn constraints_dmp_messages() {
1123 let mut constraints = make_constraints();
1124 let mut modifications = ConstraintModifications::identity();
1125 assert!(constraints.check_modifications(&modifications).is_ok());
1126 assert!(constraints.apply_modifications(&modifications).is_ok());
1127
1128 modifications.dmp_messages_processed = 6;
1129
1130 assert_eq!(
1131 constraints.check_modifications(&modifications),
1132 Err(ModificationError::DmpMessagesUnderflow {
1133 messages_remaining: 0,
1134 messages_processed: 6,
1135 }),
1136 );
1137
1138 assert_eq!(
1139 constraints.apply_modifications(&modifications),
1140 Err(ModificationError::DmpMessagesUnderflow {
1141 messages_remaining: 0,
1142 messages_processed: 6,
1143 }),
1144 );
1145
1146 constraints.dmp_remaining_messages = vec![1, 4, 8, 10];
1147 modifications.dmp_messages_processed = 2;
1148 assert!(constraints.check_modifications(&modifications).is_ok());
1149 let constraints = constraints
1150 .apply_modifications(&modifications)
1151 .expect("modifications are valid");
1152
1153 assert_eq!(&constraints.dmp_remaining_messages, &[8, 10]);
1154 }
1155
1156 #[test]
1157 fn constraints_nonexistent_code_upgrade() {
1158 let constraints = make_constraints();
1159 let mut modifications = ConstraintModifications::identity();
1160 modifications.code_upgrade_applied = true;
1161
1162 assert_eq!(
1163 constraints.check_modifications(&modifications),
1164 Err(ModificationError::AppliedNonexistentCodeUpgrade),
1165 );
1166
1167 assert_eq!(
1168 constraints.apply_modifications(&modifications),
1169 Err(ModificationError::AppliedNonexistentCodeUpgrade),
1170 );
1171 }
1172
1173 fn make_candidate(
1174 constraints: &Constraints,
1175 relay_parent: &RelayChainBlockInfo,
1176 ) -> ProspectiveCandidate {
1177 ProspectiveCandidate {
1178 commitments: CandidateCommitments {
1179 upward_messages: Default::default(),
1180 horizontal_messages: Default::default(),
1181 new_validation_code: None,
1182 head_data: HeadData::from(vec![1, 2, 3, 4, 5]),
1183 processed_downward_messages: 0,
1184 hrmp_watermark: relay_parent.number,
1185 },
1186 persisted_validation_data: PersistedValidationData {
1187 parent_head: constraints.required_parent.clone(),
1188 relay_parent_number: relay_parent.number,
1189 relay_parent_storage_root: relay_parent.storage_root,
1190 max_pov_size: constraints.max_pov_size as u32,
1191 },
1192 pov_hash: Hash::repeat_byte(1),
1193 validation_code_hash: constraints.validation_code_hash,
1194 }
1195 }
1196
1197 #[test]
1198 fn fragment_validation_code_mismatch() {
1199 let relay_parent = RelayChainBlockInfo {
1200 number: 6,
1201 hash: Hash::repeat_byte(0x0a),
1202 storage_root: Hash::repeat_byte(0xff),
1203 };
1204
1205 let constraints = make_constraints();
1206 let mut candidate = make_candidate(&constraints, &relay_parent);
1207
1208 let expected_code = constraints.validation_code_hash;
1209 let got_code = ValidationCode(vec![9, 9, 9]).hash();
1210
1211 candidate.validation_code_hash = got_code;
1212
1213 assert_eq!(
1214 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1215 Err(FragmentValidityError::ValidationCodeMismatch(expected_code, got_code,)),
1216 )
1217 }
1218
1219 #[test]
1220 fn fragment_pvd_mismatch() {
1221 let relay_parent = RelayChainBlockInfo {
1222 number: 6,
1223 hash: Hash::repeat_byte(0x0a),
1224 storage_root: Hash::repeat_byte(0xff),
1225 };
1226
1227 let relay_parent_b = RelayChainBlockInfo {
1228 number: 6,
1229 hash: Hash::repeat_byte(0x0b),
1230 storage_root: Hash::repeat_byte(0xee),
1231 };
1232
1233 let constraints = make_constraints();
1234 let candidate = make_candidate(&constraints, &relay_parent);
1235
1236 let expected_pvd = PersistedValidationData {
1237 parent_head: constraints.required_parent.clone(),
1238 relay_parent_number: relay_parent_b.number,
1239 relay_parent_storage_root: relay_parent_b.storage_root,
1240 max_pov_size: constraints.max_pov_size as u32,
1241 };
1242
1243 let got_pvd = candidate.persisted_validation_data.clone();
1244
1245 assert_eq!(
1246 Fragment::new(relay_parent_b, constraints, Arc::new(candidate.clone())),
1247 Err(FragmentValidityError::PersistedValidationDataMismatch(expected_pvd, got_pvd,)),
1248 );
1249 }
1250
1251 #[test]
1252 fn fragment_code_size_too_large() {
1253 let relay_parent = RelayChainBlockInfo {
1254 number: 6,
1255 hash: Hash::repeat_byte(0x0a),
1256 storage_root: Hash::repeat_byte(0xff),
1257 };
1258
1259 let constraints = make_constraints();
1260 let mut candidate = make_candidate(&constraints, &relay_parent);
1261
1262 let max_code_size = constraints.max_code_size;
1263 candidate.commitments.new_validation_code = Some(vec![0; max_code_size + 1].into());
1264
1265 assert_eq!(
1266 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1267 Err(FragmentValidityError::CodeSizeTooLarge(max_code_size, max_code_size + 1,)),
1268 );
1269 }
1270
1271 #[test]
1272 fn ump_signals_ignored() {
1273 let relay_parent = RelayChainBlockInfo {
1274 number: 6,
1275 hash: Hash::repeat_byte(0xbe),
1276 storage_root: Hash::repeat_byte(0xff),
1277 };
1278
1279 let constraints = make_constraints();
1280 let mut candidate = make_candidate(&constraints, &relay_parent);
1281 let max_ump = constraints.max_ump_num_per_candidate;
1282
1283 candidate
1285 .commitments
1286 .upward_messages
1287 .try_extend((0..max_ump).map(|i| vec![i as u8]))
1288 .unwrap();
1289
1290 candidate.commitments.upward_messages.force_push(UMP_SEPARATOR);
1292 candidate
1293 .commitments
1294 .upward_messages
1295 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1296
1297 Fragment::new(relay_parent, constraints, Arc::new(candidate)).unwrap();
1298 }
1299
1300 #[test]
1301 fn fragment_relay_parent_too_old() {
1302 let relay_parent = RelayChainBlockInfo {
1303 number: 3,
1304 hash: Hash::repeat_byte(0x0a),
1305 storage_root: Hash::repeat_byte(0xff),
1306 };
1307
1308 let constraints = make_constraints();
1309 let candidate = make_candidate(&constraints, &relay_parent);
1310
1311 assert_eq!(
1312 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1313 Err(FragmentValidityError::RelayParentTooOld(5, 3,)),
1314 );
1315 }
1316
1317 #[test]
1318 fn fragment_hrmp_messages_overflow() {
1319 let relay_parent = RelayChainBlockInfo {
1320 number: 6,
1321 hash: Hash::repeat_byte(0x0a),
1322 storage_root: Hash::repeat_byte(0xff),
1323 };
1324
1325 let constraints = make_constraints();
1326 let mut candidate = make_candidate(&constraints, &relay_parent);
1327
1328 let max_hrmp = constraints.max_hrmp_num_per_candidate;
1329
1330 candidate
1331 .commitments
1332 .horizontal_messages
1333 .try_extend((0..max_hrmp + 1).map(|i| OutboundHrmpMessage {
1334 recipient: ParaId::from(i as u32),
1335 data: vec![1, 2, 3],
1336 }))
1337 .unwrap();
1338
1339 assert_eq!(
1340 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1341 Err(FragmentValidityError::HrmpMessagesPerCandidateOverflow {
1342 messages_allowed: max_hrmp,
1343 messages_submitted: max_hrmp + 1,
1344 }),
1345 );
1346 }
1347
1348 #[test]
1349 fn fragment_dmp_advancement_rule() {
1350 let relay_parent = RelayChainBlockInfo {
1351 number: 6,
1352 hash: Hash::repeat_byte(0x0a),
1353 storage_root: Hash::repeat_byte(0xff),
1354 };
1355
1356 let mut constraints = make_constraints();
1357 let mut candidate = make_candidate(&constraints, &relay_parent);
1358
1359 assert!(Fragment::new(
1361 relay_parent.clone(),
1362 constraints.clone(),
1363 Arc::new(candidate.clone())
1364 )
1365 .is_ok());
1366 constraints.dmp_remaining_messages = vec![relay_parent.number + 1];
1368 assert!(Fragment::new(
1369 relay_parent.clone(),
1370 constraints.clone(),
1371 Arc::new(candidate.clone())
1372 )
1373 .is_ok());
1374
1375 for block_number in 0..=relay_parent.number {
1376 constraints.dmp_remaining_messages = vec![block_number];
1377
1378 assert_eq!(
1379 Fragment::new(
1380 relay_parent.clone(),
1381 constraints.clone(),
1382 Arc::new(candidate.clone())
1383 ),
1384 Err(FragmentValidityError::DmpAdvancementRule),
1385 );
1386 }
1387
1388 candidate.commitments.processed_downward_messages = 1;
1389 assert!(Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())).is_ok());
1390 }
1391
1392 #[test]
1393 fn fragment_ump_messages_overflow() {
1394 let relay_parent = RelayChainBlockInfo {
1395 number: 6,
1396 hash: Hash::repeat_byte(0x0a),
1397 storage_root: Hash::repeat_byte(0xff),
1398 };
1399
1400 let constraints = make_constraints();
1401 let mut candidate = make_candidate(&constraints, &relay_parent);
1402
1403 let max_ump = constraints.max_ump_num_per_candidate;
1404
1405 candidate
1406 .commitments
1407 .upward_messages
1408 .try_extend((0..max_ump + 1).map(|i| vec![i as u8]))
1409 .unwrap();
1410
1411 assert_eq!(
1412 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1413 Err(FragmentValidityError::UmpMessagesPerCandidateOverflow {
1414 messages_allowed: max_ump,
1415 messages_submitted: max_ump + 1,
1416 }),
1417 );
1418 }
1419
1420 #[test]
1421 fn fragment_code_upgrade_restricted() {
1422 let relay_parent = RelayChainBlockInfo {
1423 number: 6,
1424 hash: Hash::repeat_byte(0x0a),
1425 storage_root: Hash::repeat_byte(0xff),
1426 };
1427
1428 let mut constraints = make_constraints();
1429 let mut candidate = make_candidate(&constraints, &relay_parent);
1430
1431 constraints.upgrade_restriction = Some(UpgradeRestriction::Present);
1432 candidate.commitments.new_validation_code = Some(ValidationCode(vec![1, 2, 3]));
1433
1434 assert_eq!(
1435 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1436 Err(FragmentValidityError::CodeUpgradeRestricted),
1437 );
1438 }
1439
1440 #[test]
1441 fn fragment_hrmp_messages_descending_or_duplicate() {
1442 let relay_parent = RelayChainBlockInfo {
1443 number: 6,
1444 hash: Hash::repeat_byte(0x0a),
1445 storage_root: Hash::repeat_byte(0xff),
1446 };
1447
1448 let constraints = make_constraints();
1449 let mut candidate = make_candidate(&constraints, &relay_parent);
1450
1451 candidate.commitments.horizontal_messages = HorizontalMessages::truncate_from(vec![
1452 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![1, 2, 3] },
1453 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![4, 5, 6] },
1454 ]);
1455
1456 assert_eq!(
1457 Fragment::new(relay_parent.clone(), constraints.clone(), Arc::new(candidate.clone())),
1458 Err(FragmentValidityError::HrmpMessagesDescendingOrDuplicate(1)),
1459 );
1460
1461 candidate.commitments.horizontal_messages = HorizontalMessages::truncate_from(vec![
1462 OutboundHrmpMessage { recipient: ParaId::from(1 as u32), data: vec![1, 2, 3] },
1463 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![4, 5, 6] },
1464 ]);
1465
1466 assert_eq!(
1467 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1468 Err(FragmentValidityError::HrmpMessagesDescendingOrDuplicate(1)),
1469 );
1470 }
1471
1472 #[test]
1473 fn head_data_size_too_large() {
1474 let relay_parent = RelayChainBlockInfo {
1475 number: 6,
1476 hash: Hash::repeat_byte(0xcc),
1477 storage_root: Hash::repeat_byte(0xff),
1478 };
1479
1480 let constraints = make_constraints();
1481 let mut candidate = make_candidate(&constraints, &relay_parent);
1482
1483 let head_data_size = constraints.max_head_data_size;
1484 candidate.commitments.head_data = vec![0; head_data_size + 1].into();
1485
1486 assert_eq!(
1487 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1488 Err(FragmentValidityError::HeadDataTooLarge(head_data_size, head_data_size + 1)),
1489 );
1490 }
1491}