polkadot_primitives/vstaging/
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// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Staging Primitives.
18use crate::{ValidatorIndex, ValidityAttestation};
19
20// Put any primitives used by staging APIs functions here
21use super::{
22	async_backing::{InboundHrmpLimitations, OutboundHrmpChannelLimitations},
23	BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId,
24	CollatorSignature, CoreIndex, GroupIndex, Hash, HashT, HeadData, Header, Id, Id as ParaId,
25	MultiDisputeStatementSet, ScheduledCore, UncheckedSignedAvailabilityBitfields,
26	UpgradeRestriction, ValidationCodeHash,
27};
28use alloc::{
29	collections::{BTreeMap, BTreeSet, VecDeque},
30	vec,
31	vec::Vec,
32};
33use bitvec::prelude::*;
34use codec::{Decode, DecodeWithMemTracking, Encode};
35use scale_info::TypeInfo;
36use sp_application_crypto::ByteArray;
37use sp_core::RuntimeDebug;
38use sp_runtime::traits::Header as HeaderT;
39use sp_staking::SessionIndex;
40/// Async backing primitives
41pub mod async_backing;
42
43/// The default claim queue offset to be used if it's not configured/accessible in the parachain
44/// runtime
45pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0;
46
47/// A type representing the version of the candidate descriptor and internal version number.
48#[derive(
49	PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, Clone, TypeInfo, RuntimeDebug, Copy,
50)]
51#[cfg_attr(feature = "std", derive(Hash))]
52pub struct InternalVersion(pub u8);
53
54/// A type representing the version of the candidate descriptor.
55#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)]
56#[cfg_attr(feature = "std", derive(Hash))]
57pub enum CandidateDescriptorVersion {
58	/// The old candidate descriptor version.
59	V1,
60	/// The new `CandidateDescriptorV2`.
61	V2,
62	/// An unknown version.
63	Unknown,
64}
65
66/// A unique descriptor of the candidate receipt.
67#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, RuntimeDebug)]
68#[cfg_attr(feature = "std", derive(Hash))]
69pub struct CandidateDescriptorV2<H = Hash> {
70	/// The ID of the para this is a candidate for.
71	para_id: ParaId,
72	/// The hash of the relay-chain block this is executed in the context of.
73	relay_parent: H,
74	/// Version field. The raw value here is not exposed, instead it is used
75	/// to determine the `CandidateDescriptorVersion`, see `fn version()`.
76	/// For the current version this field is set to `0` and will be incremented
77	/// by next versions.
78	version: InternalVersion,
79	/// The core index where the candidate is backed.
80	core_index: u16,
81	/// The session index of the candidate relay parent.
82	session_index: SessionIndex,
83	/// Reserved bytes.
84	reserved1: [u8; 25],
85	/// The blake2-256 hash of the persisted validation data. This is extra data derived from
86	/// relay-chain state which may vary based on bitfields included before the candidate.
87	/// Thus it cannot be derived entirely from the relay-parent.
88	persisted_validation_data_hash: Hash,
89	/// The blake2-256 hash of the PoV.
90	pov_hash: Hash,
91	/// The root of a block's erasure encoding Merkle tree.
92	erasure_root: Hash,
93	/// Reserved bytes.
94	reserved2: [u8; 64],
95	/// Hash of the para header that is being generated by this candidate.
96	para_head: Hash,
97	/// The blake2-256 hash of the validation code bytes.
98	validation_code_hash: ValidationCodeHash,
99}
100
101impl<H: Copy> From<CandidateDescriptorV2<H>> for CandidateDescriptor<H> {
102	fn from(value: CandidateDescriptorV2<H>) -> Self {
103		Self {
104			para_id: value.para_id,
105			relay_parent: value.relay_parent,
106			collator: value.rebuild_collator_field(),
107			persisted_validation_data_hash: value.persisted_validation_data_hash,
108			pov_hash: value.pov_hash,
109			erasure_root: value.erasure_root,
110			signature: value.rebuild_signature_field(),
111			para_head: value.para_head,
112			validation_code_hash: value.validation_code_hash,
113		}
114	}
115}
116
117fn clone_into_array<A, T>(slice: &[T]) -> A
118where
119	A: Default + AsMut<[T]>,
120	T: Clone,
121{
122	let mut a = A::default();
123	<A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
124	a
125}
126
127impl<H: Copy> From<CandidateDescriptor<H>> for CandidateDescriptorV2<H> {
128	fn from(value: CandidateDescriptor<H>) -> Self {
129		let collator = value.collator.as_slice();
130
131		Self {
132			para_id: value.para_id,
133			relay_parent: value.relay_parent,
134			// Use first byte of the `collator` field.
135			version: InternalVersion(collator[0]),
136			// Use next 2 bytes of the `collator` field.
137			core_index: u16::from_ne_bytes(clone_into_array(&collator[1..=2])),
138			// Use next 4 bytes of the `collator` field.
139			session_index: SessionIndex::from_ne_bytes(clone_into_array(&collator[3..=6])),
140			// Use remaing 25 bytes of the `collator` field.
141			reserved1: clone_into_array(&collator[7..]),
142			persisted_validation_data_hash: value.persisted_validation_data_hash,
143			pov_hash: value.pov_hash,
144			erasure_root: value.erasure_root,
145			reserved2: value.signature.into_inner().0,
146			para_head: value.para_head,
147			validation_code_hash: value.validation_code_hash,
148		}
149	}
150}
151
152impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
153	/// Constructor
154	pub fn new(
155		para_id: Id,
156		relay_parent: H,
157		core_index: CoreIndex,
158		session_index: SessionIndex,
159		persisted_validation_data_hash: Hash,
160		pov_hash: Hash,
161		erasure_root: Hash,
162		para_head: Hash,
163		validation_code_hash: ValidationCodeHash,
164	) -> Self {
165		Self {
166			para_id,
167			relay_parent,
168			version: InternalVersion(0),
169			core_index: core_index.0 as u16,
170			session_index,
171			reserved1: [0; 25],
172			persisted_validation_data_hash,
173			pov_hash,
174			erasure_root,
175			reserved2: [0; 64],
176			para_head,
177			validation_code_hash,
178		}
179	}
180
181	/// Check the signature of the collator within this descriptor.
182	pub fn check_collator_signature(&self) -> Result<(), ()> {
183		// Return `Ok` if collator signature is not included (v2+ descriptor).
184		let Some(collator) = self.collator() else { return Ok(()) };
185
186		let Some(signature) = self.signature() else { return Ok(()) };
187
188		super::v8::check_collator_signature(
189			&self.relay_parent,
190			&self.para_id,
191			&self.persisted_validation_data_hash,
192			&self.pov_hash,
193			&self.validation_code_hash,
194			&collator,
195			&signature,
196		)
197	}
198}
199
200/// A trait to allow changing the descriptor field values in tests.
201#[cfg(feature = "test")]
202
203pub trait MutateDescriptorV2<H> {
204	/// Set the relay parent of the descriptor.
205	fn set_relay_parent(&mut self, relay_parent: H);
206	/// Set the `ParaId` of the descriptor.
207	fn set_para_id(&mut self, para_id: Id);
208	/// Set the PoV hash of the descriptor.
209	fn set_pov_hash(&mut self, pov_hash: Hash);
210	/// Set the version field of the descriptor.
211	fn set_version(&mut self, version: InternalVersion);
212	/// Set the PVD of the descriptor.
213	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash);
214	/// Set the validation code hash of the descriptor.
215	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash);
216	/// Set the erasure root of the descriptor.
217	fn set_erasure_root(&mut self, erasure_root: Hash);
218	/// Set the para head of the descriptor.
219	fn set_para_head(&mut self, para_head: Hash);
220	/// Set the core index of the descriptor.
221	fn set_core_index(&mut self, core_index: CoreIndex);
222	/// Set the session index of the descriptor.
223	fn set_session_index(&mut self, session_index: SessionIndex);
224}
225
226#[cfg(feature = "test")]
227impl<H> MutateDescriptorV2<H> for CandidateDescriptorV2<H> {
228	fn set_para_id(&mut self, para_id: Id) {
229		self.para_id = para_id;
230	}
231
232	fn set_relay_parent(&mut self, relay_parent: H) {
233		self.relay_parent = relay_parent;
234	}
235
236	fn set_pov_hash(&mut self, pov_hash: Hash) {
237		self.pov_hash = pov_hash;
238	}
239
240	fn set_version(&mut self, version: InternalVersion) {
241		self.version = version;
242	}
243
244	fn set_core_index(&mut self, core_index: CoreIndex) {
245		self.core_index = core_index.0 as u16;
246	}
247
248	fn set_session_index(&mut self, session_index: SessionIndex) {
249		self.session_index = session_index;
250	}
251
252	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) {
253		self.persisted_validation_data_hash = persisted_validation_data_hash;
254	}
255
256	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) {
257		self.validation_code_hash = validation_code_hash;
258	}
259
260	fn set_erasure_root(&mut self, erasure_root: Hash) {
261		self.erasure_root = erasure_root;
262	}
263
264	fn set_para_head(&mut self, para_head: Hash) {
265		self.para_head = para_head;
266	}
267}
268
269/// A candidate-receipt at version 2.
270#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, RuntimeDebug)]
271#[cfg_attr(feature = "std", derive(Hash))]
272pub struct CandidateReceiptV2<H = Hash> {
273	/// The descriptor of the candidate.
274	pub descriptor: CandidateDescriptorV2<H>,
275	/// The hash of the encoded commitments made as a result of candidate execution.
276	pub commitments_hash: Hash,
277}
278
279/// A candidate-receipt with commitments directly included.
280#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, RuntimeDebug)]
281#[cfg_attr(feature = "std", derive(Hash))]
282pub struct CommittedCandidateReceiptV2<H = Hash> {
283	/// The descriptor of the candidate.
284	pub descriptor: CandidateDescriptorV2<H>,
285	/// The commitments of the candidate receipt.
286	pub commitments: CandidateCommitments,
287}
288
289/// An event concerning a candidate.
290#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
291#[cfg_attr(feature = "std", derive(PartialEq))]
292pub enum CandidateEvent<H = Hash> {
293	/// This candidate receipt was backed in the most recent block.
294	/// This includes the core index the candidate is now occupying.
295	#[codec(index = 0)]
296	CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
297	/// This candidate receipt was included and became a parablock at the most recent block.
298	/// This includes the core index the candidate was occupying as well as the group responsible
299	/// for backing the candidate.
300	#[codec(index = 1)]
301	CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
302	/// This candidate receipt was not made available in time and timed out.
303	/// This includes the core index the candidate was occupying.
304	#[codec(index = 2)]
305	CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
306}
307
308impl<H: Encode + Copy> From<CandidateEvent<H>> for super::v8::CandidateEvent<H> {
309	fn from(value: CandidateEvent<H>) -> Self {
310		match value {
311			CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) =>
312				super::v8::CandidateEvent::CandidateBacked(
313					receipt.into(),
314					head_data,
315					core_index,
316					group_index,
317				),
318			CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) =>
319				super::v8::CandidateEvent::CandidateIncluded(
320					receipt.into(),
321					head_data,
322					core_index,
323					group_index,
324				),
325			CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) =>
326				super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index),
327		}
328	}
329}
330
331impl<H> CandidateReceiptV2<H> {
332	/// Get a reference to the candidate descriptor.
333	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
334		&self.descriptor
335	}
336
337	/// Computes the blake2-256 hash of the receipt.
338	pub fn hash(&self) -> CandidateHash
339	where
340		H: Encode,
341	{
342		CandidateHash(BlakeTwo256::hash_of(self))
343	}
344}
345
346impl<H: Copy> From<super::v8::CandidateReceipt<H>> for CandidateReceiptV2<H> {
347	fn from(value: super::v8::CandidateReceipt<H>) -> Self {
348		CandidateReceiptV2 {
349			descriptor: value.descriptor.into(),
350			commitments_hash: value.commitments_hash,
351		}
352	}
353}
354
355impl<H: Copy> From<super::v8::CommittedCandidateReceipt<H>> for CommittedCandidateReceiptV2<H> {
356	fn from(value: super::v8::CommittedCandidateReceipt<H>) -> Self {
357		CommittedCandidateReceiptV2 {
358			descriptor: value.descriptor.into(),
359			commitments: value.commitments,
360		}
361	}
362}
363
364impl<H: Clone> CommittedCandidateReceiptV2<H> {
365	/// Transforms this into a plain `CandidateReceipt`.
366	pub fn to_plain(&self) -> CandidateReceiptV2<H> {
367		CandidateReceiptV2 {
368			descriptor: self.descriptor.clone(),
369			commitments_hash: self.commitments.hash(),
370		}
371	}
372
373	/// Computes the hash of the committed candidate receipt.
374	///
375	/// This computes the canonical hash, not the hash of the directly encoded data.
376	/// Thus this is a shortcut for `candidate.to_plain().hash()`.
377	pub fn hash(&self) -> CandidateHash
378	where
379		H: Encode,
380	{
381		self.to_plain().hash()
382	}
383
384	/// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]?
385	pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
386	where
387		H: PartialEq,
388	{
389		receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
390	}
391}
392
393impl PartialOrd for CommittedCandidateReceiptV2 {
394	fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
395		Some(self.cmp(other))
396	}
397}
398
399impl Ord for CommittedCandidateReceiptV2 {
400	fn cmp(&self, other: &Self) -> core::cmp::Ordering {
401		self.descriptor
402			.para_id
403			.cmp(&other.descriptor.para_id)
404			.then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
405	}
406}
407
408impl<H: Copy> From<CommittedCandidateReceiptV2<H>> for super::v8::CommittedCandidateReceipt<H> {
409	fn from(value: CommittedCandidateReceiptV2<H>) -> Self {
410		Self { descriptor: value.descriptor.into(), commitments: value.commitments }
411	}
412}
413
414impl<H: Copy> From<CandidateReceiptV2<H>> for super::v8::CandidateReceipt<H> {
415	fn from(value: CandidateReceiptV2<H>) -> Self {
416		Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash }
417	}
418}
419
420/// A strictly increasing sequence number, typically this would be the least significant byte of the
421/// block number.
422#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
423pub struct CoreSelector(pub u8);
424
425/// An offset in the relay chain claim queue.
426#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
427pub struct ClaimQueueOffset(pub u8);
428
429/// Signals that a parachain can send to the relay chain via the UMP queue.
430#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
431pub enum UMPSignal {
432	/// A message sent by a parachain to select the core the candidate is committed to.
433	/// Relay chain validators, in particular backers, use the `CoreSelector` and
434	/// `ClaimQueueOffset` to compute the index of the core the candidate has committed to.
435	SelectCore(CoreSelector, ClaimQueueOffset),
436}
437/// Separator between `XCM` and `UMPSignal`.
438pub const UMP_SEPARATOR: Vec<u8> = vec![];
439
440/// Utility function for skipping the ump signals.
441pub fn skip_ump_signals<'a>(
442	upward_messages: impl Iterator<Item = &'a Vec<u8>>,
443) -> impl Iterator<Item = &'a Vec<u8>> {
444	upward_messages.take_while(|message| *message != &UMP_SEPARATOR)
445}
446
447impl CandidateCommitments {
448	/// Returns the core selector and claim queue offset determined by `UMPSignal::SelectCore`
449	/// commitment, if present.
450	pub fn core_selector(
451		&self,
452	) -> Result<Option<(CoreSelector, ClaimQueueOffset)>, CommittedCandidateReceiptError> {
453		let mut signals_iter =
454			self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR);
455
456		if signals_iter.next().is_some() {
457			let Some(core_selector_message) = signals_iter.next() else { return Ok(None) };
458			// We should have exactly one signal beyond the separator
459			if signals_iter.next().is_some() {
460				return Err(CommittedCandidateReceiptError::TooManyUMPSignals)
461			}
462
463			match UMPSignal::decode(&mut core_selector_message.as_slice())
464				.map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)?
465			{
466				UMPSignal::SelectCore(core_index_selector, cq_offset) =>
467					Ok(Some((core_index_selector, cq_offset))),
468			}
469		} else {
470			Ok(None)
471		}
472	}
473}
474
475/// CommittedCandidateReceiptError construction errors.
476#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
477#[cfg_attr(feature = "std", derive(thiserror::Error))]
478pub enum CommittedCandidateReceiptError {
479	/// The specified core index is invalid.
480	#[cfg_attr(feature = "std", error("The specified core index is invalid"))]
481	InvalidCoreIndex,
482	/// The core index in commitments doesn't match the one in descriptor
483	#[cfg_attr(
484		feature = "std",
485		error("The core index in commitments doesn't match the one in descriptor")
486	)]
487	CoreIndexMismatch,
488	/// The core selector or claim queue offset is invalid.
489	#[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))]
490	InvalidSelectedCore,
491	#[cfg_attr(feature = "std", error("Could not decode UMP signal"))]
492	/// Could not decode UMP signal.
493	UmpSignalDecode,
494	/// The parachain is not assigned to any core at specified claim queue offset.
495	#[cfg_attr(
496		feature = "std",
497		error("The parachain is not assigned to any core at specified claim queue offset")
498	)]
499	NoAssignment,
500	/// No core was selected. The `SelectCore` commitment is mandatory for
501	/// v2 receipts if parachains has multiple cores assigned.
502	#[cfg_attr(feature = "std", error("Core selector not present"))]
503	NoCoreSelected,
504	/// Unknown version.
505	#[cfg_attr(feature = "std", error("Unknown internal version"))]
506	UnknownVersion(InternalVersion),
507	/// The allowed number of `UMPSignal` messages in the queue was exceeded.
508	/// Currenly only one such message is allowed.
509	#[cfg_attr(feature = "std", error("Too many UMP signals"))]
510	TooManyUMPSignals,
511	/// If the parachain runtime started sending core selectors, v1 descriptors are no longer
512	/// allowed.
513	#[cfg_attr(feature = "std", error("Version 1 receipt does not support core selectors"))]
514	CoreSelectorWithV1Decriptor,
515}
516
517macro_rules! impl_getter {
518	($field:ident, $type:ident) => {
519		/// Returns the value of `$field` field.
520		pub fn $field(&self) -> $type {
521			self.$field
522		}
523	};
524}
525
526impl<H: Copy> CandidateDescriptorV2<H> {
527	impl_getter!(erasure_root, Hash);
528	impl_getter!(para_head, Hash);
529	impl_getter!(relay_parent, H);
530	impl_getter!(para_id, ParaId);
531	impl_getter!(persisted_validation_data_hash, Hash);
532	impl_getter!(pov_hash, Hash);
533	impl_getter!(validation_code_hash, ValidationCodeHash);
534
535	/// Returns the candidate descriptor version.
536	/// The candidate is at version 2 if the reserved fields are zeroed out
537	/// and the internal `version` field is 0.
538	pub fn version(&self) -> CandidateDescriptorVersion {
539		if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] {
540			return CandidateDescriptorVersion::V1
541		}
542
543		match self.version.0 {
544			0 => CandidateDescriptorVersion::V2,
545			_ => CandidateDescriptorVersion::Unknown,
546		}
547	}
548
549	fn rebuild_collator_field(&self) -> CollatorId {
550		let mut collator_id = Vec::with_capacity(32);
551		let core_index: [u8; 2] = self.core_index.to_ne_bytes();
552		let session_index: [u8; 4] = self.session_index.to_ne_bytes();
553
554		collator_id.push(self.version.0);
555		collator_id.extend_from_slice(core_index.as_slice());
556		collator_id.extend_from_slice(session_index.as_slice());
557		collator_id.extend_from_slice(self.reserved1.as_slice());
558
559		CollatorId::from_slice(&collator_id.as_slice())
560			.expect("Slice size is exactly 32 bytes; qed")
561	}
562
563	/// Returns the collator id if this is a v1 `CandidateDescriptor`
564	pub fn collator(&self) -> Option<CollatorId> {
565		if self.version() == CandidateDescriptorVersion::V1 {
566			Some(self.rebuild_collator_field())
567		} else {
568			None
569		}
570	}
571
572	fn rebuild_signature_field(&self) -> CollatorSignature {
573		CollatorSignature::from_slice(self.reserved2.as_slice())
574			.expect("Slice size is exactly 64 bytes; qed")
575	}
576
577	/// Returns the collator signature of `V1` candidate descriptors, `None` otherwise.
578	pub fn signature(&self) -> Option<CollatorSignature> {
579		if self.version() == CandidateDescriptorVersion::V1 {
580			return Some(self.rebuild_signature_field())
581		}
582
583		None
584	}
585
586	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
587	pub fn core_index(&self) -> Option<CoreIndex> {
588		if self.version() == CandidateDescriptorVersion::V1 {
589			return None
590		}
591
592		Some(CoreIndex(self.core_index as u32))
593	}
594
595	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
596	pub fn session_index(&self) -> Option<SessionIndex> {
597		if self.version() == CandidateDescriptorVersion::V1 {
598			return None
599		}
600
601		Some(self.session_index)
602	}
603}
604
605impl<H: Copy> CommittedCandidateReceiptV2<H> {
606	/// Checks if descriptor core index is equal to the committed core index.
607	/// Input `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as
608	/// a mapping between `ParaId` and the cores assigned per depth.
609	pub fn check_core_index(
610		&self,
611		cores_per_para: &TransposedClaimQueue,
612	) -> Result<(), CommittedCandidateReceiptError> {
613		let maybe_core_selector = self.commitments.core_selector()?;
614
615		match self.descriptor.version() {
616			CandidateDescriptorVersion::V1 => {
617				// If the parachain runtime started sending core selectors, v1 descriptors are no
618				// longer allowed.
619				if maybe_core_selector.is_some() {
620					return Err(CommittedCandidateReceiptError::CoreSelectorWithV1Decriptor)
621				} else {
622					// Nothing else to check for v1 descriptors.
623					return Ok(())
624				}
625			},
626			CandidateDescriptorVersion::V2 => {},
627			CandidateDescriptorVersion::Unknown =>
628				return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version)),
629		}
630
631		let (maybe_core_index_selector, cq_offset) = maybe_core_selector.map_or_else(
632			|| (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)),
633			|(sel, off)| (Some(sel), off),
634		);
635
636		let assigned_cores = cores_per_para
637			.get(&self.descriptor.para_id())
638			.ok_or(CommittedCandidateReceiptError::NoAssignment)?
639			.get(&cq_offset.0)
640			.ok_or(CommittedCandidateReceiptError::NoAssignment)?;
641
642		if assigned_cores.is_empty() {
643			return Err(CommittedCandidateReceiptError::NoAssignment)
644		}
645
646		let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
647
648		let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector {
649			// We have a committed core selector, we can use it.
650			core_index_selector
651		} else if assigned_cores.len() > 1 {
652			// We got more than one assigned core and no core selector. Special care is needed.
653			if !assigned_cores.contains(&descriptor_core_index) {
654				// core index in the descriptor is not assigned to the para. Error.
655				return Err(CommittedCandidateReceiptError::InvalidCoreIndex)
656			} else {
657				// the descriptor core index is indeed assigned to the para. This is the most we can
658				// check for now
659				return Ok(())
660			}
661		} else {
662			// No core selector but there's only one assigned core, use it.
663			CoreSelector(0)
664		};
665
666		let core_index = assigned_cores
667			.iter()
668			.nth(core_index_selector.0 as usize % assigned_cores.len())
669			.ok_or(CommittedCandidateReceiptError::InvalidSelectedCore)
670			.copied()?;
671
672		if core_index != descriptor_core_index {
673			return Err(CommittedCandidateReceiptError::CoreIndexMismatch)
674		}
675
676		Ok(())
677	}
678}
679
680/// A backed (or backable, depending on context) candidate.
681#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
682pub struct BackedCandidate<H = Hash> {
683	/// The candidate referred to.
684	candidate: CommittedCandidateReceiptV2<H>,
685	/// The validity votes themselves, expressed as signatures.
686	validity_votes: Vec<ValidityAttestation>,
687	/// The indices of the validators within the group, expressed as a bitfield. May be extended
688	/// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is
689	/// enabled.
690	validator_indices: BitVec<u8, bitvec::order::Lsb0>,
691}
692
693/// Parachains inherent-data passed into the runtime by a block author
694#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, RuntimeDebug, TypeInfo)]
695pub struct InherentData<HDR: HeaderT = Header> {
696	/// Signed bitfields by validators about availability.
697	pub bitfields: UncheckedSignedAvailabilityBitfields,
698	/// Backed candidates for inclusion in the block.
699	pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
700	/// Sets of dispute votes for inclusion,
701	pub disputes: MultiDisputeStatementSet,
702	/// The parent block header. Used for checking state proofs.
703	pub parent_header: HDR,
704}
705
706impl<H> BackedCandidate<H> {
707	/// Constructor
708	pub fn new(
709		candidate: CommittedCandidateReceiptV2<H>,
710		validity_votes: Vec<ValidityAttestation>,
711		validator_indices: BitVec<u8, bitvec::order::Lsb0>,
712		core_index: CoreIndex,
713	) -> Self {
714		let mut instance = Self { candidate, validity_votes, validator_indices };
715		instance.inject_core_index(core_index);
716		instance
717	}
718
719	/// Get a reference to the committed candidate receipt of the candidate.
720	pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
721		&self.candidate
722	}
723
724	/// Get a mutable reference to the committed candidate receipt of the candidate.
725	/// Only for testing.
726	#[cfg(feature = "test")]
727	pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2<H> {
728		&mut self.candidate
729	}
730	/// Get a reference to the descriptor of the candidate.
731	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
732		&self.candidate.descriptor
733	}
734
735	/// Get a mutable reference to the descriptor of the candidate. Only for testing.
736	#[cfg(feature = "test")]
737	pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
738		&mut self.candidate.descriptor
739	}
740
741	/// Get a reference to the validity votes of the candidate.
742	pub fn validity_votes(&self) -> &[ValidityAttestation] {
743		&self.validity_votes
744	}
745
746	/// Get a mutable reference to validity votes of the para.
747	pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
748		&mut self.validity_votes
749	}
750
751	/// Compute this candidate's hash.
752	pub fn hash(&self) -> CandidateHash
753	where
754		H: Clone + Encode,
755	{
756		self.candidate.to_plain().hash()
757	}
758
759	/// Get this candidate's receipt.
760	pub fn receipt(&self) -> CandidateReceiptV2<H>
761	where
762		H: Clone,
763	{
764		self.candidate.to_plain()
765	}
766
767	/// Get a copy of the validator indices and the assumed core index, if any.
768	pub fn validator_indices_and_core_index(
769		&self,
770	) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
771		// `BackedCandidate::validity_indices` are extended to store a 8 bit core index.
772		let core_idx_offset = self.validator_indices.len().saturating_sub(8);
773		if core_idx_offset > 0 {
774			let (validator_indices_slice, core_idx_slice) =
775				self.validator_indices.split_at(core_idx_offset);
776			return (validator_indices_slice, Some(CoreIndex(core_idx_slice.load::<u8>() as u32)));
777		}
778
779		(&self.validator_indices, None)
780	}
781
782	/// Inject a core index in the validator_indices bitvec.
783	fn inject_core_index(&mut self, core_index: CoreIndex) {
784		let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
785			BitVec::from_vec(vec![core_index.0 as u8]);
786		self.validator_indices.extend(core_index_to_inject);
787	}
788
789	/// Update the validator indices and core index in the candidate.
790	pub fn set_validator_indices_and_core_index(
791		&mut self,
792		new_indices: BitVec<u8, bitvec::order::Lsb0>,
793		maybe_core_index: Option<CoreIndex>,
794	) {
795		self.validator_indices = new_indices;
796
797		if let Some(core_index) = maybe_core_index {
798			self.inject_core_index(core_index);
799		}
800	}
801}
802
803/// Scraped runtime backing votes and resolved disputes.
804#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
805#[cfg_attr(feature = "std", derive(PartialEq))]
806pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
807	/// The session in which the block was included.
808	pub session: SessionIndex,
809	/// Set of backing validators for each candidate, represented by its candidate
810	/// receipt.
811	pub backing_validators_per_candidate:
812		Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
813	/// On-chain-recorded set of disputes.
814	/// Note that the above `backing_validators` are
815	/// unrelated to the backers of the disputes candidates.
816	pub disputes: MultiDisputeStatementSet,
817}
818
819impl<H: Encode + Decode + Copy> From<ScrapedOnChainVotes<H>> for super::v8::ScrapedOnChainVotes<H> {
820	fn from(value: ScrapedOnChainVotes<H>) -> Self {
821		Self {
822			session: value.session,
823			backing_validators_per_candidate: value
824				.backing_validators_per_candidate
825				.into_iter()
826				.map(|(receipt, validators)| (receipt.into(), validators))
827				.collect::<Vec<_>>(),
828			disputes: value.disputes,
829		}
830	}
831}
832
833/// Information about a core which is currently occupied.
834#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
835#[cfg_attr(feature = "std", derive(PartialEq))]
836pub struct OccupiedCore<H = Hash, N = BlockNumber> {
837	// NOTE: this has no ParaId as it can be deduced from the candidate descriptor.
838	/// If this core is freed by availability, this is the assignment that is next up on this
839	/// core, if any. None if there is nothing queued for this core.
840	pub next_up_on_available: Option<ScheduledCore>,
841	/// The relay-chain block number this began occupying the core at.
842	pub occupied_since: N,
843	/// The relay-chain block this will time-out at, if any.
844	pub time_out_at: N,
845	/// If this core is freed by being timed-out, this is the assignment that is next up on this
846	/// core. None if there is nothing queued for this core or there is no possibility of timing
847	/// out.
848	pub next_up_on_time_out: Option<ScheduledCore>,
849	/// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
850	/// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
851	/// this will be available.
852	pub availability: BitVec<u8, bitvec::order::Lsb0>,
853	/// The group assigned to distribute availability pieces of this candidate.
854	pub group_responsible: GroupIndex,
855	/// The hash of the candidate occupying the core.
856	pub candidate_hash: CandidateHash,
857	/// The descriptor of the candidate occupying the core.
858	pub candidate_descriptor: CandidateDescriptorV2<H>,
859}
860
861impl<H, N> OccupiedCore<H, N> {
862	/// Get the Para currently occupying this core.
863	pub fn para_id(&self) -> Id {
864		self.candidate_descriptor.para_id
865	}
866}
867
868/// The state of a particular availability core.
869#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
870#[cfg_attr(feature = "std", derive(PartialEq))]
871pub enum CoreState<H = Hash, N = BlockNumber> {
872	/// The core is currently occupied.
873	#[codec(index = 0)]
874	Occupied(OccupiedCore<H, N>),
875	/// The core is currently free, with a para scheduled and given the opportunity
876	/// to occupy.
877	///
878	/// If a particular Collator is required to author this block, that is also present in this
879	/// variant.
880	#[codec(index = 1)]
881	Scheduled(ScheduledCore),
882	/// The core is currently free and there is nothing scheduled. This can be the case for
883	/// parathread cores when there are no parathread blocks queued. Parachain cores will never be
884	/// left idle.
885	#[codec(index = 2)]
886	Free,
887}
888
889impl<N> CoreState<N> {
890	/// Returns the scheduled `ParaId` for the core or `None` if nothing is scheduled.
891	///
892	/// This function is deprecated. `ClaimQueue` should be used to obtain the scheduled `ParaId`s
893	/// for each core.
894	#[deprecated(
895		note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead."
896	)]
897	pub fn para_id(&self) -> Option<Id> {
898		match self {
899			Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id),
900			Self::Scheduled(core) => Some(core.para_id),
901			Self::Free => None,
902		}
903	}
904
905	/// Is this core state `Self::Occupied`?
906	pub fn is_occupied(&self) -> bool {
907		matches!(self, Self::Occupied(_))
908	}
909}
910
911impl<H: Copy> From<OccupiedCore<H>> for super::v8::OccupiedCore<H> {
912	fn from(value: OccupiedCore<H>) -> Self {
913		Self {
914			next_up_on_available: value.next_up_on_available,
915			occupied_since: value.occupied_since,
916			time_out_at: value.time_out_at,
917			next_up_on_time_out: value.next_up_on_time_out,
918			availability: value.availability,
919			group_responsible: value.group_responsible,
920			candidate_hash: value.candidate_hash,
921			candidate_descriptor: value.candidate_descriptor.into(),
922		}
923	}
924}
925
926impl<H: Copy> From<CoreState<H>> for super::v8::CoreState<H> {
927	fn from(value: CoreState<H>) -> Self {
928		match value {
929			CoreState::Free => super::v8::CoreState::Free,
930			CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core),
931			CoreState::Occupied(occupied_core) =>
932				super::v8::CoreState::Occupied(occupied_core.into()),
933		}
934	}
935}
936
937/// The claim queue mapped by parachain id.
938pub type TransposedClaimQueue = BTreeMap<ParaId, BTreeMap<u8, BTreeSet<CoreIndex>>>;
939
940/// Returns a mapping between the para id and the core indices assigned at different
941/// depths in the claim queue.
942pub fn transpose_claim_queue(
943	claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
944) -> TransposedClaimQueue {
945	let mut per_para_claim_queue = BTreeMap::new();
946
947	for (core, paras) in claim_queue {
948		// Iterate paras assigned to this core at each depth.
949		for (depth, para) in paras.into_iter().enumerate() {
950			let depths: &mut BTreeMap<u8, BTreeSet<CoreIndex>> =
951				per_para_claim_queue.entry(para).or_insert_with(|| Default::default());
952
953			depths.entry(depth as u8).or_default().insert(core);
954		}
955	}
956
957	per_para_claim_queue
958}
959
960#[cfg(test)]
961mod tests {
962	use super::*;
963	use crate::{
964		v8::{
965			tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt,
966			CommittedCandidateReceipt, Hash, HeadData, ValidationCode,
967		},
968		vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2},
969	};
970
971	fn dummy_collator_signature() -> CollatorSignature {
972		CollatorSignature::from_slice(&mut (0..64).into_iter().collect::<Vec<_>>().as_slice())
973			.expect("64 bytes; qed")
974	}
975
976	fn dummy_collator_id() -> CollatorId {
977		CollatorId::from_slice(&mut (0..32).into_iter().collect::<Vec<_>>().as_slice())
978			.expect("32 bytes; qed")
979	}
980
981	pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 {
982		let zeros = Hash::zero();
983		let reserved2 = [0; 64];
984
985		CommittedCandidateReceiptV2 {
986			descriptor: CandidateDescriptorV2 {
987				para_id: 0.into(),
988				relay_parent: zeros,
989				version: InternalVersion(0),
990				core_index: 123,
991				session_index: 1,
992				reserved1: Default::default(),
993				persisted_validation_data_hash: zeros,
994				pov_hash: zeros,
995				erasure_root: zeros,
996				reserved2,
997				para_head: zeros,
998				validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(),
999			},
1000			commitments: CandidateCommitments {
1001				head_data: HeadData(vec![]),
1002				upward_messages: vec![].try_into().expect("empty vec fits within bounds"),
1003				new_validation_code: None,
1004				horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"),
1005				processed_downward_messages: 0,
1006				hrmp_watermark: 0_u32,
1007			},
1008		}
1009	}
1010
1011	#[test]
1012	fn is_binary_compatibile() {
1013		let old_ccr = dummy_old_committed_candidate_receipt();
1014		let new_ccr = dummy_committed_candidate_receipt_v2();
1015
1016		assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size());
1017
1018		let encoded_old = old_ccr.encode();
1019
1020		// Deserialize from old candidate receipt.
1021		let new_ccr: CommittedCandidateReceiptV2 =
1022			Decode::decode(&mut encoded_old.as_slice()).unwrap();
1023
1024		// We get same candidate hash.
1025		assert_eq!(old_ccr.hash(), new_ccr.hash());
1026	}
1027
1028	#[test]
1029	fn test_from_v1_descriptor() {
1030		let mut old_ccr = dummy_old_committed_candidate_receipt().to_plain();
1031		old_ccr.descriptor.collator = dummy_collator_id();
1032		old_ccr.descriptor.signature = dummy_collator_signature();
1033
1034		let mut new_ccr = dummy_committed_candidate_receipt_v2().to_plain();
1035
1036		// Override descriptor from old candidate receipt.
1037		new_ccr.descriptor = old_ccr.descriptor.clone().into();
1038
1039		// We get same candidate hash.
1040		assert_eq!(old_ccr.hash(), new_ccr.hash());
1041
1042		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V1);
1043		assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap());
1044		assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap());
1045	}
1046
1047	#[test]
1048	fn invalid_version_descriptor() {
1049		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1050		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2);
1051		// Put some unknown version.
1052		new_ccr.descriptor.version = InternalVersion(100);
1053
1054		// Deserialize as V1.
1055		let new_ccr: CommittedCandidateReceiptV2 =
1056			Decode::decode(&mut new_ccr.encode().as_slice()).unwrap();
1057
1058		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown);
1059		assert_eq!(
1060			new_ccr.check_core_index(&BTreeMap::new()),
1061			Err(CommittedCandidateReceiptError::UnknownVersion(InternalVersion(100)))
1062		)
1063	}
1064
1065	#[test]
1066	fn test_ump_commitment() {
1067		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1068		new_ccr.descriptor.core_index = 123;
1069		new_ccr.descriptor.para_id = ParaId::new(1000);
1070
1071		// dummy XCM messages
1072		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1073		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1074
1075		// separator
1076		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1077
1078		// CoreIndex commitment
1079		new_ccr
1080			.commitments
1081			.upward_messages
1082			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1083
1084		let mut cq = BTreeMap::new();
1085		cq.insert(
1086			CoreIndex(123),
1087			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1088		);
1089
1090		assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(()));
1091	}
1092
1093	#[test]
1094	fn test_invalid_ump_commitment() {
1095		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1096		new_ccr.descriptor.core_index = 0;
1097		new_ccr.descriptor.para_id = ParaId::new(1000);
1098
1099		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1100
1101		let mut cq = BTreeMap::new();
1102		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1103
1104		// The check should not fail because no `SelectCore` signal was sent.
1105		// The message is optional.
1106		assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok());
1107
1108		// Garbage message.
1109		new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode());
1110
1111		// No `SelectCore` can be decoded.
1112		assert_eq!(
1113			new_ccr.commitments.core_selector(),
1114			Err(CommittedCandidateReceiptError::UmpSignalDecode)
1115		);
1116
1117		// Has two cores assigned but no core commitment. Will pass the check if the descriptor core
1118		// index is indeed assigned to the para.
1119		new_ccr.commitments.upward_messages.clear();
1120
1121		let mut cq = BTreeMap::new();
1122		cq.insert(
1123			CoreIndex(0),
1124			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1125		);
1126		cq.insert(
1127			CoreIndex(100),
1128			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1129		);
1130		assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), Ok(()));
1131
1132		new_ccr.descriptor.set_core_index(CoreIndex(1));
1133		assert_eq!(
1134			new_ccr.check_core_index(&transpose_claim_queue(cq.clone())),
1135			Err(CommittedCandidateReceiptError::InvalidCoreIndex)
1136		);
1137		new_ccr.descriptor.set_core_index(CoreIndex(0));
1138
1139		new_ccr.commitments.upward_messages.clear();
1140		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1141		new_ccr
1142			.commitments
1143			.upward_messages
1144			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1145
1146		// No assignments.
1147		assert_eq!(
1148			new_ccr.check_core_index(&transpose_claim_queue(Default::default())),
1149			Err(CommittedCandidateReceiptError::NoAssignment)
1150		);
1151
1152		// Mismatch between descriptor index and commitment.
1153		new_ccr.descriptor.set_core_index(CoreIndex(1));
1154		assert_eq!(
1155			new_ccr.check_core_index(&transpose_claim_queue(cq.clone())),
1156			Err(CommittedCandidateReceiptError::CoreIndexMismatch)
1157		);
1158		new_ccr.descriptor.set_core_index(CoreIndex(0));
1159
1160		// Too many UMP signals.
1161		new_ccr
1162			.commitments
1163			.upward_messages
1164			.force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
1165
1166		assert_eq!(
1167			new_ccr.check_core_index(&transpose_claim_queue(cq)),
1168			Err(CommittedCandidateReceiptError::TooManyUMPSignals)
1169		);
1170	}
1171
1172	#[test]
1173	fn test_version2_receipts_decoded_as_v1() {
1174		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1175		new_ccr.descriptor.core_index = 123;
1176		new_ccr.descriptor.para_id = ParaId::new(1000);
1177
1178		// dummy XCM messages
1179		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1180		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1181
1182		// separator
1183		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1184
1185		// CoreIndex commitment
1186		new_ccr
1187			.commitments
1188			.upward_messages
1189			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1190
1191		let encoded_ccr = new_ccr.encode();
1192		let decoded_ccr: CommittedCandidateReceipt =
1193			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1194
1195		assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent());
1196		assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id());
1197
1198		assert_eq!(new_ccr.hash(), decoded_ccr.hash());
1199
1200		// Encode v1 and decode as V2
1201		let encoded_ccr = new_ccr.encode();
1202		let v2_ccr: CommittedCandidateReceiptV2 =
1203			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1204
1205		assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123)));
1206
1207		let mut cq = BTreeMap::new();
1208		cq.insert(
1209			CoreIndex(123),
1210			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1211		);
1212
1213		assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(()));
1214
1215		assert_eq!(new_ccr.hash(), v2_ccr.hash());
1216	}
1217
1218	// V1 descriptors are forbidden once the parachain runtime started sending UMP signals.
1219	#[test]
1220	fn test_v1_descriptors_with_ump_signal() {
1221		let mut ccr = dummy_old_committed_candidate_receipt();
1222		ccr.descriptor.para_id = ParaId::new(1024);
1223		// Adding collator signature should make it decode as v1.
1224		ccr.descriptor.signature = dummy_collator_signature();
1225		ccr.descriptor.collator = dummy_collator_id();
1226
1227		ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1228		ccr.commitments
1229			.upward_messages
1230			.force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
1231
1232		let encoded_ccr: Vec<u8> = ccr.encode();
1233
1234		let v1_ccr: CommittedCandidateReceiptV2 =
1235			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1236
1237		assert_eq!(v1_ccr.descriptor.version(), CandidateDescriptorVersion::V1);
1238		assert!(v1_ccr.commitments.core_selector().unwrap().is_some());
1239
1240		let mut cq = BTreeMap::new();
1241		cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into());
1242		cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into());
1243
1244		assert_eq!(v1_ccr.descriptor.core_index(), None);
1245
1246		assert_eq!(
1247			v1_ccr.check_core_index(&transpose_claim_queue(cq)),
1248			Err(CommittedCandidateReceiptError::CoreSelectorWithV1Decriptor)
1249		);
1250	}
1251
1252	#[test]
1253	fn test_core_select_is_optional() {
1254		// Testing edge case when collators provide zeroed signature and collator id.
1255		let mut old_ccr = dummy_old_committed_candidate_receipt();
1256		old_ccr.descriptor.para_id = ParaId::new(1000);
1257		let encoded_ccr: Vec<u8> = old_ccr.encode();
1258
1259		let new_ccr: CommittedCandidateReceiptV2 =
1260			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1261
1262		let mut cq = BTreeMap::new();
1263		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1264
1265		// Since collator sig and id are zeroed, it means that the descriptor uses format
1266		// version 2. Should still pass checks without core selector.
1267		assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok());
1268
1269		let mut cq = BTreeMap::new();
1270		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1271		cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into());
1272
1273		// Passes even if 2 cores are assigned, because elastic scaling MVP could still inject the
1274		// core index in the `BackedCandidate`.
1275		assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(()));
1276
1277		// Adding collator signature should make it decode as v1.
1278		old_ccr.descriptor.signature = dummy_collator_signature();
1279		old_ccr.descriptor.collator = dummy_collator_id();
1280
1281		let old_ccr_hash = old_ccr.hash();
1282
1283		let encoded_ccr: Vec<u8> = old_ccr.encode();
1284
1285		let new_ccr: CommittedCandidateReceiptV2 =
1286			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1287
1288		assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature));
1289		assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator));
1290
1291		assert_eq!(new_ccr.descriptor.core_index(), None);
1292		assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000));
1293
1294		assert_eq!(old_ccr_hash, new_ccr.hash());
1295	}
1296}