referrerpolicy=no-referrer-when-downgrade

polkadot_node_subsystem_util/inclusion_emulator/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14/// # Overview
15///
16/// A set of utilities for node-side code to emulate the logic the runtime uses for checking
17/// parachain blocks in order to build prospective parachains that are produced ahead of the
18/// relay chain. These utilities allow the node-side to predict, with high accuracy, what
19/// the relay-chain will accept in the near future.
20///
21/// This module has 2 key data types: [`Constraints`] and [`Fragment`]s. [`Constraints`]
22/// exhaustively define the set of valid inputs and outputs to parachain execution. A
23/// [`Fragment`] indicates a parachain block, anchored to the relay-chain at a particular
24/// relay-chain block, known as the relay-parent.
25///
26/// ## Fragment Validity
27///
28/// Every relay-parent is implicitly associated with a unique set of [`Constraints`] that
29/// describe the properties that must be true for a block to be included in a direct child of
30/// that block, assuming there is no intermediate parachain block pending availability.
31///
32/// However, the key factor that makes asynchronously-grown prospective chains
33/// possible is the fact that the relay-chain accepts candidate blocks based on whether they
34/// are valid under the constraints of the present moment, not based on whether they were
35/// valid at the time of construction.
36///
37/// As such, [`Fragment`]s are often, but not always constructed in such a way that they are
38/// invalid at first and become valid later on, as the relay chain grows.
39///
40/// # Usage
41///
42/// It's expected that the users of this module will be building up chains or trees of
43/// [`Fragment`]s and consistently pruning and adding to them.
44///
45/// ## Operating Constraints
46///
47/// The *operating constraints* of a `Fragment` are the constraints with which that fragment
48/// was intended to comply. The operating constraints are defined as the base constraints
49/// of the relay-parent of the fragment modified by the cumulative modifications of all
50/// fragments between the relay-parent and the current fragment.
51///
52/// What the operating constraints are, in practice, is a prediction about the state of the
53/// relay-chain in the future. The relay-chain is aware of some current state, and we want to
54/// make an intelligent prediction about what might be accepted in the future based on
55/// prior fragments that also exist off-chain.
56///
57/// ## Fragment Chains
58///
59/// For the sake of this module, we don't care how higher-level code is managing parachain
60/// fragments, whether or not they're kept as a chain or tree. In reality,
61/// prospective-parachains is maintaining for every active leaf, a chain of the "best" backable
62/// candidates and a storage of potential candidates which may be added to this chain in the
63/// future.
64///
65/// As the relay-chain grows, some predictions come true and others come false.
66/// And new predictions get made. Higher-level code is responsible for adding and pruning the
67/// fragments chains.
68///
69/// Avoiding fragment-chain blowup is beyond the scope of this module. Higher-level must ensure
70/// proper spam protection.
71///
72/// ### Code Upgrades
73///
74/// Code upgrades are the main place where this emulation fails. The on-chain PVF upgrade
75/// scheduling logic is very path-dependent and intricate so we just assume that code upgrades
76/// can't be initiated and applied within a single fragment-chain. Fragment-chains aren't deep,
77/// in practice (bounded by a linear function of the the number of cores assigned to a
78/// parachain) and code upgrades are fairly rare. So what's likely to happen around code
79/// upgrades is that the entire fragment-chain has to get discarded at some point.
80///
81/// That means a few blocks of execution time lost, which is not a big deal for code upgrades
82/// in practice at most once every few weeks.
83use 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/// Constraints on inbound HRMP channels.
91#[derive(Debug, Clone, PartialEq)]
92pub struct InboundHrmpLimitations {
93	/// An exhaustive set of all valid watermarks, sorted ascending
94	pub valid_watermarks: Vec<BlockNumber>,
95}
96
97/// Constraints on outbound HRMP channels.
98#[derive(Debug, Clone, PartialEq)]
99pub struct OutboundHrmpChannelLimitations {
100	/// The maximum bytes that can be written to the channel.
101	pub bytes_remaining: usize,
102	/// The maximum messages that can be written to the channel.
103	pub messages_remaining: usize,
104}
105
106/// Constraints on the actions that can be taken by a new parachain
107/// block. These limitations are implicitly associated with some particular
108/// parachain, which should be apparent from usage.
109#[derive(Debug, Clone, PartialEq)]
110pub struct Constraints {
111	/// The minimum relay-parent number accepted under these constraints.
112	pub min_relay_parent_number: BlockNumber,
113	/// The maximum Proof-of-Validity size allowed, in bytes.
114	pub max_pov_size: usize,
115	/// The maximum new validation code size allowed, in bytes.
116	pub max_code_size: usize,
117	/// The maximum head-data size, in bytes.
118	pub max_head_data_size: usize,
119	/// The amount of UMP messages remaining.
120	pub ump_remaining: usize,
121	/// The amount of UMP bytes remaining.
122	pub ump_remaining_bytes: usize,
123	/// The maximum number of UMP messages allowed per candidate.
124	pub max_ump_num_per_candidate: usize,
125	/// Remaining DMP queue. Only includes sent-at block numbers.
126	pub dmp_remaining_messages: Vec<BlockNumber>,
127	/// The limitations of all registered inbound HRMP channels.
128	pub hrmp_inbound: InboundHrmpLimitations,
129	/// The limitations of all registered outbound HRMP channels.
130	pub hrmp_channels_out: HashMap<ParaId, OutboundHrmpChannelLimitations>,
131	/// The maximum number of HRMP messages allowed per candidate.
132	pub max_hrmp_num_per_candidate: usize,
133	/// The required parent head-data of the parachain.
134	pub required_parent: HeadData,
135	/// The expected validation-code-hash of this parachain.
136	pub validation_code_hash: ValidationCodeHash,
137	/// The code upgrade restriction signal as-of this parachain.
138	pub upgrade_restriction: Option<UpgradeRestriction>,
139	/// The future validation code hash, if any, and at what relay-parent
140	/// number the upgrade would be minimally applied.
141	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/// Kinds of errors that can occur when modifying constraints.
181#[derive(Debug, Clone, PartialEq)]
182pub enum ModificationError {
183	/// The HRMP watermark is not allowed.
184	DisallowedHrmpWatermark(BlockNumber),
185	/// No such HRMP outbound channel.
186	NoSuchHrmpChannel(ParaId),
187	/// Too many messages submitted to HRMP channel.
188	HrmpMessagesOverflow {
189		/// The ID of the recipient.
190		para_id: ParaId,
191		/// The amount of remaining messages in the capacity of the channel.
192		messages_remaining: usize,
193		/// The amount of messages submitted to the channel.
194		messages_submitted: usize,
195	},
196	/// Too many bytes submitted to HRMP channel.
197	HrmpBytesOverflow {
198		/// The ID of the recipient.
199		para_id: ParaId,
200		/// The amount of remaining bytes in the capacity of the channel.
201		bytes_remaining: usize,
202		/// The amount of bytes submitted to the channel.
203		bytes_submitted: usize,
204	},
205	/// Too many messages submitted to UMP.
206	UmpMessagesOverflow {
207		/// The amount of remaining messages in the capacity of UMP.
208		messages_remaining: usize,
209		/// The amount of messages submitted to UMP.
210		messages_submitted: usize,
211	},
212	/// Too many bytes submitted to UMP.
213	UmpBytesOverflow {
214		/// The amount of remaining bytes in the capacity of UMP.
215		bytes_remaining: usize,
216		/// The amount of bytes submitted to UMP.
217		bytes_submitted: usize,
218	},
219	/// Too many messages processed from DMP.
220	DmpMessagesUnderflow {
221		/// The amount of messages waiting to be processed from DMP.
222		messages_remaining: usize,
223		/// The amount of messages processed.
224		messages_processed: usize,
225	},
226	/// No validation code upgrade to apply.
227	AppliedNonexistentCodeUpgrade,
228}
229
230impl Constraints {
231	/// Check modifications against constraints.
232	pub fn check_modifications(
233		&self,
234		modifications: &ConstraintModifications,
235	) -> Result<(), ModificationError> {
236		if let Some(HrmpWatermarkUpdate::Trunk(hrmp_watermark)) = modifications.hrmp_watermark {
237			// head updates are always valid.
238			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	/// Apply modifications to these constraints. If this succeeds, it passes
296	/// all sanity-checks.
297	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					// Exact match, so this is OK in all cases.
311					let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
312				},
313				Err(pos) => match hrmp_watermark {
314					HrmpWatermarkUpdate::Head(_) => {
315						// Updates to Head are always OK.
316						let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
317					},
318					HrmpWatermarkUpdate::Trunk(n) => {
319						// Trunk update landing on disallowed watermark is not OK.
320						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/// Information about a relay-chain block.
388#[derive(Debug, Clone, PartialEq)]
389pub struct RelayChainBlockInfo {
390	/// The hash of the relay-chain block.
391	pub hash: Hash,
392	/// The number of the relay-chain block.
393	pub number: BlockNumber,
394	/// The storage-root of the relay-chain block.
395	pub storage_root: Hash,
396}
397
398/// An update to outbound HRMP channels.
399#[derive(Debug, Clone, PartialEq, Default)]
400pub struct OutboundHrmpChannelModification {
401	/// The number of bytes submitted to the channel.
402	pub bytes_submitted: usize,
403	/// The number of messages submitted to the channel.
404	pub messages_submitted: usize,
405}
406
407/// An update to the HRMP Watermark.
408#[derive(Debug, Clone, PartialEq)]
409pub enum HrmpWatermarkUpdate {
410	/// This is an update placing the watermark at the head of the chain,
411	/// which is always legal.
412	Head(BlockNumber),
413	/// This is an update placing the watermark behind the head of the
414	/// chain, which is only legal if it lands on a block where messages
415	/// were queued.
416	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/// Modifications to constraints as a result of prospective candidates.
428#[derive(Debug, Clone, PartialEq)]
429pub struct ConstraintModifications {
430	/// The required parent head to build upon.
431	pub required_parent: Option<HeadData>,
432	/// The new HRMP watermark
433	pub hrmp_watermark: Option<HrmpWatermarkUpdate>,
434	/// Outbound HRMP channel modifications.
435	pub outbound_hrmp: HashMap<ParaId, OutboundHrmpChannelModification>,
436	/// The amount of UMP XCM messages sent. `UMPSignal` and separator are excluded.
437	pub ump_messages_sent: usize,
438	/// The amount of UMP XCM bytes sent. `UMPSignal` and separator are excluded.
439	pub ump_bytes_sent: usize,
440	/// The amount of DMP messages processed.
441	pub dmp_messages_processed: usize,
442	/// Whether a pending code upgrade has been applied.
443	pub code_upgrade_applied: bool,
444}
445
446impl ConstraintModifications {
447	/// The 'identity' modifications: these can be applied to
448	/// any constraints and yield the exact same result.
449	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	/// Stack other modifications on top of these.
462	///
463	/// This does no sanity-checking, so if `other` is garbage relative
464	/// to `self`, then the new value will be garbage as well.
465	///
466	/// This is an addition which is not commutative.
467	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/// The prospective candidate.
489///
490/// This comprises the key information that represent a candidate
491/// without pinning it to a particular session. For example commitments are
492/// represented here. But the erasure-root is not. This means that prospective candidates
493/// are not correlated to any session in particular.
494#[derive(Debug, Clone, PartialEq)]
495pub struct ProspectiveCandidate {
496	/// The commitments to the output of the execution.
497	pub commitments: CandidateCommitments,
498	/// The persisted validation data used to create the candidate.
499	pub persisted_validation_data: PersistedValidationData,
500	/// The hash of the PoV.
501	pub pov_hash: Hash,
502	/// The validation code hash used by the candidate.
503	pub validation_code_hash: ValidationCodeHash,
504}
505
506/// Kinds of errors with the validity of a fragment.
507#[derive(Debug, Clone, PartialEq)]
508pub enum FragmentValidityError {
509	/// The validation code of the candidate doesn't match the
510	/// operating constraints.
511	///
512	/// Expected, Got
513	ValidationCodeMismatch(ValidationCodeHash, ValidationCodeHash),
514	/// The persisted-validation-data doesn't match.
515	///
516	/// Expected, Got
517	PersistedValidationDataMismatch(PersistedValidationData, PersistedValidationData),
518	/// The outputs of the candidate are invalid under the operating
519	/// constraints.
520	OutputsInvalid(ModificationError),
521	/// New validation code size too big.
522	///
523	/// Max allowed, new.
524	CodeSizeTooLarge(usize, usize),
525	/// Head data size too big.
526	///
527	/// Max allowed, new.
528	HeadDataTooLarge(usize, usize),
529	/// Relay parent too old.
530	///
531	/// Min allowed, current.
532	RelayParentTooOld(BlockNumber, BlockNumber),
533	/// Para is required to process at least one DMP message from the queue.
534	DmpAdvancementRule,
535	/// Too many messages upward messages submitted.
536	UmpMessagesPerCandidateOverflow {
537		/// The amount of messages a single candidate can submit.
538		messages_allowed: usize,
539		/// The amount of messages sent to all HRMP channels.
540		messages_submitted: usize,
541	},
542	/// Too many messages submitted to all HRMP channels.
543	HrmpMessagesPerCandidateOverflow {
544		/// The amount of messages a single candidate can submit.
545		messages_allowed: usize,
546		/// The amount of messages sent to all HRMP channels.
547		messages_submitted: usize,
548	},
549	/// Code upgrade not allowed.
550	CodeUpgradeRestricted,
551	/// HRMP messages are not ascending or are duplicate.
552	///
553	/// The `usize` is the index into the outbound HRMP messages of
554	/// the candidate.
555	HrmpMessagesDescendingOrDuplicate(usize),
556}
557
558/// A parachain fragment, representing another prospective parachain block.
559///
560/// This is a type which guarantees that the candidate is valid under the
561/// operating constraints.
562#[derive(Debug, Clone, PartialEq)]
563pub struct Fragment {
564	/// The new relay-parent.
565	relay_parent: RelayChainBlockInfo,
566	/// The constraints this fragment is operating under.
567	operating_constraints: Constraints,
568	/// The core information about the prospective candidate.
569	candidate: Arc<ProspectiveCandidate>,
570	/// Modifications to the constraints based on the outputs of
571	/// the candidate.
572	modifications: ConstraintModifications,
573}
574
575impl Fragment {
576	/// Create a new fragment.
577	///
578	/// This fails if the fragment isn't in line with the operating
579	/// constraints. That is, either its inputs or its outputs fail
580	/// checks against the constraints.
581	///
582	/// This doesn't check that the collator signature is valid or
583	/// whether the PoV is small enough.
584	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	/// Check the candidate against the operating constrains and return the constraint modifications
601	/// made by this candidate.
602	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		// Filter UMP signals and the separator.
610		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	/// Access the relay parent information.
670	pub fn relay_parent(&self) -> &RelayChainBlockInfo {
671		&self.relay_parent
672	}
673
674	/// Access the operating constraints
675	pub fn operating_constraints(&self) -> &Constraints {
676		&self.operating_constraints
677	}
678
679	/// Access the underlying prospective candidate.
680	pub fn candidate(&self) -> &ProspectiveCandidate {
681		&self.candidate
682	}
683
684	/// Get a cheap ref-counted copy of the underlying prospective candidate.
685	pub fn candidate_clone(&self) -> Arc<ProspectiveCandidate> {
686		self.candidate.clone()
687	}
688
689	/// Modifications to constraints based on the outputs of the candidate.
690	pub fn constraint_modifications(&self) -> &ConstraintModifications {
691		&self.modifications
692	}
693}
694
695/// Validates if the candidate commitments are obeying the constraints.
696pub 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		// The current hrmp watermark is kept
959		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		// Fill ump queue to the limit.
1284		candidate
1285			.commitments
1286			.upward_messages
1287			.try_extend((0..max_ump).map(|i| vec![i as u8]))
1288			.unwrap();
1289
1290		// Add ump signals.
1291		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		// Empty dmp queue is ok.
1360		assert!(Fragment::new(
1361			relay_parent.clone(),
1362			constraints.clone(),
1363			Arc::new(candidate.clone())
1364		)
1365		.is_ok());
1366		// Unprocessed message that was sent later is ok.
1367		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}