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_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/// Constraints on inbound HRMP channels.
92#[derive(Debug, Clone, PartialEq)]
93pub struct InboundHrmpLimitations {
94	/// An exhaustive set of all valid watermarks, sorted ascending
95	pub valid_watermarks: Vec<BlockNumber>,
96}
97
98/// Constraints on outbound HRMP channels.
99#[derive(Debug, Clone, PartialEq)]
100pub struct OutboundHrmpChannelLimitations {
101	/// The maximum bytes that can be written to the channel.
102	pub bytes_remaining: usize,
103	/// The maximum messages that can be written to the channel.
104	pub messages_remaining: usize,
105}
106
107/// Constraints on the actions that can be taken by a new parachain
108/// block. These limitations are implicitly associated with some particular
109/// parachain, which should be apparent from usage.
110#[derive(Debug, Clone, PartialEq)]
111pub struct Constraints {
112	/// The minimum relay-parent number accepted under these constraints.
113	pub min_relay_parent_number: BlockNumber,
114	/// The maximum Proof-of-Validity size allowed, in bytes.
115	pub max_pov_size: usize,
116	/// The maximum new validation code size allowed, in bytes.
117	pub max_code_size: usize,
118	/// The maximum head-data size, in bytes.
119	pub max_head_data_size: usize,
120	/// The amount of UMP messages remaining.
121	pub ump_remaining: usize,
122	/// The amount of UMP bytes remaining.
123	pub ump_remaining_bytes: usize,
124	/// The maximum number of UMP messages allowed per candidate.
125	pub max_ump_num_per_candidate: usize,
126	/// Remaining DMP queue. Only includes sent-at block numbers.
127	pub dmp_remaining_messages: Vec<BlockNumber>,
128	/// The limitations of all registered inbound HRMP channels.
129	pub hrmp_inbound: InboundHrmpLimitations,
130	/// The limitations of all registered outbound HRMP channels.
131	pub hrmp_channels_out: HashMap<ParaId, OutboundHrmpChannelLimitations>,
132	/// The maximum number of HRMP messages allowed per candidate.
133	pub max_hrmp_num_per_candidate: usize,
134	/// The required parent head-data of the parachain.
135	pub required_parent: HeadData,
136	/// The expected validation-code-hash of this parachain.
137	pub validation_code_hash: ValidationCodeHash,
138	/// The code upgrade restriction signal as-of this parachain.
139	pub upgrade_restriction: Option<UpgradeRestriction>,
140	/// The future validation code hash, if any, and at what relay-parent
141	/// number the upgrade would be minimally applied.
142	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/// Kinds of errors that can occur when modifying constraints.
182#[derive(Debug, Clone, PartialEq)]
183pub enum ModificationError {
184	/// The HRMP watermark is not allowed.
185	DisallowedHrmpWatermark(BlockNumber),
186	/// No such HRMP outbound channel.
187	NoSuchHrmpChannel(ParaId),
188	/// Too many messages submitted to HRMP channel.
189	HrmpMessagesOverflow {
190		/// The ID of the recipient.
191		para_id: ParaId,
192		/// The amount of remaining messages in the capacity of the channel.
193		messages_remaining: usize,
194		/// The amount of messages submitted to the channel.
195		messages_submitted: usize,
196	},
197	/// Too many bytes submitted to HRMP channel.
198	HrmpBytesOverflow {
199		/// The ID of the recipient.
200		para_id: ParaId,
201		/// The amount of remaining bytes in the capacity of the channel.
202		bytes_remaining: usize,
203		/// The amount of bytes submitted to the channel.
204		bytes_submitted: usize,
205	},
206	/// Too many messages submitted to UMP.
207	UmpMessagesOverflow {
208		/// The amount of remaining messages in the capacity of UMP.
209		messages_remaining: usize,
210		/// The amount of messages submitted to UMP.
211		messages_submitted: usize,
212	},
213	/// Too many bytes submitted to UMP.
214	UmpBytesOverflow {
215		/// The amount of remaining bytes in the capacity of UMP.
216		bytes_remaining: usize,
217		/// The amount of bytes submitted to UMP.
218		bytes_submitted: usize,
219	},
220	/// Too many messages processed from DMP.
221	DmpMessagesUnderflow {
222		/// The amount of messages waiting to be processed from DMP.
223		messages_remaining: usize,
224		/// The amount of messages processed.
225		messages_processed: usize,
226	},
227	/// No validation code upgrade to apply.
228	AppliedNonexistentCodeUpgrade,
229}
230
231impl Constraints {
232	/// Check modifications against constraints.
233	pub fn check_modifications(
234		&self,
235		modifications: &ConstraintModifications,
236	) -> Result<(), ModificationError> {
237		if let Some(HrmpWatermarkUpdate::Trunk(hrmp_watermark)) = modifications.hrmp_watermark {
238			// head updates are always valid.
239			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	/// Apply modifications to these constraints. If this succeeds, it passes
297	/// all sanity-checks.
298	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					// Exact match, so this is OK in all cases.
312					let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
313				},
314				Err(pos) => match hrmp_watermark {
315					HrmpWatermarkUpdate::Head(_) => {
316						// Updates to Head are always OK.
317						let _ = new.hrmp_inbound.valid_watermarks.drain(..pos);
318					},
319					HrmpWatermarkUpdate::Trunk(n) => {
320						// Trunk update landing on disallowed watermark is not OK.
321						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/// Information about a relay-chain block.
389#[derive(Debug, Clone, PartialEq)]
390pub struct RelayChainBlockInfo {
391	/// The hash of the relay-chain block.
392	pub hash: Hash,
393	/// The number of the relay-chain block.
394	pub number: BlockNumber,
395	/// The storage-root of the relay-chain block.
396	pub storage_root: Hash,
397}
398
399/// An update to outbound HRMP channels.
400#[derive(Debug, Clone, PartialEq, Default)]
401pub struct OutboundHrmpChannelModification {
402	/// The number of bytes submitted to the channel.
403	pub bytes_submitted: usize,
404	/// The number of messages submitted to the channel.
405	pub messages_submitted: usize,
406}
407
408/// An update to the HRMP Watermark.
409#[derive(Debug, Clone, PartialEq)]
410pub enum HrmpWatermarkUpdate {
411	/// This is an update placing the watermark at the head of the chain,
412	/// which is always legal.
413	Head(BlockNumber),
414	/// This is an update placing the watermark behind the head of the
415	/// chain, which is only legal if it lands on a block where messages
416	/// were queued.
417	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/// Modifications to constraints as a result of prospective candidates.
429#[derive(Debug, Clone, PartialEq)]
430pub struct ConstraintModifications {
431	/// The required parent head to build upon.
432	pub required_parent: Option<HeadData>,
433	/// The new HRMP watermark
434	pub hrmp_watermark: Option<HrmpWatermarkUpdate>,
435	/// Outbound HRMP channel modifications.
436	pub outbound_hrmp: HashMap<ParaId, OutboundHrmpChannelModification>,
437	/// The amount of UMP XCM messages sent. `UMPSignal` and separator are excluded.
438	pub ump_messages_sent: usize,
439	/// The amount of UMP XCM bytes sent. `UMPSignal` and separator are excluded.
440	pub ump_bytes_sent: usize,
441	/// The amount of DMP messages processed.
442	pub dmp_messages_processed: usize,
443	/// Whether a pending code upgrade has been applied.
444	pub code_upgrade_applied: bool,
445}
446
447impl ConstraintModifications {
448	/// The 'identity' modifications: these can be applied to
449	/// any constraints and yield the exact same result.
450	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	/// Stack other modifications on top of these.
463	///
464	/// This does no sanity-checking, so if `other` is garbage relative
465	/// to `self`, then the new value will be garbage as well.
466	///
467	/// This is an addition which is not commutative.
468	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/// The prospective candidate.
490///
491/// This comprises the key information that represent a candidate
492/// without pinning it to a particular session. For example commitments are
493/// represented here. But the erasure-root is not. This means that prospective candidates
494/// are not correlated to any session in particular.
495#[derive(Debug, Clone, PartialEq)]
496pub struct ProspectiveCandidate {
497	/// The commitments to the output of the execution.
498	pub commitments: CandidateCommitments,
499	/// The persisted validation data used to create the candidate.
500	pub persisted_validation_data: PersistedValidationData,
501	/// The hash of the PoV.
502	pub pov_hash: Hash,
503	/// The validation code hash used by the candidate.
504	pub validation_code_hash: ValidationCodeHash,
505}
506
507/// Kinds of errors with the validity of a fragment.
508#[derive(Debug, Clone, PartialEq)]
509pub enum FragmentValidityError {
510	/// The validation code of the candidate doesn't match the
511	/// operating constraints.
512	///
513	/// Expected, Got
514	ValidationCodeMismatch(ValidationCodeHash, ValidationCodeHash),
515	/// The persisted-validation-data doesn't match.
516	///
517	/// Expected, Got
518	PersistedValidationDataMismatch(PersistedValidationData, PersistedValidationData),
519	/// The outputs of the candidate are invalid under the operating
520	/// constraints.
521	OutputsInvalid(ModificationError),
522	/// New validation code size too big.
523	///
524	/// Max allowed, new.
525	CodeSizeTooLarge(usize, usize),
526	/// Head data size too big.
527	///
528	/// Max allowed, new.
529	HeadDataTooLarge(usize, usize),
530	/// Relay parent too old.
531	///
532	/// Min allowed, current.
533	RelayParentTooOld(BlockNumber, BlockNumber),
534	/// Para is required to process at least one DMP message from the queue.
535	DmpAdvancementRule,
536	/// Too many messages upward messages submitted.
537	UmpMessagesPerCandidateOverflow {
538		/// The amount of messages a single candidate can submit.
539		messages_allowed: usize,
540		/// The amount of messages sent to all HRMP channels.
541		messages_submitted: usize,
542	},
543	/// Too many messages submitted to all HRMP channels.
544	HrmpMessagesPerCandidateOverflow {
545		/// The amount of messages a single candidate can submit.
546		messages_allowed: usize,
547		/// The amount of messages sent to all HRMP channels.
548		messages_submitted: usize,
549	},
550	/// Code upgrade not allowed.
551	CodeUpgradeRestricted,
552	/// HRMP messages are not ascending or are duplicate.
553	///
554	/// The `usize` is the index into the outbound HRMP messages of
555	/// the candidate.
556	HrmpMessagesDescendingOrDuplicate(usize),
557}
558
559/// A parachain fragment, representing another prospective parachain block.
560///
561/// This is a type which guarantees that the candidate is valid under the
562/// operating constraints.
563#[derive(Debug, Clone, PartialEq)]
564pub struct Fragment {
565	/// The new relay-parent.
566	relay_parent: RelayChainBlockInfo,
567	/// The constraints this fragment is operating under.
568	operating_constraints: Constraints,
569	/// The core information about the prospective candidate.
570	candidate: Arc<ProspectiveCandidate>,
571	/// Modifications to the constraints based on the outputs of
572	/// the candidate.
573	modifications: ConstraintModifications,
574}
575
576impl Fragment {
577	/// Create a new fragment.
578	///
579	/// This fails if the fragment isn't in line with the operating
580	/// constraints. That is, either its inputs or its outputs fail
581	/// checks against the constraints.
582	///
583	/// This doesn't check that the collator signature is valid or
584	/// whether the PoV is small enough.
585	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	/// Check the candidate against the operating constrains and return the constraint modifications
602	/// made by this candidate.
603	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		// Filter UMP signals and the separator.
611		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	/// Access the relay parent information.
671	pub fn relay_parent(&self) -> &RelayChainBlockInfo {
672		&self.relay_parent
673	}
674
675	/// Access the operating constraints
676	pub fn operating_constraints(&self) -> &Constraints {
677		&self.operating_constraints
678	}
679
680	/// Access the underlying prospective candidate.
681	pub fn candidate(&self) -> &ProspectiveCandidate {
682		&self.candidate
683	}
684
685	/// Get a cheap ref-counted copy of the underlying prospective candidate.
686	pub fn candidate_clone(&self) -> Arc<ProspectiveCandidate> {
687		self.candidate.clone()
688	}
689
690	/// Modifications to constraints based on the outputs of the candidate.
691	pub fn constraint_modifications(&self) -> &ConstraintModifications {
692		&self.modifications
693	}
694}
695
696/// Validates if the candidate commitments are obeying the constraints.
697pub 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
796/// Trait for a hypothetical or concrete candidate, as needed when assessing the validity of a
797/// potential candidate.
798pub trait HypotheticalOrConcreteCandidate {
799	/// Return a reference to the candidate commitments, if present.
800	fn commitments(&self) -> Option<&CandidateCommitments>;
801	/// Return a reference to the persisted validation data, if present.
802	fn persisted_validation_data(&self) -> Option<&PersistedValidationData>;
803	/// Return a reference to the validation code hash, if present.
804	fn validation_code_hash(&self) -> Option<ValidationCodeHash>;
805	/// Return the parent head hash.
806	fn parent_head_data_hash(&self) -> Hash;
807	/// Return the output head hash, if present.
808	fn output_head_data_hash(&self) -> Option<Hash>;
809	/// Return the relay parent hash.
810	fn relay_parent(&self) -> Hash;
811	/// Return the candidate hash.
812	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		// The current hrmp watermark is kept
1008		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		// Fill ump queue to the limit.
1333		candidate
1334			.commitments
1335			.upward_messages
1336			.try_extend((0..max_ump).map(|i| vec![i as u8]))
1337			.unwrap();
1338
1339		// Add ump signals.
1340		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		// Empty dmp queue is ok.
1409		assert!(Fragment::new(
1410			relay_parent.clone(),
1411			constraints.clone(),
1412			Arc::new(candidate.clone())
1413		)
1414		.is_ok());
1415		// Unprocessed message that was sent later is ok.
1416		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}