1use polkadot_node_subsystem::messages::HypotheticalCandidate;
84use polkadot_primitives::{
85 async_backing::Constraints as PrimitiveConstraints, skip_ump_signals, BlockNumber,
86 CandidateCommitments, CandidateHash, Hash, HeadData, Id as ParaId, PersistedValidationData,
87 UpgradeRestriction, ValidationCodeHash,
88};
89use std::{collections::HashMap, sync::Arc};
90
91#[derive(Debug, Clone, PartialEq)]
93pub struct InboundHrmpLimitations {
94 pub valid_watermarks: Vec<BlockNumber>,
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub struct OutboundHrmpChannelLimitations {
101 pub bytes_remaining: usize,
103 pub messages_remaining: usize,
105}
106
107#[derive(Debug, Clone, PartialEq)]
111pub struct Constraints {
112 pub min_relay_parent_number: BlockNumber,
114 pub max_pov_size: usize,
116 pub max_code_size: usize,
118 pub max_head_data_size: usize,
120 pub ump_remaining: usize,
122 pub ump_remaining_bytes: usize,
124 pub max_ump_num_per_candidate: usize,
126 pub dmp_remaining_messages: Vec<BlockNumber>,
128 pub hrmp_inbound: InboundHrmpLimitations,
130 pub hrmp_channels_out: HashMap<ParaId, OutboundHrmpChannelLimitations>,
132 pub max_hrmp_num_per_candidate: usize,
134 pub required_parent: HeadData,
136 pub validation_code_hash: ValidationCodeHash,
138 pub upgrade_restriction: Option<UpgradeRestriction>,
140 pub future_validation_code: Option<(BlockNumber, ValidationCodeHash)>,
143}
144
145impl From<PrimitiveConstraints> for Constraints {
146 fn from(c: PrimitiveConstraints) -> Self {
147 Constraints {
148 min_relay_parent_number: c.min_relay_parent_number,
149 max_pov_size: c.max_pov_size as _,
150 max_code_size: c.max_code_size as _,
151 max_head_data_size: c.max_head_data_size as _,
152 ump_remaining: c.ump_remaining as _,
153 ump_remaining_bytes: c.ump_remaining_bytes as _,
154 max_ump_num_per_candidate: c.max_ump_num_per_candidate as _,
155 dmp_remaining_messages: c.dmp_remaining_messages,
156 hrmp_inbound: InboundHrmpLimitations {
157 valid_watermarks: c.hrmp_inbound.valid_watermarks,
158 },
159 hrmp_channels_out: c
160 .hrmp_channels_out
161 .into_iter()
162 .map(|(para_id, limits)| {
163 (
164 para_id,
165 OutboundHrmpChannelLimitations {
166 bytes_remaining: limits.bytes_remaining as _,
167 messages_remaining: limits.messages_remaining as _,
168 },
169 )
170 })
171 .collect(),
172 max_hrmp_num_per_candidate: c.max_hrmp_num_per_candidate as _,
173 required_parent: c.required_parent,
174 validation_code_hash: c.validation_code_hash,
175 upgrade_restriction: c.upgrade_restriction,
176 future_validation_code: c.future_validation_code,
177 }
178 }
179}
180
181#[derive(Debug, Clone, PartialEq)]
183pub enum ModificationError {
184 DisallowedHrmpWatermark(BlockNumber),
186 NoSuchHrmpChannel(ParaId),
188 HrmpMessagesOverflow {
190 para_id: ParaId,
192 messages_remaining: usize,
194 messages_submitted: usize,
196 },
197 HrmpBytesOverflow {
199 para_id: ParaId,
201 bytes_remaining: usize,
203 bytes_submitted: usize,
205 },
206 UmpMessagesOverflow {
208 messages_remaining: usize,
210 messages_submitted: usize,
212 },
213 UmpBytesOverflow {
215 bytes_remaining: usize,
217 bytes_submitted: usize,
219 },
220 DmpMessagesUnderflow {
222 messages_remaining: usize,
224 messages_processed: usize,
226 },
227 AppliedNonexistentCodeUpgrade,
229}
230
231impl Constraints {
232 pub fn check_modifications(
234 &self,
235 modifications: &ConstraintModifications,
236 ) -> Result<(), ModificationError> {
237 if let Some(HrmpWatermarkUpdate::Trunk(hrmp_watermark)) = modifications.hrmp_watermark {
238 if !self.hrmp_inbound.valid_watermarks.contains(&hrmp_watermark) {
240 return Err(ModificationError::DisallowedHrmpWatermark(hrmp_watermark))
241 }
242 }
243
244 for (id, outbound_hrmp_mod) in &modifications.outbound_hrmp {
245 if let Some(outbound) = self.hrmp_channels_out.get(&id) {
246 outbound.bytes_remaining.checked_sub(outbound_hrmp_mod.bytes_submitted).ok_or(
247 ModificationError::HrmpBytesOverflow {
248 para_id: *id,
249 bytes_remaining: outbound.bytes_remaining,
250 bytes_submitted: outbound_hrmp_mod.bytes_submitted,
251 },
252 )?;
253
254 outbound
255 .messages_remaining
256 .checked_sub(outbound_hrmp_mod.messages_submitted)
257 .ok_or(ModificationError::HrmpMessagesOverflow {
258 para_id: *id,
259 messages_remaining: outbound.messages_remaining,
260 messages_submitted: outbound_hrmp_mod.messages_submitted,
261 })?;
262 } else {
263 return Err(ModificationError::NoSuchHrmpChannel(*id))
264 }
265 }
266
267 self.ump_remaining.checked_sub(modifications.ump_messages_sent).ok_or(
268 ModificationError::UmpMessagesOverflow {
269 messages_remaining: self.ump_remaining,
270 messages_submitted: modifications.ump_messages_sent,
271 },
272 )?;
273
274 self.ump_remaining_bytes.checked_sub(modifications.ump_bytes_sent).ok_or(
275 ModificationError::UmpBytesOverflow {
276 bytes_remaining: self.ump_remaining_bytes,
277 bytes_submitted: modifications.ump_bytes_sent,
278 },
279 )?;
280
281 self.dmp_remaining_messages
282 .len()
283 .checked_sub(modifications.dmp_messages_processed)
284 .ok_or(ModificationError::DmpMessagesUnderflow {
285 messages_remaining: self.dmp_remaining_messages.len(),
286 messages_processed: modifications.dmp_messages_processed,
287 })?;
288
289 if self.future_validation_code.is_none() && modifications.code_upgrade_applied {
290 return Err(ModificationError::AppliedNonexistentCodeUpgrade)
291 }
292
293 Ok(())
294 }
295
296 pub fn apply_modifications(
299 &self,
300 modifications: &ConstraintModifications,
301 ) -> Result<Self, ModificationError> {
302 let mut new = self.clone();
303
304 if let Some(required_parent) = modifications.required_parent.as_ref() {
305 new.required_parent = required_parent.clone();
306 }
307
308 if let Some(ref hrmp_watermark) = modifications.hrmp_watermark {
309 match new.hrmp_inbound.valid_watermarks.binary_search(&hrmp_watermark.watermark()) {
310 Ok(pos) => {
311 let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
313 },
314 Err(pos) => match hrmp_watermark {
315 HrmpWatermarkUpdate::Head(_) => {
316 let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
318 },
319 HrmpWatermarkUpdate::Trunk(n) => {
320 return Err(ModificationError::DisallowedHrmpWatermark(*n))
322 },
323 },
324 }
325 }
326
327 for (id, outbound_hrmp_mod) in &modifications.outbound_hrmp {
328 if let Some(outbound) = new.hrmp_channels_out.get_mut(&id) {
329 outbound.bytes_remaining = outbound
330 .bytes_remaining
331 .checked_sub(outbound_hrmp_mod.bytes_submitted)
332 .ok_or(ModificationError::HrmpBytesOverflow {
333 para_id: *id,
334 bytes_remaining: outbound.bytes_remaining,
335 bytes_submitted: outbound_hrmp_mod.bytes_submitted,
336 })?;
337
338 outbound.messages_remaining = outbound
339 .messages_remaining
340 .checked_sub(outbound_hrmp_mod.messages_submitted)
341 .ok_or(ModificationError::HrmpMessagesOverflow {
342 para_id: *id,
343 messages_remaining: outbound.messages_remaining,
344 messages_submitted: outbound_hrmp_mod.messages_submitted,
345 })?;
346 } else {
347 return Err(ModificationError::NoSuchHrmpChannel(*id))
348 }
349 }
350
351 new.ump_remaining = new.ump_remaining.checked_sub(modifications.ump_messages_sent).ok_or(
352 ModificationError::UmpMessagesOverflow {
353 messages_remaining: new.ump_remaining,
354 messages_submitted: modifications.ump_messages_sent,
355 },
356 )?;
357
358 new.ump_remaining_bytes = new
359 .ump_remaining_bytes
360 .checked_sub(modifications.ump_bytes_sent)
361 .ok_or(ModificationError::UmpBytesOverflow {
362 bytes_remaining: new.ump_remaining_bytes,
363 bytes_submitted: modifications.ump_bytes_sent,
364 })?;
365
366 if modifications.dmp_messages_processed > new.dmp_remaining_messages.len() {
367 return Err(ModificationError::DmpMessagesUnderflow {
368 messages_remaining: new.dmp_remaining_messages.len(),
369 messages_processed: modifications.dmp_messages_processed,
370 })
371 } else {
372 new.dmp_remaining_messages =
373 new.dmp_remaining_messages[modifications.dmp_messages_processed..].to_vec();
374 }
375
376 if modifications.code_upgrade_applied {
377 new.validation_code_hash = new
378 .future_validation_code
379 .take()
380 .ok_or(ModificationError::AppliedNonexistentCodeUpgrade)?
381 .1;
382 }
383
384 Ok(new)
385 }
386}
387
388#[derive(Debug, Clone, PartialEq)]
390pub struct RelayChainBlockInfo {
391 pub hash: Hash,
393 pub number: BlockNumber,
395 pub storage_root: Hash,
397}
398
399#[derive(Debug, Clone, PartialEq, Default)]
401pub struct OutboundHrmpChannelModification {
402 pub bytes_submitted: usize,
404 pub messages_submitted: usize,
406}
407
408#[derive(Debug, Clone, PartialEq)]
410pub enum HrmpWatermarkUpdate {
411 Head(BlockNumber),
414 Trunk(BlockNumber),
418}
419
420impl HrmpWatermarkUpdate {
421 fn watermark(&self) -> BlockNumber {
422 match *self {
423 HrmpWatermarkUpdate::Head(n) | HrmpWatermarkUpdate::Trunk(n) => n,
424 }
425 }
426}
427
428#[derive(Debug, Clone, PartialEq)]
430pub struct ConstraintModifications {
431 pub required_parent: Option<HeadData>,
433 pub hrmp_watermark: Option<HrmpWatermarkUpdate>,
435 pub outbound_hrmp: HashMap<ParaId, OutboundHrmpChannelModification>,
437 pub ump_messages_sent: usize,
439 pub ump_bytes_sent: usize,
441 pub dmp_messages_processed: usize,
443 pub code_upgrade_applied: bool,
445}
446
447impl ConstraintModifications {
448 pub fn identity() -> Self {
451 ConstraintModifications {
452 required_parent: None,
453 hrmp_watermark: None,
454 outbound_hrmp: HashMap::new(),
455 ump_messages_sent: 0,
456 ump_bytes_sent: 0,
457 dmp_messages_processed: 0,
458 code_upgrade_applied: false,
459 }
460 }
461
462 pub fn stack(&mut self, other: &Self) {
469 if let Some(ref new_parent) = other.required_parent {
470 self.required_parent = Some(new_parent.clone());
471 }
472 if let Some(ref new_hrmp_watermark) = other.hrmp_watermark {
473 self.hrmp_watermark = Some(new_hrmp_watermark.clone());
474 }
475
476 for (id, mods) in &other.outbound_hrmp {
477 let record = self.outbound_hrmp.entry(*id).or_default();
478 record.messages_submitted += mods.messages_submitted;
479 record.bytes_submitted += mods.bytes_submitted;
480 }
481
482 self.ump_messages_sent += other.ump_messages_sent;
483 self.ump_bytes_sent += other.ump_bytes_sent;
484 self.dmp_messages_processed += other.dmp_messages_processed;
485 self.code_upgrade_applied |= other.code_upgrade_applied;
486 }
487}
488
489#[derive(Debug, Clone, PartialEq)]
496pub struct ProspectiveCandidate {
497 pub commitments: CandidateCommitments,
499 pub persisted_validation_data: PersistedValidationData,
501 pub pov_hash: Hash,
503 pub validation_code_hash: ValidationCodeHash,
505}
506
507#[derive(Debug, Clone, PartialEq)]
509pub enum FragmentValidityError {
510 ValidationCodeMismatch(ValidationCodeHash, ValidationCodeHash),
515 PersistedValidationDataMismatch(PersistedValidationData, PersistedValidationData),
519 OutputsInvalid(ModificationError),
522 CodeSizeTooLarge(usize, usize),
526 HeadDataTooLarge(usize, usize),
530 RelayParentTooOld(BlockNumber, BlockNumber),
534 DmpAdvancementRule,
536 UmpMessagesPerCandidateOverflow {
538 messages_allowed: usize,
540 messages_submitted: usize,
542 },
543 HrmpMessagesPerCandidateOverflow {
545 messages_allowed: usize,
547 messages_submitted: usize,
549 },
550 CodeUpgradeRestricted,
552 HrmpMessagesDescendingOrDuplicate(usize),
557}
558
559#[derive(Debug, Clone, PartialEq)]
564pub struct Fragment {
565 relay_parent: RelayChainBlockInfo,
567 operating_constraints: Constraints,
569 candidate: Arc<ProspectiveCandidate>,
571 modifications: ConstraintModifications,
574}
575
576impl Fragment {
577 pub fn new(
586 relay_parent: RelayChainBlockInfo,
587 operating_constraints: Constraints,
588 candidate: Arc<ProspectiveCandidate>,
589 ) -> Result<Self, FragmentValidityError> {
590 let modifications = Self::check_against_constraints(
591 &relay_parent,
592 &operating_constraints,
593 &candidate.commitments,
594 &candidate.validation_code_hash,
595 &candidate.persisted_validation_data,
596 )?;
597
598 Ok(Fragment { relay_parent, operating_constraints, candidate, modifications })
599 }
600
601 pub fn check_against_constraints(
604 relay_parent: &RelayChainBlockInfo,
605 operating_constraints: &Constraints,
606 commitments: &CandidateCommitments,
607 validation_code_hash: &ValidationCodeHash,
608 persisted_validation_data: &PersistedValidationData,
609 ) -> Result<ConstraintModifications, FragmentValidityError> {
610 let upward_messages =
612 skip_ump_signals(commitments.upward_messages.iter()).collect::<Vec<_>>();
613
614 let ump_messages_sent = upward_messages.len();
615 let ump_bytes_sent = upward_messages.iter().map(|msg| msg.len()).sum();
616
617 let modifications = {
618 ConstraintModifications {
619 required_parent: Some(commitments.head_data.clone()),
620 hrmp_watermark: Some({
621 if commitments.hrmp_watermark == relay_parent.number {
622 HrmpWatermarkUpdate::Head(commitments.hrmp_watermark)
623 } else {
624 HrmpWatermarkUpdate::Trunk(commitments.hrmp_watermark)
625 }
626 }),
627 outbound_hrmp: {
628 let mut outbound_hrmp = HashMap::<_, OutboundHrmpChannelModification>::new();
629
630 let mut last_recipient = None::<ParaId>;
631 for (i, message) in commitments.horizontal_messages.iter().enumerate() {
632 if let Some(last) = last_recipient {
633 if last >= message.recipient {
634 return Err(
635 FragmentValidityError::HrmpMessagesDescendingOrDuplicate(i),
636 )
637 }
638 }
639
640 last_recipient = Some(message.recipient);
641 let record = outbound_hrmp.entry(message.recipient).or_default();
642
643 record.bytes_submitted += message.data.len();
644 record.messages_submitted += 1;
645 }
646
647 outbound_hrmp
648 },
649 ump_messages_sent,
650 ump_bytes_sent,
651 dmp_messages_processed: commitments.processed_downward_messages as _,
652 code_upgrade_applied: operating_constraints
653 .future_validation_code
654 .map_or(false, |(at, _)| relay_parent.number >= at),
655 }
656 };
657
658 validate_against_constraints(
659 &operating_constraints,
660 &relay_parent,
661 commitments,
662 persisted_validation_data,
663 validation_code_hash,
664 &modifications,
665 )?;
666
667 Ok(modifications)
668 }
669
670 pub fn relay_parent(&self) -> &RelayChainBlockInfo {
672 &self.relay_parent
673 }
674
675 pub fn operating_constraints(&self) -> &Constraints {
677 &self.operating_constraints
678 }
679
680 pub fn candidate(&self) -> &ProspectiveCandidate {
682 &self.candidate
683 }
684
685 pub fn candidate_clone(&self) -> Arc<ProspectiveCandidate> {
687 self.candidate.clone()
688 }
689
690 pub fn constraint_modifications(&self) -> &ConstraintModifications {
692 &self.modifications
693 }
694}
695
696pub fn validate_commitments(
698 constraints: &Constraints,
699 relay_parent: &RelayChainBlockInfo,
700 commitments: &CandidateCommitments,
701 validation_code_hash: &ValidationCodeHash,
702) -> Result<(), FragmentValidityError> {
703 if constraints.validation_code_hash != *validation_code_hash {
704 return Err(FragmentValidityError::ValidationCodeMismatch(
705 constraints.validation_code_hash,
706 *validation_code_hash,
707 ))
708 }
709
710 if commitments.head_data.0.len() > constraints.max_head_data_size {
711 return Err(FragmentValidityError::HeadDataTooLarge(
712 constraints.max_head_data_size,
713 commitments.head_data.0.len(),
714 ))
715 }
716
717 if relay_parent.number < constraints.min_relay_parent_number {
718 return Err(FragmentValidityError::RelayParentTooOld(
719 constraints.min_relay_parent_number,
720 relay_parent.number,
721 ))
722 }
723
724 if commitments.new_validation_code.is_some() {
725 match constraints.upgrade_restriction {
726 None => {},
727 Some(UpgradeRestriction::Present) =>
728 return Err(FragmentValidityError::CodeUpgradeRestricted),
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
796pub trait HypotheticalOrConcreteCandidate {
799 fn commitments(&self) -> Option<&CandidateCommitments>;
801 fn persisted_validation_data(&self) -> Option<&PersistedValidationData>;
803 fn validation_code_hash(&self) -> Option<ValidationCodeHash>;
805 fn parent_head_data_hash(&self) -> Hash;
807 fn output_head_data_hash(&self) -> Option<Hash>;
809 fn relay_parent(&self) -> Hash;
811 fn candidate_hash(&self) -> CandidateHash;
813}
814
815impl HypotheticalOrConcreteCandidate for HypotheticalCandidate {
816 fn commitments(&self) -> Option<&CandidateCommitments> {
817 self.commitments()
818 }
819
820 fn persisted_validation_data(&self) -> Option<&PersistedValidationData> {
821 self.persisted_validation_data()
822 }
823
824 fn validation_code_hash(&self) -> Option<ValidationCodeHash> {
825 self.validation_code_hash()
826 }
827
828 fn parent_head_data_hash(&self) -> Hash {
829 self.parent_head_data_hash()
830 }
831
832 fn output_head_data_hash(&self) -> Option<Hash> {
833 self.output_head_data_hash()
834 }
835
836 fn relay_parent(&self) -> Hash {
837 self.relay_parent()
838 }
839
840 fn candidate_hash(&self) -> CandidateHash {
841 self.candidate_hash()
842 }
843}
844
845#[cfg(test)]
846mod tests {
847 use super::*;
848 use codec::Encode;
849 use polkadot_primitives::{
850 ClaimQueueOffset, CoreSelector, HorizontalMessages, OutboundHrmpMessage, UMPSignal,
851 ValidationCode, UMP_SEPARATOR,
852 };
853
854 #[test]
855 fn stack_modifications() {
856 let para_a = ParaId::from(1u32);
857 let para_b = ParaId::from(2u32);
858 let para_c = ParaId::from(3u32);
859
860 let a = ConstraintModifications {
861 required_parent: None,
862 hrmp_watermark: None,
863 outbound_hrmp: {
864 let mut map = HashMap::new();
865 map.insert(
866 para_a,
867 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
868 );
869
870 map.insert(
871 para_b,
872 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
873 );
874
875 map
876 },
877 ump_messages_sent: 6,
878 ump_bytes_sent: 1000,
879 dmp_messages_processed: 5,
880 code_upgrade_applied: true,
881 };
882
883 let b = ConstraintModifications {
884 required_parent: None,
885 hrmp_watermark: None,
886 outbound_hrmp: {
887 let mut map = HashMap::new();
888 map.insert(
889 para_b,
890 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
891 );
892
893 map.insert(
894 para_c,
895 OutboundHrmpChannelModification { bytes_submitted: 100, messages_submitted: 5 },
896 );
897
898 map
899 },
900 ump_messages_sent: 6,
901 ump_bytes_sent: 1000,
902 dmp_messages_processed: 5,
903 code_upgrade_applied: true,
904 };
905
906 let mut c = a.clone();
907 c.stack(&b);
908
909 assert_eq!(
910 c,
911 ConstraintModifications {
912 required_parent: None,
913 hrmp_watermark: None,
914 outbound_hrmp: {
915 let mut map = HashMap::new();
916 map.insert(
917 para_a,
918 OutboundHrmpChannelModification {
919 bytes_submitted: 100,
920 messages_submitted: 5,
921 },
922 );
923
924 map.insert(
925 para_b,
926 OutboundHrmpChannelModification {
927 bytes_submitted: 200,
928 messages_submitted: 10,
929 },
930 );
931
932 map.insert(
933 para_c,
934 OutboundHrmpChannelModification {
935 bytes_submitted: 100,
936 messages_submitted: 5,
937 },
938 );
939
940 map
941 },
942 ump_messages_sent: 12,
943 ump_bytes_sent: 2000,
944 dmp_messages_processed: 10,
945 code_upgrade_applied: true,
946 },
947 );
948
949 let mut d = ConstraintModifications::identity();
950 d.stack(&a);
951 d.stack(&b);
952
953 assert_eq!(c, d);
954 }
955
956 fn make_constraints() -> Constraints {
957 let para_a = ParaId::from(1u32);
958 let para_b = ParaId::from(2u32);
959 let para_c = ParaId::from(3u32);
960
961 Constraints {
962 min_relay_parent_number: 5,
963 max_pov_size: 1000,
964 max_code_size: 1000,
965 ump_remaining: 10,
966 ump_remaining_bytes: 1024,
967 max_ump_num_per_candidate: 5,
968 dmp_remaining_messages: Vec::new(),
969 hrmp_inbound: InboundHrmpLimitations { valid_watermarks: vec![6, 8] },
970 hrmp_channels_out: {
971 let mut map = HashMap::new();
972
973 map.insert(
974 para_a,
975 OutboundHrmpChannelLimitations { messages_remaining: 5, bytes_remaining: 512 },
976 );
977
978 map.insert(
979 para_b,
980 OutboundHrmpChannelLimitations {
981 messages_remaining: 10,
982 bytes_remaining: 1024,
983 },
984 );
985
986 map.insert(
987 para_c,
988 OutboundHrmpChannelLimitations { messages_remaining: 1, bytes_remaining: 128 },
989 );
990
991 map
992 },
993 max_hrmp_num_per_candidate: 5,
994 required_parent: HeadData::from(vec![1, 2, 3]),
995 validation_code_hash: ValidationCode(vec![4, 5, 6]).hash(),
996 upgrade_restriction: None,
997 future_validation_code: None,
998 max_head_data_size: 1024,
999 }
1000 }
1001
1002 #[test]
1003 fn constraints_check_trunk_watermark() {
1004 let constraints = make_constraints();
1005 let mut modifications = ConstraintModifications::identity();
1006
1007 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(6));
1009 assert!(constraints.check_modifications(&modifications).is_ok());
1010 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
1011 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![6, 8]);
1012
1013 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(7));
1014 assert_eq!(
1015 constraints.check_modifications(&modifications),
1016 Err(ModificationError::DisallowedHrmpWatermark(7)),
1017 );
1018 assert_eq!(
1019 constraints.apply_modifications(&modifications),
1020 Err(ModificationError::DisallowedHrmpWatermark(7)),
1021 );
1022
1023 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Trunk(8));
1024 assert!(constraints.check_modifications(&modifications).is_ok());
1025 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
1026 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![8]);
1027 }
1028
1029 #[test]
1030 fn constraints_check_head_watermark() {
1031 let constraints = make_constraints();
1032 let mut modifications = ConstraintModifications::identity();
1033
1034 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Head(5));
1035 assert!(constraints.check_modifications(&modifications).is_ok());
1036 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
1037 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![6, 8]);
1038
1039 modifications.hrmp_watermark = Some(HrmpWatermarkUpdate::Head(7));
1040 assert!(constraints.check_modifications(&modifications).is_ok());
1041 let new_constraints = constraints.apply_modifications(&modifications).unwrap();
1042 assert_eq!(new_constraints.hrmp_inbound.valid_watermarks, vec![8]);
1043 }
1044
1045 #[test]
1046 fn constraints_no_such_hrmp_channel() {
1047 let constraints = make_constraints();
1048 let mut modifications = ConstraintModifications::identity();
1049 let bad_para = ParaId::from(100u32);
1050 modifications.outbound_hrmp.insert(
1051 bad_para,
1052 OutboundHrmpChannelModification { bytes_submitted: 0, messages_submitted: 0 },
1053 );
1054
1055 assert_eq!(
1056 constraints.check_modifications(&modifications),
1057 Err(ModificationError::NoSuchHrmpChannel(bad_para)),
1058 );
1059
1060 assert_eq!(
1061 constraints.apply_modifications(&modifications),
1062 Err(ModificationError::NoSuchHrmpChannel(bad_para)),
1063 );
1064 }
1065
1066 #[test]
1067 fn constraints_hrmp_messages_overflow() {
1068 let constraints = make_constraints();
1069 let mut modifications = ConstraintModifications::identity();
1070 let para_a = ParaId::from(1u32);
1071 modifications.outbound_hrmp.insert(
1072 para_a,
1073 OutboundHrmpChannelModification { bytes_submitted: 0, messages_submitted: 6 },
1074 );
1075
1076 assert_eq!(
1077 constraints.check_modifications(&modifications),
1078 Err(ModificationError::HrmpMessagesOverflow {
1079 para_id: para_a,
1080 messages_remaining: 5,
1081 messages_submitted: 6,
1082 }),
1083 );
1084
1085 assert_eq!(
1086 constraints.apply_modifications(&modifications),
1087 Err(ModificationError::HrmpMessagesOverflow {
1088 para_id: para_a,
1089 messages_remaining: 5,
1090 messages_submitted: 6,
1091 }),
1092 );
1093 }
1094
1095 #[test]
1096 fn constraints_hrmp_bytes_overflow() {
1097 let constraints = make_constraints();
1098 let mut modifications = ConstraintModifications::identity();
1099 let para_a = ParaId::from(1u32);
1100 modifications.outbound_hrmp.insert(
1101 para_a,
1102 OutboundHrmpChannelModification { bytes_submitted: 513, messages_submitted: 1 },
1103 );
1104
1105 assert_eq!(
1106 constraints.check_modifications(&modifications),
1107 Err(ModificationError::HrmpBytesOverflow {
1108 para_id: para_a,
1109 bytes_remaining: 512,
1110 bytes_submitted: 513,
1111 }),
1112 );
1113
1114 assert_eq!(
1115 constraints.apply_modifications(&modifications),
1116 Err(ModificationError::HrmpBytesOverflow {
1117 para_id: para_a,
1118 bytes_remaining: 512,
1119 bytes_submitted: 513,
1120 }),
1121 );
1122 }
1123
1124 #[test]
1125 fn constraints_ump_messages_overflow() {
1126 let constraints = make_constraints();
1127 let mut modifications = ConstraintModifications::identity();
1128 modifications.ump_messages_sent = 11;
1129
1130 assert_eq!(
1131 constraints.check_modifications(&modifications),
1132 Err(ModificationError::UmpMessagesOverflow {
1133 messages_remaining: 10,
1134 messages_submitted: 11,
1135 }),
1136 );
1137
1138 assert_eq!(
1139 constraints.apply_modifications(&modifications),
1140 Err(ModificationError::UmpMessagesOverflow {
1141 messages_remaining: 10,
1142 messages_submitted: 11,
1143 }),
1144 );
1145 }
1146
1147 #[test]
1148 fn constraints_ump_bytes_overflow() {
1149 let constraints = make_constraints();
1150 let mut modifications = ConstraintModifications::identity();
1151 modifications.ump_bytes_sent = 1025;
1152
1153 assert_eq!(
1154 constraints.check_modifications(&modifications),
1155 Err(ModificationError::UmpBytesOverflow {
1156 bytes_remaining: 1024,
1157 bytes_submitted: 1025,
1158 }),
1159 );
1160
1161 assert_eq!(
1162 constraints.apply_modifications(&modifications),
1163 Err(ModificationError::UmpBytesOverflow {
1164 bytes_remaining: 1024,
1165 bytes_submitted: 1025,
1166 }),
1167 );
1168 }
1169
1170 #[test]
1171 fn constraints_dmp_messages() {
1172 let mut constraints = make_constraints();
1173 let mut modifications = ConstraintModifications::identity();
1174 assert!(constraints.check_modifications(&modifications).is_ok());
1175 assert!(constraints.apply_modifications(&modifications).is_ok());
1176
1177 modifications.dmp_messages_processed = 6;
1178
1179 assert_eq!(
1180 constraints.check_modifications(&modifications),
1181 Err(ModificationError::DmpMessagesUnderflow {
1182 messages_remaining: 0,
1183 messages_processed: 6,
1184 }),
1185 );
1186
1187 assert_eq!(
1188 constraints.apply_modifications(&modifications),
1189 Err(ModificationError::DmpMessagesUnderflow {
1190 messages_remaining: 0,
1191 messages_processed: 6,
1192 }),
1193 );
1194
1195 constraints.dmp_remaining_messages = vec![1, 4, 8, 10];
1196 modifications.dmp_messages_processed = 2;
1197 assert!(constraints.check_modifications(&modifications).is_ok());
1198 let constraints = constraints
1199 .apply_modifications(&modifications)
1200 .expect("modifications are valid");
1201
1202 assert_eq!(&constraints.dmp_remaining_messages, &[8, 10]);
1203 }
1204
1205 #[test]
1206 fn constraints_nonexistent_code_upgrade() {
1207 let constraints = make_constraints();
1208 let mut modifications = ConstraintModifications::identity();
1209 modifications.code_upgrade_applied = true;
1210
1211 assert_eq!(
1212 constraints.check_modifications(&modifications),
1213 Err(ModificationError::AppliedNonexistentCodeUpgrade),
1214 );
1215
1216 assert_eq!(
1217 constraints.apply_modifications(&modifications),
1218 Err(ModificationError::AppliedNonexistentCodeUpgrade),
1219 );
1220 }
1221
1222 fn make_candidate(
1223 constraints: &Constraints,
1224 relay_parent: &RelayChainBlockInfo,
1225 ) -> ProspectiveCandidate {
1226 ProspectiveCandidate {
1227 commitments: CandidateCommitments {
1228 upward_messages: Default::default(),
1229 horizontal_messages: Default::default(),
1230 new_validation_code: None,
1231 head_data: HeadData::from(vec![1, 2, 3, 4, 5]),
1232 processed_downward_messages: 0,
1233 hrmp_watermark: relay_parent.number,
1234 },
1235 persisted_validation_data: PersistedValidationData {
1236 parent_head: constraints.required_parent.clone(),
1237 relay_parent_number: relay_parent.number,
1238 relay_parent_storage_root: relay_parent.storage_root,
1239 max_pov_size: constraints.max_pov_size as u32,
1240 },
1241 pov_hash: Hash::repeat_byte(1),
1242 validation_code_hash: constraints.validation_code_hash,
1243 }
1244 }
1245
1246 #[test]
1247 fn fragment_validation_code_mismatch() {
1248 let relay_parent = RelayChainBlockInfo {
1249 number: 6,
1250 hash: Hash::repeat_byte(0x0a),
1251 storage_root: Hash::repeat_byte(0xff),
1252 };
1253
1254 let constraints = make_constraints();
1255 let mut candidate = make_candidate(&constraints, &relay_parent);
1256
1257 let expected_code = constraints.validation_code_hash;
1258 let got_code = ValidationCode(vec![9, 9, 9]).hash();
1259
1260 candidate.validation_code_hash = got_code;
1261
1262 assert_eq!(
1263 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1264 Err(FragmentValidityError::ValidationCodeMismatch(expected_code, got_code,)),
1265 )
1266 }
1267
1268 #[test]
1269 fn fragment_pvd_mismatch() {
1270 let relay_parent = RelayChainBlockInfo {
1271 number: 6,
1272 hash: Hash::repeat_byte(0x0a),
1273 storage_root: Hash::repeat_byte(0xff),
1274 };
1275
1276 let relay_parent_b = RelayChainBlockInfo {
1277 number: 6,
1278 hash: Hash::repeat_byte(0x0b),
1279 storage_root: Hash::repeat_byte(0xee),
1280 };
1281
1282 let constraints = make_constraints();
1283 let candidate = make_candidate(&constraints, &relay_parent);
1284
1285 let expected_pvd = PersistedValidationData {
1286 parent_head: constraints.required_parent.clone(),
1287 relay_parent_number: relay_parent_b.number,
1288 relay_parent_storage_root: relay_parent_b.storage_root,
1289 max_pov_size: constraints.max_pov_size as u32,
1290 };
1291
1292 let got_pvd = candidate.persisted_validation_data.clone();
1293
1294 assert_eq!(
1295 Fragment::new(relay_parent_b, constraints, Arc::new(candidate.clone())),
1296 Err(FragmentValidityError::PersistedValidationDataMismatch(expected_pvd, got_pvd,)),
1297 );
1298 }
1299
1300 #[test]
1301 fn fragment_code_size_too_large() {
1302 let relay_parent = RelayChainBlockInfo {
1303 number: 6,
1304 hash: Hash::repeat_byte(0x0a),
1305 storage_root: Hash::repeat_byte(0xff),
1306 };
1307
1308 let constraints = make_constraints();
1309 let mut candidate = make_candidate(&constraints, &relay_parent);
1310
1311 let max_code_size = constraints.max_code_size;
1312 candidate.commitments.new_validation_code = Some(vec![0; max_code_size + 1].into());
1313
1314 assert_eq!(
1315 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1316 Err(FragmentValidityError::CodeSizeTooLarge(max_code_size, max_code_size + 1,)),
1317 );
1318 }
1319
1320 #[test]
1321 fn ump_signals_ignored() {
1322 let relay_parent = RelayChainBlockInfo {
1323 number: 6,
1324 hash: Hash::repeat_byte(0xbe),
1325 storage_root: Hash::repeat_byte(0xff),
1326 };
1327
1328 let constraints = make_constraints();
1329 let mut candidate = make_candidate(&constraints, &relay_parent);
1330 let max_ump = constraints.max_ump_num_per_candidate;
1331
1332 candidate
1334 .commitments
1335 .upward_messages
1336 .try_extend((0..max_ump).map(|i| vec![i as u8]))
1337 .unwrap();
1338
1339 candidate.commitments.upward_messages.force_push(UMP_SEPARATOR);
1341 candidate
1342 .commitments
1343 .upward_messages
1344 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1345
1346 Fragment::new(relay_parent, constraints, Arc::new(candidate)).unwrap();
1347 }
1348
1349 #[test]
1350 fn fragment_relay_parent_too_old() {
1351 let relay_parent = RelayChainBlockInfo {
1352 number: 3,
1353 hash: Hash::repeat_byte(0x0a),
1354 storage_root: Hash::repeat_byte(0xff),
1355 };
1356
1357 let constraints = make_constraints();
1358 let candidate = make_candidate(&constraints, &relay_parent);
1359
1360 assert_eq!(
1361 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1362 Err(FragmentValidityError::RelayParentTooOld(5, 3,)),
1363 );
1364 }
1365
1366 #[test]
1367 fn fragment_hrmp_messages_overflow() {
1368 let relay_parent = RelayChainBlockInfo {
1369 number: 6,
1370 hash: Hash::repeat_byte(0x0a),
1371 storage_root: Hash::repeat_byte(0xff),
1372 };
1373
1374 let constraints = make_constraints();
1375 let mut candidate = make_candidate(&constraints, &relay_parent);
1376
1377 let max_hrmp = constraints.max_hrmp_num_per_candidate;
1378
1379 candidate
1380 .commitments
1381 .horizontal_messages
1382 .try_extend((0..max_hrmp + 1).map(|i| OutboundHrmpMessage {
1383 recipient: ParaId::from(i as u32),
1384 data: vec![1, 2, 3],
1385 }))
1386 .unwrap();
1387
1388 assert_eq!(
1389 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1390 Err(FragmentValidityError::HrmpMessagesPerCandidateOverflow {
1391 messages_allowed: max_hrmp,
1392 messages_submitted: max_hrmp + 1,
1393 }),
1394 );
1395 }
1396
1397 #[test]
1398 fn fragment_dmp_advancement_rule() {
1399 let relay_parent = RelayChainBlockInfo {
1400 number: 6,
1401 hash: Hash::repeat_byte(0x0a),
1402 storage_root: Hash::repeat_byte(0xff),
1403 };
1404
1405 let mut constraints = make_constraints();
1406 let mut candidate = make_candidate(&constraints, &relay_parent);
1407
1408 assert!(Fragment::new(
1410 relay_parent.clone(),
1411 constraints.clone(),
1412 Arc::new(candidate.clone())
1413 )
1414 .is_ok());
1415 constraints.dmp_remaining_messages = vec![relay_parent.number + 1];
1417 assert!(Fragment::new(
1418 relay_parent.clone(),
1419 constraints.clone(),
1420 Arc::new(candidate.clone())
1421 )
1422 .is_ok());
1423
1424 for block_number in 0..=relay_parent.number {
1425 constraints.dmp_remaining_messages = vec![block_number];
1426
1427 assert_eq!(
1428 Fragment::new(
1429 relay_parent.clone(),
1430 constraints.clone(),
1431 Arc::new(candidate.clone())
1432 ),
1433 Err(FragmentValidityError::DmpAdvancementRule),
1434 );
1435 }
1436
1437 candidate.commitments.processed_downward_messages = 1;
1438 assert!(Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())).is_ok());
1439 }
1440
1441 #[test]
1442 fn fragment_ump_messages_overflow() {
1443 let relay_parent = RelayChainBlockInfo {
1444 number: 6,
1445 hash: Hash::repeat_byte(0x0a),
1446 storage_root: Hash::repeat_byte(0xff),
1447 };
1448
1449 let constraints = make_constraints();
1450 let mut candidate = make_candidate(&constraints, &relay_parent);
1451
1452 let max_ump = constraints.max_ump_num_per_candidate;
1453
1454 candidate
1455 .commitments
1456 .upward_messages
1457 .try_extend((0..max_ump + 1).map(|i| vec![i as u8]))
1458 .unwrap();
1459
1460 assert_eq!(
1461 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1462 Err(FragmentValidityError::UmpMessagesPerCandidateOverflow {
1463 messages_allowed: max_ump,
1464 messages_submitted: max_ump + 1,
1465 }),
1466 );
1467 }
1468
1469 #[test]
1470 fn fragment_code_upgrade_restricted() {
1471 let relay_parent = RelayChainBlockInfo {
1472 number: 6,
1473 hash: Hash::repeat_byte(0x0a),
1474 storage_root: Hash::repeat_byte(0xff),
1475 };
1476
1477 let mut constraints = make_constraints();
1478 let mut candidate = make_candidate(&constraints, &relay_parent);
1479
1480 constraints.upgrade_restriction = Some(UpgradeRestriction::Present);
1481 candidate.commitments.new_validation_code = Some(ValidationCode(vec![1, 2, 3]));
1482
1483 assert_eq!(
1484 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1485 Err(FragmentValidityError::CodeUpgradeRestricted),
1486 );
1487 }
1488
1489 #[test]
1490 fn fragment_hrmp_messages_descending_or_duplicate() {
1491 let relay_parent = RelayChainBlockInfo {
1492 number: 6,
1493 hash: Hash::repeat_byte(0x0a),
1494 storage_root: Hash::repeat_byte(0xff),
1495 };
1496
1497 let constraints = make_constraints();
1498 let mut candidate = make_candidate(&constraints, &relay_parent);
1499
1500 candidate.commitments.horizontal_messages = HorizontalMessages::truncate_from(vec![
1501 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![1, 2, 3] },
1502 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![4, 5, 6] },
1503 ]);
1504
1505 assert_eq!(
1506 Fragment::new(relay_parent.clone(), constraints.clone(), Arc::new(candidate.clone())),
1507 Err(FragmentValidityError::HrmpMessagesDescendingOrDuplicate(1)),
1508 );
1509
1510 candidate.commitments.horizontal_messages = HorizontalMessages::truncate_from(vec![
1511 OutboundHrmpMessage { recipient: ParaId::from(1 as u32), data: vec![1, 2, 3] },
1512 OutboundHrmpMessage { recipient: ParaId::from(0 as u32), data: vec![4, 5, 6] },
1513 ]);
1514
1515 assert_eq!(
1516 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1517 Err(FragmentValidityError::HrmpMessagesDescendingOrDuplicate(1)),
1518 );
1519 }
1520
1521 #[test]
1522 fn head_data_size_too_large() {
1523 let relay_parent = RelayChainBlockInfo {
1524 number: 6,
1525 hash: Hash::repeat_byte(0xcc),
1526 storage_root: Hash::repeat_byte(0xff),
1527 };
1528
1529 let constraints = make_constraints();
1530 let mut candidate = make_candidate(&constraints, &relay_parent);
1531
1532 let head_data_size = constraints.max_head_data_size;
1533 candidate.commitments.head_data = vec![0; head_data_size + 1].into();
1534
1535 assert_eq!(
1536 Fragment::new(relay_parent, constraints, Arc::new(candidate.clone())),
1537 Err(FragmentValidityError::HeadDataTooLarge(head_data_size, head_data_size + 1)),
1538 );
1539 }
1540}