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 core::fmt::Formatter;
19
20use crate::{slashing::DisputesTimeSlot, ValidatorId, ValidatorIndex, ValidityAttestation};
21
22// Put any primitives used by staging APIs functions here
23use super::{
24	async_backing::{InboundHrmpLimitations, OutboundHrmpChannelLimitations},
25	BlakeTwo256, BlockNumber, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId,
26	CollatorSignature, CoreIndex, GroupIndex, Hash, HashT, HeadData, Header, Id, Id as ParaId,
27	MultiDisputeStatementSet, ScheduledCore, UncheckedSignedAvailabilityBitfields,
28	UpgradeRestriction, ValidationCodeHash,
29};
30use alloc::{
31	collections::{BTreeMap, BTreeSet, VecDeque},
32	vec,
33	vec::Vec,
34};
35use bitvec::prelude::*;
36use bounded_collections::BoundedVec;
37use codec::{Decode, DecodeWithMemTracking, Encode};
38use scale_info::TypeInfo;
39use sp_application_crypto::ByteArray;
40use sp_core::{ConstU32, RuntimeDebug};
41use sp_runtime::traits::Header as HeaderT;
42use sp_staking::SessionIndex;
43
44/// Async backing primitives
45pub mod async_backing;
46
47/// The default claim queue offset to be used if it's not configured/accessible in the parachain
48/// runtime
49pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0;
50
51/// A type representing the version of the candidate descriptor and internal version number.
52#[derive(
53	PartialEq, Eq, Encode, Decode, DecodeWithMemTracking, Clone, TypeInfo, RuntimeDebug, Copy,
54)]
55#[cfg_attr(feature = "std", derive(Hash))]
56pub struct InternalVersion(pub u8);
57
58/// A type representing the version of the candidate descriptor.
59#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)]
60#[cfg_attr(feature = "std", derive(Hash))]
61pub enum CandidateDescriptorVersion {
62	/// The old candidate descriptor version.
63	V1,
64	/// The new `CandidateDescriptorV2`.
65	V2,
66	/// An unknown version.
67	Unknown,
68}
69
70/// A unique descriptor of the candidate receipt.
71#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
72#[cfg_attr(feature = "std", derive(Hash))]
73pub struct CandidateDescriptorV2<H = Hash> {
74	/// The ID of the para this is a candidate for.
75	para_id: ParaId,
76	/// The hash of the relay-chain block this is executed in the context of.
77	relay_parent: H,
78	/// Version field. The raw value here is not exposed, instead it is used
79	/// to determine the `CandidateDescriptorVersion`, see `fn version()`.
80	/// For the current version this field is set to `0` and will be incremented
81	/// by next versions.
82	version: InternalVersion,
83	/// The core index where the candidate is backed.
84	core_index: u16,
85	/// The session index of the candidate relay parent.
86	session_index: SessionIndex,
87	/// Reserved bytes.
88	reserved1: [u8; 25],
89	/// The blake2-256 hash of the persisted validation data. This is extra data derived from
90	/// relay-chain state which may vary based on bitfields included before the candidate.
91	/// Thus it cannot be derived entirely from the relay-parent.
92	persisted_validation_data_hash: Hash,
93	/// The blake2-256 hash of the PoV.
94	pov_hash: Hash,
95	/// The root of a block's erasure encoding Merkle tree.
96	erasure_root: Hash,
97	/// Reserved bytes.
98	reserved2: [u8; 64],
99	/// Hash of the para header that is being generated by this candidate.
100	para_head: Hash,
101	/// The blake2-256 hash of the validation code bytes.
102	validation_code_hash: ValidationCodeHash,
103}
104impl<H> CandidateDescriptorV2<H> {
105	/// Returns the candidate descriptor version.
106	///
107	/// The candidate is at version 2 if the reserved fields are zeroed out
108	/// and the internal `version` field is 0.
109	pub fn version(&self) -> CandidateDescriptorVersion {
110		if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] {
111			return CandidateDescriptorVersion::V1
112		}
113
114		match self.version.0 {
115			0 => CandidateDescriptorVersion::V2,
116			_ => CandidateDescriptorVersion::Unknown,
117		}
118	}
119}
120
121impl<H> core::fmt::Debug for CandidateDescriptorV2<H>
122where
123	H: core::fmt::Debug,
124{
125	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
126		match self.version() {
127			CandidateDescriptorVersion::V1 => f
128				.debug_struct("CandidateDescriptorV1")
129				.field("para_id", &self.para_id)
130				.field("relay_parent", &self.relay_parent)
131				.field("persisted_validation_hash", &self.persisted_validation_data_hash)
132				.field("pov_hash", &self.pov_hash)
133				.field("erasure_root", &self.erasure_root)
134				.field("para_head", &self.para_head)
135				.field("validation_code_hash", &self.validation_code_hash)
136				.finish(),
137			CandidateDescriptorVersion::V2 => f
138				.debug_struct("CandidateDescriptorV2")
139				.field("para_id", &self.para_id)
140				.field("relay_parent", &self.relay_parent)
141				.field("core_index", &self.core_index)
142				.field("session_index", &self.session_index)
143				.field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
144				.field("pov_hash", &self.pov_hash)
145				.field("erasure_root", &self.pov_hash)
146				.field("para_head", &self.para_head)
147				.field("validation_code_hash", &self.validation_code_hash)
148				.finish(),
149			CandidateDescriptorVersion::Unknown => {
150				write!(f, "Invalid CandidateDescriptorVersion")
151			},
152		}
153	}
154}
155
156impl<H: Copy> From<CandidateDescriptorV2<H>> for CandidateDescriptor<H> {
157	fn from(value: CandidateDescriptorV2<H>) -> Self {
158		Self {
159			para_id: value.para_id,
160			relay_parent: value.relay_parent,
161			collator: value.rebuild_collator_field(),
162			persisted_validation_data_hash: value.persisted_validation_data_hash,
163			pov_hash: value.pov_hash,
164			erasure_root: value.erasure_root,
165			signature: value.rebuild_signature_field(),
166			para_head: value.para_head,
167			validation_code_hash: value.validation_code_hash,
168		}
169	}
170}
171
172fn clone_into_array<A, T>(slice: &[T]) -> A
173where
174	A: Default + AsMut<[T]>,
175	T: Clone,
176{
177	let mut a = A::default();
178	<A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
179	a
180}
181
182impl<H: Copy> From<CandidateDescriptor<H>> for CandidateDescriptorV2<H> {
183	fn from(value: CandidateDescriptor<H>) -> Self {
184		let collator = value.collator.as_slice();
185
186		Self {
187			para_id: value.para_id,
188			relay_parent: value.relay_parent,
189			// Use first byte of the `collator` field.
190			version: InternalVersion(collator[0]),
191			// Use next 2 bytes of the `collator` field.
192			core_index: u16::from_ne_bytes(clone_into_array(&collator[1..=2])),
193			// Use next 4 bytes of the `collator` field.
194			session_index: SessionIndex::from_ne_bytes(clone_into_array(&collator[3..=6])),
195			// Use remaing 25 bytes of the `collator` field.
196			reserved1: clone_into_array(&collator[7..]),
197			persisted_validation_data_hash: value.persisted_validation_data_hash,
198			pov_hash: value.pov_hash,
199			erasure_root: value.erasure_root,
200			reserved2: value.signature.into_inner().0,
201			para_head: value.para_head,
202			validation_code_hash: value.validation_code_hash,
203		}
204	}
205}
206
207impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
208	/// Constructor
209	pub fn new(
210		para_id: Id,
211		relay_parent: H,
212		core_index: CoreIndex,
213		session_index: SessionIndex,
214		persisted_validation_data_hash: Hash,
215		pov_hash: Hash,
216		erasure_root: Hash,
217		para_head: Hash,
218		validation_code_hash: ValidationCodeHash,
219	) -> Self {
220		Self {
221			para_id,
222			relay_parent,
223			version: InternalVersion(0),
224			core_index: core_index.0 as u16,
225			session_index,
226			reserved1: [0; 25],
227			persisted_validation_data_hash,
228			pov_hash,
229			erasure_root,
230			reserved2: [0; 64],
231			para_head,
232			validation_code_hash,
233		}
234	}
235
236	/// Check the signature of the collator within this descriptor.
237	pub fn check_collator_signature(&self) -> Result<(), ()> {
238		// Return `Ok` if collator signature is not included (v2+ descriptor).
239		let Some(collator) = self.collator() else { return Ok(()) };
240
241		let Some(signature) = self.signature() else { return Ok(()) };
242
243		super::v8::check_collator_signature(
244			&self.relay_parent,
245			&self.para_id,
246			&self.persisted_validation_data_hash,
247			&self.pov_hash,
248			&self.validation_code_hash,
249			&collator,
250			&signature,
251		)
252	}
253}
254
255/// A trait to allow changing the descriptor field values in tests.
256#[cfg(feature = "test")]
257
258pub trait MutateDescriptorV2<H> {
259	/// Set the relay parent of the descriptor.
260	fn set_relay_parent(&mut self, relay_parent: H);
261	/// Set the `ParaId` of the descriptor.
262	fn set_para_id(&mut self, para_id: Id);
263	/// Set the PoV hash of the descriptor.
264	fn set_pov_hash(&mut self, pov_hash: Hash);
265	/// Set the version field of the descriptor.
266	fn set_version(&mut self, version: InternalVersion);
267	/// Set the PVD of the descriptor.
268	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash);
269	/// Set the validation code hash of the descriptor.
270	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash);
271	/// Set the erasure root of the descriptor.
272	fn set_erasure_root(&mut self, erasure_root: Hash);
273	/// Set the para head of the descriptor.
274	fn set_para_head(&mut self, para_head: Hash);
275	/// Set the core index of the descriptor.
276	fn set_core_index(&mut self, core_index: CoreIndex);
277	/// Set the session index of the descriptor.
278	fn set_session_index(&mut self, session_index: SessionIndex);
279}
280
281#[cfg(feature = "test")]
282impl<H> MutateDescriptorV2<H> for CandidateDescriptorV2<H> {
283	fn set_para_id(&mut self, para_id: Id) {
284		self.para_id = para_id;
285	}
286
287	fn set_relay_parent(&mut self, relay_parent: H) {
288		self.relay_parent = relay_parent;
289	}
290
291	fn set_pov_hash(&mut self, pov_hash: Hash) {
292		self.pov_hash = pov_hash;
293	}
294
295	fn set_version(&mut self, version: InternalVersion) {
296		self.version = version;
297	}
298
299	fn set_core_index(&mut self, core_index: CoreIndex) {
300		self.core_index = core_index.0 as u16;
301	}
302
303	fn set_session_index(&mut self, session_index: SessionIndex) {
304		self.session_index = session_index;
305	}
306
307	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) {
308		self.persisted_validation_data_hash = persisted_validation_data_hash;
309	}
310
311	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) {
312		self.validation_code_hash = validation_code_hash;
313	}
314
315	fn set_erasure_root(&mut self, erasure_root: Hash) {
316		self.erasure_root = erasure_root;
317	}
318
319	fn set_para_head(&mut self, para_head: Hash) {
320		self.para_head = para_head;
321	}
322}
323
324/// A candidate-receipt at version 2.
325#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, RuntimeDebug)]
326#[cfg_attr(feature = "std", derive(Hash))]
327pub struct CandidateReceiptV2<H = Hash> {
328	/// The descriptor of the candidate.
329	pub descriptor: CandidateDescriptorV2<H>,
330	/// The hash of the encoded commitments made as a result of candidate execution.
331	pub commitments_hash: Hash,
332}
333
334/// A candidate-receipt with commitments directly included.
335#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, RuntimeDebug)]
336#[cfg_attr(feature = "std", derive(Hash))]
337pub struct CommittedCandidateReceiptV2<H = Hash> {
338	/// The descriptor of the candidate.
339	pub descriptor: CandidateDescriptorV2<H>,
340	/// The commitments of the candidate receipt.
341	pub commitments: CandidateCommitments,
342}
343
344/// An event concerning a candidate.
345#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
346#[cfg_attr(feature = "std", derive(PartialEq))]
347pub enum CandidateEvent<H = Hash> {
348	/// This candidate receipt was backed in the most recent block.
349	/// This includes the core index the candidate is now occupying.
350	#[codec(index = 0)]
351	CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
352	/// This candidate receipt was included and became a parablock at the most recent block.
353	/// This includes the core index the candidate was occupying as well as the group responsible
354	/// for backing the candidate.
355	#[codec(index = 1)]
356	CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
357	/// This candidate receipt was not made available in time and timed out.
358	/// This includes the core index the candidate was occupying.
359	#[codec(index = 2)]
360	CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
361}
362
363impl<H: Encode + Copy> From<CandidateEvent<H>> for super::v8::CandidateEvent<H> {
364	fn from(value: CandidateEvent<H>) -> Self {
365		match value {
366			CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) =>
367				super::v8::CandidateEvent::CandidateBacked(
368					receipt.into(),
369					head_data,
370					core_index,
371					group_index,
372				),
373			CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) =>
374				super::v8::CandidateEvent::CandidateIncluded(
375					receipt.into(),
376					head_data,
377					core_index,
378					group_index,
379				),
380			CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) =>
381				super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index),
382		}
383	}
384}
385
386impl<H> CandidateReceiptV2<H> {
387	/// Get a reference to the candidate descriptor.
388	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
389		&self.descriptor
390	}
391
392	/// Computes the blake2-256 hash of the receipt.
393	pub fn hash(&self) -> CandidateHash
394	where
395		H: Encode,
396	{
397		CandidateHash(BlakeTwo256::hash_of(self))
398	}
399}
400
401impl<H: Copy> From<super::v8::CandidateReceipt<H>> for CandidateReceiptV2<H> {
402	fn from(value: super::v8::CandidateReceipt<H>) -> Self {
403		CandidateReceiptV2 {
404			descriptor: value.descriptor.into(),
405			commitments_hash: value.commitments_hash,
406		}
407	}
408}
409
410impl<H: Copy> From<super::v8::CommittedCandidateReceipt<H>> for CommittedCandidateReceiptV2<H> {
411	fn from(value: super::v8::CommittedCandidateReceipt<H>) -> Self {
412		CommittedCandidateReceiptV2 {
413			descriptor: value.descriptor.into(),
414			commitments: value.commitments,
415		}
416	}
417}
418
419impl<H: Clone> CommittedCandidateReceiptV2<H> {
420	/// Transforms this into a plain `CandidateReceipt`.
421	pub fn to_plain(&self) -> CandidateReceiptV2<H> {
422		CandidateReceiptV2 {
423			descriptor: self.descriptor.clone(),
424			commitments_hash: self.commitments.hash(),
425		}
426	}
427
428	/// Computes the hash of the committed candidate receipt.
429	///
430	/// This computes the canonical hash, not the hash of the directly encoded data.
431	/// Thus this is a shortcut for `candidate.to_plain().hash()`.
432	pub fn hash(&self) -> CandidateHash
433	where
434		H: Encode,
435	{
436		self.to_plain().hash()
437	}
438
439	/// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]?
440	pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
441	where
442		H: PartialEq,
443	{
444		receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
445	}
446}
447
448impl PartialOrd for CommittedCandidateReceiptV2 {
449	fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
450		Some(self.cmp(other))
451	}
452}
453
454impl Ord for CommittedCandidateReceiptV2 {
455	fn cmp(&self, other: &Self) -> core::cmp::Ordering {
456		self.descriptor
457			.para_id
458			.cmp(&other.descriptor.para_id)
459			.then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
460	}
461}
462
463impl<H: Copy> From<CommittedCandidateReceiptV2<H>> for super::v8::CommittedCandidateReceipt<H> {
464	fn from(value: CommittedCandidateReceiptV2<H>) -> Self {
465		Self { descriptor: value.descriptor.into(), commitments: value.commitments }
466	}
467}
468
469impl<H: Copy> From<CandidateReceiptV2<H>> for super::v8::CandidateReceipt<H> {
470	fn from(value: CandidateReceiptV2<H>) -> Self {
471		Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash }
472	}
473}
474
475/// A strictly increasing sequence number, typically this would be the least significant byte of the
476/// block number.
477#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
478pub struct CoreSelector(pub u8);
479
480/// An offset in the relay chain claim queue.
481#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
482pub struct ClaimQueueOffset(pub u8);
483
484/// Approved PeerId type. PeerIds in polkadot should typically be 32 bytes long but for identity
485/// multihash can go up to 64. Cannot reuse the PeerId type definition from the networking code as
486/// it's too generic and extensible.
487pub type ApprovedPeerId = BoundedVec<u8, ConstU32<64>>;
488
489/// Signals that a parachain can send to the relay chain via the UMP queue.
490#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
491pub enum UMPSignal {
492	/// A message sent by a parachain to select the core the candidate is committed to.
493	/// Relay chain validators, in particular backers, use the `CoreSelector` and
494	/// `ClaimQueueOffset` to compute the index of the core the candidate has committed to.
495	SelectCore(CoreSelector, ClaimQueueOffset),
496	/// A message sent by a parachain to promote the reputation of a given peerid.
497	ApprovedPeer(ApprovedPeerId),
498}
499
500#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug, Default)]
501/// User-friendly representation of a candidate's UMP signals.
502pub struct CandidateUMPSignals {
503	select_core: Option<(CoreSelector, ClaimQueueOffset)>,
504	approved_peer: Option<ApprovedPeerId>,
505}
506
507impl CandidateUMPSignals {
508	/// Get the core selector UMP signal.
509	pub fn core_selector(&self) -> Option<(CoreSelector, ClaimQueueOffset)> {
510		self.select_core
511	}
512
513	/// Get a reference to the approved peer UMP signal.
514	pub fn approved_peer(&self) -> Option<&ApprovedPeerId> {
515		self.approved_peer.as_ref()
516	}
517
518	/// Returns `true` if UMP signals are empty.
519	pub fn is_empty(&self) -> bool {
520		self.select_core.is_none() && self.approved_peer.is_none()
521	}
522
523	fn try_decode_signal(
524		&mut self,
525		buffer: &mut impl codec::Input,
526	) -> Result<(), CommittedCandidateReceiptError> {
527		match UMPSignal::decode(buffer)
528			.map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)?
529		{
530			UMPSignal::ApprovedPeer(approved_peer_id) if self.approved_peer.is_none() => {
531				self.approved_peer = Some(approved_peer_id);
532			},
533			UMPSignal::SelectCore(core_selector, cq_offset) if self.select_core.is_none() => {
534				self.select_core = Some((core_selector, cq_offset));
535			},
536			_ => {
537				// This means that we got duplicate UMP signals.
538				return Err(CommittedCandidateReceiptError::DuplicateUMPSignal)
539			},
540		};
541
542		Ok(())
543	}
544}
545
546/// Separator between `XCM` and `UMPSignal`.
547pub const UMP_SEPARATOR: Vec<u8> = vec![];
548
549/// Utility function for skipping the ump signals.
550pub fn skip_ump_signals<'a>(
551	upward_messages: impl Iterator<Item = &'a Vec<u8>>,
552) -> impl Iterator<Item = &'a Vec<u8>> {
553	upward_messages.take_while(|message| *message != &UMP_SEPARATOR)
554}
555
556impl CandidateCommitments {
557	/// Returns the ump signals of this candidate, if any, or an error if they violate the expected
558	/// format.
559	pub fn ump_signals(&self) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
560		let mut res = CandidateUMPSignals::default();
561
562		let mut signals_iter =
563			self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR);
564
565		if signals_iter.next().is_none() {
566			// No UMP separator
567			return Ok(res)
568		}
569
570		// Process first signal
571		let Some(first_signal) = signals_iter.next() else { return Ok(res) };
572		res.try_decode_signal(&mut first_signal.as_slice())?;
573
574		// Process second signal
575		let Some(second_signal) = signals_iter.next() else { return Ok(res) };
576		res.try_decode_signal(&mut second_signal.as_slice())?;
577
578		// At most two signals are allowed
579		if signals_iter.next().is_some() {
580			return Err(CommittedCandidateReceiptError::TooManyUMPSignals)
581		}
582
583		Ok(res)
584	}
585}
586
587/// CommittedCandidateReceiptError construction errors.
588#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
589#[cfg_attr(feature = "std", derive(thiserror::Error))]
590pub enum CommittedCandidateReceiptError {
591	/// The specified core index is invalid.
592	#[cfg_attr(feature = "std", error("The specified core index is invalid"))]
593	InvalidCoreIndex,
594	/// The core index in commitments doesn't match the one in descriptor
595	#[cfg_attr(
596		feature = "std",
597		error("The core index in commitments ({commitments:?}) doesn't match the one in descriptor ({descriptor:?})")
598	)]
599	CoreIndexMismatch {
600		/// The core index as found in the descriptor.
601		descriptor: CoreIndex,
602		/// The core index as found in the commitments.
603		commitments: CoreIndex,
604	},
605	/// The core selector or claim queue offset is invalid.
606	#[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))]
607	InvalidSelectedCore,
608	#[cfg_attr(feature = "std", error("Could not decode UMP signal"))]
609	/// Could not decode UMP signal.
610	UmpSignalDecode,
611	/// The parachain is not assigned to any core at specified claim queue offset.
612	#[cfg_attr(
613		feature = "std",
614		error("The parachain is not assigned to any core at specified claim queue offset")
615	)]
616	NoAssignment,
617	/// Unknown version.
618	#[cfg_attr(feature = "std", error("Unknown internal version"))]
619	UnknownVersion(InternalVersion),
620	/// The allowed number of `UMPSignal` messages in the queue was exceeded.
621	#[cfg_attr(feature = "std", error("Too many UMP signals"))]
622	TooManyUMPSignals,
623	/// Duplicated UMP signal.
624	#[cfg_attr(feature = "std", error("Duplicate UMP signal"))]
625	DuplicateUMPSignal,
626	/// If the parachain runtime started sending ump signals, v1 descriptors are no longer
627	/// allowed.
628	#[cfg_attr(feature = "std", error("Version 1 receipt does not support ump signals"))]
629	UMPSignalWithV1Decriptor,
630}
631
632macro_rules! impl_getter {
633	($field:ident, $type:ident) => {
634		/// Returns the value of `$field` field.
635		pub fn $field(&self) -> $type {
636			self.$field
637		}
638	};
639}
640
641impl<H: Copy> CandidateDescriptorV2<H> {
642	impl_getter!(erasure_root, Hash);
643	impl_getter!(para_head, Hash);
644	impl_getter!(relay_parent, H);
645	impl_getter!(para_id, ParaId);
646	impl_getter!(persisted_validation_data_hash, Hash);
647	impl_getter!(pov_hash, Hash);
648	impl_getter!(validation_code_hash, ValidationCodeHash);
649
650	fn rebuild_collator_field(&self) -> CollatorId {
651		let mut collator_id = Vec::with_capacity(32);
652		let core_index: [u8; 2] = self.core_index.to_ne_bytes();
653		let session_index: [u8; 4] = self.session_index.to_ne_bytes();
654
655		collator_id.push(self.version.0);
656		collator_id.extend_from_slice(core_index.as_slice());
657		collator_id.extend_from_slice(session_index.as_slice());
658		collator_id.extend_from_slice(self.reserved1.as_slice());
659
660		CollatorId::from_slice(&collator_id.as_slice())
661			.expect("Slice size is exactly 32 bytes; qed")
662	}
663
664	/// Returns the collator id if this is a v1 `CandidateDescriptor`
665	pub fn collator(&self) -> Option<CollatorId> {
666		if self.version() == CandidateDescriptorVersion::V1 {
667			Some(self.rebuild_collator_field())
668		} else {
669			None
670		}
671	}
672
673	fn rebuild_signature_field(&self) -> CollatorSignature {
674		CollatorSignature::from_slice(self.reserved2.as_slice())
675			.expect("Slice size is exactly 64 bytes; qed")
676	}
677
678	/// Returns the collator signature of `V1` candidate descriptors, `None` otherwise.
679	pub fn signature(&self) -> Option<CollatorSignature> {
680		if self.version() == CandidateDescriptorVersion::V1 {
681			return Some(self.rebuild_signature_field())
682		}
683
684		None
685	}
686
687	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
688	pub fn core_index(&self) -> Option<CoreIndex> {
689		if self.version() == CandidateDescriptorVersion::V1 {
690			return None
691		}
692
693		Some(CoreIndex(self.core_index as u32))
694	}
695
696	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
697	pub fn session_index(&self) -> Option<SessionIndex> {
698		if self.version() == CandidateDescriptorVersion::V1 {
699			return None
700		}
701
702		Some(self.session_index)
703	}
704}
705
706impl<H: Copy> CommittedCandidateReceiptV2<H> {
707	/// Performs checks on the UMP signals and returns them.
708	///
709	/// Also checks if descriptor core index is equal to the committed core index.
710	///
711	/// Params:
712	/// - `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as
713	/// a mapping between `ParaId` and the cores assigned per depth.
714	pub fn parse_ump_signals(
715		&self,
716		cores_per_para: &TransposedClaimQueue,
717	) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
718		let signals = self.commitments.ump_signals()?;
719
720		match self.descriptor.version() {
721			CandidateDescriptorVersion::V1 => {
722				// If the parachain runtime started sending ump signals, v1 descriptors are no
723				// longer allowed.
724				if !signals.is_empty() {
725					return Err(CommittedCandidateReceiptError::UMPSignalWithV1Decriptor)
726				} else {
727					// Nothing else to check for v1 descriptors.
728					return Ok(CandidateUMPSignals::default())
729				}
730			},
731			CandidateDescriptorVersion::V2 => {},
732			CandidateDescriptorVersion::Unknown =>
733				return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version)),
734		}
735
736		// Check the core index
737		let (maybe_core_index_selector, cq_offset) = signals
738			.core_selector()
739			.map(|(selector, offset)| (Some(selector), offset))
740			.unwrap_or_else(|| (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)));
741
742		self.check_core_index(cores_per_para, maybe_core_index_selector, cq_offset)?;
743
744		// Nothing to further check for the approved peer. If everything passed so far, return the
745		// signals.
746		Ok(signals)
747	}
748
749	/// Checks if descriptor core index is equal to the committed core index.
750	/// Input `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as
751	/// a mapping between `ParaId` and the cores assigned per depth.
752	fn check_core_index(
753		&self,
754		cores_per_para: &TransposedClaimQueue,
755		maybe_core_index_selector: Option<CoreSelector>,
756		cq_offset: ClaimQueueOffset,
757	) -> Result<(), CommittedCandidateReceiptError> {
758		let assigned_cores = cores_per_para
759			.get(&self.descriptor.para_id())
760			.ok_or(CommittedCandidateReceiptError::NoAssignment)?
761			.get(&cq_offset.0)
762			.ok_or(CommittedCandidateReceiptError::NoAssignment)?;
763
764		if assigned_cores.is_empty() {
765			return Err(CommittedCandidateReceiptError::NoAssignment)
766		}
767
768		let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
769
770		let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector {
771			// We have a committed core selector, we can use it.
772			core_index_selector
773		} else if assigned_cores.len() > 1 {
774			// We got more than one assigned core and no core selector. Special care is needed.
775			if !assigned_cores.contains(&descriptor_core_index) {
776				// core index in the descriptor is not assigned to the para. Error.
777				return Err(CommittedCandidateReceiptError::InvalidCoreIndex)
778			} else {
779				// the descriptor core index is indeed assigned to the para. This is the most we can
780				// check for now
781				return Ok(())
782			}
783		} else {
784			// No core selector but there's only one assigned core, use it.
785			CoreSelector(0)
786		};
787
788		let core_index = assigned_cores
789			.iter()
790			.nth(core_index_selector.0 as usize % assigned_cores.len())
791			.ok_or(CommittedCandidateReceiptError::InvalidSelectedCore)
792			.copied()?;
793
794		if core_index != descriptor_core_index {
795			return Err(CommittedCandidateReceiptError::CoreIndexMismatch {
796				descriptor: descriptor_core_index,
797				commitments: core_index,
798			})
799		}
800
801		Ok(())
802	}
803}
804
805/// A backed (or backable, depending on context) candidate.
806#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
807pub struct BackedCandidate<H = Hash> {
808	/// The candidate referred to.
809	candidate: CommittedCandidateReceiptV2<H>,
810	/// The validity votes themselves, expressed as signatures.
811	validity_votes: Vec<ValidityAttestation>,
812	/// The indices of the validators within the group, expressed as a bitfield. May be extended
813	/// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is
814	/// enabled.
815	validator_indices: BitVec<u8, bitvec::order::Lsb0>,
816}
817
818/// Parachains inherent-data passed into the runtime by a block author
819#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, RuntimeDebug, TypeInfo)]
820pub struct InherentData<HDR: HeaderT = Header> {
821	/// Signed bitfields by validators about availability.
822	pub bitfields: UncheckedSignedAvailabilityBitfields,
823	/// Backed candidates for inclusion in the block.
824	pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
825	/// Sets of dispute votes for inclusion,
826	pub disputes: MultiDisputeStatementSet,
827	/// The parent block header. Used for checking state proofs.
828	pub parent_header: HDR,
829}
830
831impl<H> BackedCandidate<H> {
832	/// Constructor
833	pub fn new(
834		candidate: CommittedCandidateReceiptV2<H>,
835		validity_votes: Vec<ValidityAttestation>,
836		validator_indices: BitVec<u8, bitvec::order::Lsb0>,
837		core_index: CoreIndex,
838	) -> Self {
839		let mut instance = Self { candidate, validity_votes, validator_indices };
840		instance.inject_core_index(core_index);
841		instance
842	}
843
844	/// Get a reference to the committed candidate receipt of the candidate.
845	pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
846		&self.candidate
847	}
848
849	/// Get a mutable reference to the committed candidate receipt of the candidate.
850	/// Only for testing.
851	#[cfg(feature = "test")]
852	pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2<H> {
853		&mut self.candidate
854	}
855	/// Get a reference to the descriptor of the candidate.
856	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
857		&self.candidate.descriptor
858	}
859
860	/// Get a mutable reference to the descriptor of the candidate. Only for testing.
861	#[cfg(feature = "test")]
862	pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
863		&mut self.candidate.descriptor
864	}
865
866	/// Get a reference to the validity votes of the candidate.
867	pub fn validity_votes(&self) -> &[ValidityAttestation] {
868		&self.validity_votes
869	}
870
871	/// Get a mutable reference to validity votes of the para.
872	pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
873		&mut self.validity_votes
874	}
875
876	/// Compute this candidate's hash.
877	pub fn hash(&self) -> CandidateHash
878	where
879		H: Clone + Encode,
880	{
881		self.candidate.to_plain().hash()
882	}
883
884	/// Get this candidate's receipt.
885	pub fn receipt(&self) -> CandidateReceiptV2<H>
886	where
887		H: Clone,
888	{
889		self.candidate.to_plain()
890	}
891
892	/// Get a copy of the validator indices and the assumed core index, if any.
893	pub fn validator_indices_and_core_index(
894		&self,
895	) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
896		// `BackedCandidate::validity_indices` are extended to store a 8 bit core index.
897		let core_idx_offset = self.validator_indices.len().saturating_sub(8);
898		if core_idx_offset > 0 {
899			let (validator_indices_slice, core_idx_slice) =
900				self.validator_indices.split_at(core_idx_offset);
901			return (validator_indices_slice, Some(CoreIndex(core_idx_slice.load::<u8>() as u32)));
902		}
903
904		(&self.validator_indices, None)
905	}
906
907	/// Inject a core index in the validator_indices bitvec.
908	fn inject_core_index(&mut self, core_index: CoreIndex) {
909		let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
910			BitVec::from_vec(vec![core_index.0 as u8]);
911		self.validator_indices.extend(core_index_to_inject);
912	}
913
914	/// Update the validator indices and core index in the candidate.
915	pub fn set_validator_indices_and_core_index(
916		&mut self,
917		new_indices: BitVec<u8, bitvec::order::Lsb0>,
918		maybe_core_index: Option<CoreIndex>,
919	) {
920		self.validator_indices = new_indices;
921
922		if let Some(core_index) = maybe_core_index {
923			self.inject_core_index(core_index);
924		}
925	}
926}
927
928/// Scraped runtime backing votes and resolved disputes.
929#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
930#[cfg_attr(feature = "std", derive(PartialEq))]
931pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
932	/// The session in which the block was included.
933	pub session: SessionIndex,
934	/// Set of backing validators for each candidate, represented by its candidate
935	/// receipt.
936	pub backing_validators_per_candidate:
937		Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
938	/// On-chain-recorded set of disputes.
939	/// Note that the above `backing_validators` are
940	/// unrelated to the backers of the disputes candidates.
941	pub disputes: MultiDisputeStatementSet,
942}
943
944impl<H: Encode + Decode + Copy> From<ScrapedOnChainVotes<H>> for super::v8::ScrapedOnChainVotes<H> {
945	fn from(value: ScrapedOnChainVotes<H>) -> Self {
946		Self {
947			session: value.session,
948			backing_validators_per_candidate: value
949				.backing_validators_per_candidate
950				.into_iter()
951				.map(|(receipt, validators)| (receipt.into(), validators))
952				.collect::<Vec<_>>(),
953			disputes: value.disputes,
954		}
955	}
956}
957
958/// Information about a core which is currently occupied.
959#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
960#[cfg_attr(feature = "std", derive(PartialEq))]
961pub struct OccupiedCore<H = Hash, N = BlockNumber> {
962	// NOTE: this has no ParaId as it can be deduced from the candidate descriptor.
963	/// If this core is freed by availability, this is the assignment that is next up on this
964	/// core, if any. None if there is nothing queued for this core.
965	pub next_up_on_available: Option<ScheduledCore>,
966	/// The relay-chain block number this began occupying the core at.
967	pub occupied_since: N,
968	/// The relay-chain block this will time-out at, if any.
969	pub time_out_at: N,
970	/// If this core is freed by being timed-out, this is the assignment that is next up on this
971	/// core. None if there is nothing queued for this core or there is no possibility of timing
972	/// out.
973	pub next_up_on_time_out: Option<ScheduledCore>,
974	/// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
975	/// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
976	/// this will be available.
977	pub availability: BitVec<u8, bitvec::order::Lsb0>,
978	/// The group assigned to distribute availability pieces of this candidate.
979	pub group_responsible: GroupIndex,
980	/// The hash of the candidate occupying the core.
981	pub candidate_hash: CandidateHash,
982	/// The descriptor of the candidate occupying the core.
983	pub candidate_descriptor: CandidateDescriptorV2<H>,
984}
985
986impl<H, N> OccupiedCore<H, N> {
987	/// Get the Para currently occupying this core.
988	pub fn para_id(&self) -> Id {
989		self.candidate_descriptor.para_id
990	}
991}
992
993/// The state of a particular availability core.
994#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
995#[cfg_attr(feature = "std", derive(PartialEq))]
996pub enum CoreState<H = Hash, N = BlockNumber> {
997	/// The core is currently occupied.
998	#[codec(index = 0)]
999	Occupied(OccupiedCore<H, N>),
1000	/// The core is currently free, with a para scheduled and given the opportunity
1001	/// to occupy.
1002	///
1003	/// If a particular Collator is required to author this block, that is also present in this
1004	/// variant.
1005	#[codec(index = 1)]
1006	Scheduled(ScheduledCore),
1007	/// The core is currently free and there is nothing scheduled. This can be the case for
1008	/// parathread cores when there are no parathread blocks queued. Parachain cores will never be
1009	/// left idle.
1010	#[codec(index = 2)]
1011	Free,
1012}
1013
1014impl<N> CoreState<N> {
1015	/// Returns the scheduled `ParaId` for the core or `None` if nothing is scheduled.
1016	///
1017	/// This function is deprecated. `ClaimQueue` should be used to obtain the scheduled `ParaId`s
1018	/// for each core.
1019	#[deprecated(
1020		note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead."
1021	)]
1022	pub fn para_id(&self) -> Option<Id> {
1023		match self {
1024			Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id),
1025			Self::Scheduled(core) => Some(core.para_id),
1026			Self::Free => None,
1027		}
1028	}
1029
1030	/// Is this core state `Self::Occupied`?
1031	pub fn is_occupied(&self) -> bool {
1032		matches!(self, Self::Occupied(_))
1033	}
1034}
1035
1036impl<H: Copy> From<OccupiedCore<H>> for super::v8::OccupiedCore<H> {
1037	fn from(value: OccupiedCore<H>) -> Self {
1038		Self {
1039			next_up_on_available: value.next_up_on_available,
1040			occupied_since: value.occupied_since,
1041			time_out_at: value.time_out_at,
1042			next_up_on_time_out: value.next_up_on_time_out,
1043			availability: value.availability,
1044			group_responsible: value.group_responsible,
1045			candidate_hash: value.candidate_hash,
1046			candidate_descriptor: value.candidate_descriptor.into(),
1047		}
1048	}
1049}
1050
1051impl<H: Copy> From<CoreState<H>> for super::v8::CoreState<H> {
1052	fn from(value: CoreState<H>) -> Self {
1053		match value {
1054			CoreState::Free => super::v8::CoreState::Free,
1055			CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core),
1056			CoreState::Occupied(occupied_core) =>
1057				super::v8::CoreState::Occupied(occupied_core.into()),
1058		}
1059	}
1060}
1061
1062/// The claim queue mapped by parachain id.
1063pub type TransposedClaimQueue = BTreeMap<ParaId, BTreeMap<u8, BTreeSet<CoreIndex>>>;
1064
1065/// Returns a mapping between the para id and the core indices assigned at different
1066/// depths in the claim queue.
1067pub fn transpose_claim_queue(
1068	claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
1069) -> TransposedClaimQueue {
1070	let mut per_para_claim_queue = BTreeMap::new();
1071
1072	for (core, paras) in claim_queue {
1073		// Iterate paras assigned to this core at each depth.
1074		for (depth, para) in paras.into_iter().enumerate() {
1075			let depths: &mut BTreeMap<u8, BTreeSet<CoreIndex>> =
1076				per_para_claim_queue.entry(para).or_insert_with(|| Default::default());
1077
1078			depths.entry(depth as u8).or_default().insert(core);
1079		}
1080	}
1081
1082	per_para_claim_queue
1083}
1084
1085#[cfg(test)]
1086mod candidate_receipt_tests {
1087	use super::*;
1088	use crate::{
1089		v8::{
1090			tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt,
1091			CommittedCandidateReceipt, Hash, HeadData, ValidationCode,
1092		},
1093		vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2},
1094	};
1095
1096	fn dummy_collator_signature() -> CollatorSignature {
1097		CollatorSignature::from_slice(&mut (0..64).into_iter().collect::<Vec<_>>().as_slice())
1098			.expect("64 bytes; qed")
1099	}
1100
1101	fn dummy_collator_id() -> CollatorId {
1102		CollatorId::from_slice(&mut (0..32).into_iter().collect::<Vec<_>>().as_slice())
1103			.expect("32 bytes; qed")
1104	}
1105
1106	pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 {
1107		let zeros = Hash::zero();
1108		let reserved2 = [0; 64];
1109
1110		CommittedCandidateReceiptV2 {
1111			descriptor: CandidateDescriptorV2 {
1112				para_id: 0.into(),
1113				relay_parent: zeros,
1114				version: InternalVersion(0),
1115				core_index: 123,
1116				session_index: 1,
1117				reserved1: Default::default(),
1118				persisted_validation_data_hash: zeros,
1119				pov_hash: zeros,
1120				erasure_root: zeros,
1121				reserved2,
1122				para_head: zeros,
1123				validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(),
1124			},
1125			commitments: CandidateCommitments {
1126				head_data: HeadData(vec![]),
1127				upward_messages: vec![].try_into().expect("empty vec fits within bounds"),
1128				new_validation_code: None,
1129				horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"),
1130				processed_downward_messages: 0,
1131				hrmp_watermark: 0_u32,
1132			},
1133		}
1134	}
1135
1136	#[test]
1137	fn is_binary_compatibile() {
1138		let old_ccr = dummy_old_committed_candidate_receipt();
1139		let new_ccr = dummy_committed_candidate_receipt_v2();
1140
1141		assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size());
1142
1143		let encoded_old = old_ccr.encode();
1144
1145		// Deserialize from old candidate receipt.
1146		let new_ccr: CommittedCandidateReceiptV2 =
1147			Decode::decode(&mut encoded_old.as_slice()).unwrap();
1148
1149		// We get same candidate hash.
1150		assert_eq!(old_ccr.hash(), new_ccr.hash());
1151	}
1152
1153	#[test]
1154	fn test_from_v1_descriptor() {
1155		let mut old_ccr = dummy_old_committed_candidate_receipt().to_plain();
1156		old_ccr.descriptor.collator = dummy_collator_id();
1157		old_ccr.descriptor.signature = dummy_collator_signature();
1158
1159		let mut new_ccr = dummy_committed_candidate_receipt_v2().to_plain();
1160
1161		// Override descriptor from old candidate receipt.
1162		new_ccr.descriptor = old_ccr.descriptor.clone().into();
1163
1164		// We get same candidate hash.
1165		assert_eq!(old_ccr.hash(), new_ccr.hash());
1166
1167		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V1);
1168		assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap());
1169		assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap());
1170	}
1171
1172	#[test]
1173	fn invalid_version_descriptor() {
1174		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1175		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2);
1176		// Put some unknown version.
1177		new_ccr.descriptor.version = InternalVersion(100);
1178
1179		// Deserialize as V1.
1180		let new_ccr: CommittedCandidateReceiptV2 =
1181			Decode::decode(&mut new_ccr.encode().as_slice()).unwrap();
1182
1183		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown);
1184		assert_eq!(
1185			new_ccr.parse_ump_signals(&BTreeMap::new()),
1186			Err(CommittedCandidateReceiptError::UnknownVersion(InternalVersion(100)))
1187		);
1188	}
1189
1190	#[test]
1191	// Test valid scenarios for parse_ump_signals():
1192	// - no signals
1193	// - only selected core signal
1194	// - only approved peer signal
1195	// - both signals in any order
1196	fn test_ump_commitments() {
1197		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1198		new_ccr.descriptor.core_index = 123;
1199		new_ccr.descriptor.para_id = ParaId::new(1000);
1200
1201		let mut cq = BTreeMap::new();
1202		cq.insert(
1203			CoreIndex(123),
1204			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1205		);
1206		let cq = transpose_claim_queue(cq);
1207
1208		// No commitments
1209
1210		// dummy XCM messages
1211		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1212		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1213
1214		assert_eq!(
1215			new_ccr.parse_ump_signals(&cq),
1216			Ok(CandidateUMPSignals { select_core: None, approved_peer: None })
1217		);
1218
1219		// separator
1220		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1221
1222		assert_eq!(
1223			new_ccr.parse_ump_signals(&cq),
1224			Ok(CandidateUMPSignals { select_core: None, approved_peer: None })
1225		);
1226
1227		// CoreIndex commitment
1228		{
1229			let mut new_ccr = new_ccr.clone();
1230			new_ccr
1231				.commitments
1232				.upward_messages
1233				.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1234
1235			assert_eq!(
1236				new_ccr.parse_ump_signals(&cq),
1237				Ok(CandidateUMPSignals {
1238					select_core: Some((CoreSelector(0), ClaimQueueOffset(1))),
1239					approved_peer: None
1240				})
1241			);
1242		}
1243
1244		{
1245			let mut new_ccr = new_ccr.clone();
1246
1247			// Test having only an approved peer.
1248			new_ccr
1249				.commitments
1250				.upward_messages
1251				.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1252
1253			assert_eq!(
1254				new_ccr.parse_ump_signals(&cq),
1255				Ok(CandidateUMPSignals {
1256					select_core: None,
1257					approved_peer: Some(vec![1, 2, 3].try_into().unwrap())
1258				})
1259			);
1260
1261			// Test having an approved peer and a core selector.
1262
1263			new_ccr
1264				.commitments
1265				.upward_messages
1266				.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1267
1268			assert_eq!(
1269				new_ccr.parse_ump_signals(&cq),
1270				Ok(CandidateUMPSignals {
1271					select_core: Some((CoreSelector(0), ClaimQueueOffset(1))),
1272					approved_peer: Some(vec![1, 2, 3].try_into().unwrap())
1273				})
1274			);
1275		}
1276
1277		// Test having a core selector and an approved peer.
1278		new_ccr
1279			.commitments
1280			.upward_messages
1281			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1282		new_ccr
1283			.commitments
1284			.upward_messages
1285			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1286
1287		assert_eq!(
1288			new_ccr.parse_ump_signals(&cq),
1289			Ok(CandidateUMPSignals {
1290				select_core: Some((CoreSelector(0), ClaimQueueOffset(1))),
1291				approved_peer: Some(vec![1, 2, 3].try_into().unwrap())
1292			})
1293		);
1294	}
1295
1296	#[test]
1297	fn test_invalid_ump_commitments() {
1298		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1299		new_ccr.descriptor.core_index = 0;
1300		new_ccr.descriptor.para_id = ParaId::new(1000);
1301
1302		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1303
1304		let mut cq = BTreeMap::new();
1305		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1306		let cq = transpose_claim_queue(cq);
1307
1308		// Add an approved peer message.
1309		new_ccr
1310			.commitments
1311			.upward_messages
1312			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1313
1314		// Garbage message.
1315		new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode());
1316
1317		// No signals can be decoded.
1318		assert_eq!(
1319			new_ccr.parse_ump_signals(&cq),
1320			Err(CommittedCandidateReceiptError::UmpSignalDecode)
1321		);
1322		assert_eq!(
1323			new_ccr.commitments.ump_signals(),
1324			Err(CommittedCandidateReceiptError::UmpSignalDecode)
1325		);
1326
1327		// Verify core index checks.
1328		{
1329			// Has two cores assigned but no core commitment. Will pass the check if the descriptor
1330			// core index is indeed assigned to the para.
1331			new_ccr.commitments.upward_messages.clear();
1332			new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1333			new_ccr
1334				.commitments
1335				.upward_messages
1336				.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1337
1338			let mut cq = BTreeMap::new();
1339			cq.insert(
1340				CoreIndex(0),
1341				vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1342			);
1343			cq.insert(
1344				CoreIndex(100),
1345				vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1346			);
1347			let cq = transpose_claim_queue(cq);
1348
1349			assert_eq!(
1350				new_ccr.parse_ump_signals(&cq),
1351				Ok(CandidateUMPSignals {
1352					select_core: None,
1353					approved_peer: Some(vec![1, 2, 3].try_into().unwrap())
1354				})
1355			);
1356
1357			new_ccr.descriptor.set_core_index(CoreIndex(1));
1358			assert_eq!(
1359				new_ccr.parse_ump_signals(&cq),
1360				Err(CommittedCandidateReceiptError::InvalidCoreIndex)
1361			);
1362			new_ccr.descriptor.set_core_index(CoreIndex(0));
1363
1364			new_ccr
1365				.commitments
1366				.upward_messages
1367				.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1368
1369			// No assignments.
1370			assert_eq!(
1371				new_ccr.parse_ump_signals(&transpose_claim_queue(Default::default())),
1372				Err(CommittedCandidateReceiptError::NoAssignment)
1373			);
1374
1375			// Mismatch between descriptor index and commitment.
1376			new_ccr.descriptor.set_core_index(CoreIndex(1));
1377			assert_eq!(
1378				new_ccr.parse_ump_signals(&cq),
1379				Err(CommittedCandidateReceiptError::CoreIndexMismatch {
1380					descriptor: CoreIndex(1),
1381					commitments: CoreIndex(0),
1382				})
1383			);
1384		}
1385
1386		new_ccr.descriptor.set_core_index(CoreIndex(0));
1387
1388		// Add two ApprovedPeer messages
1389		new_ccr.commitments.upward_messages.clear();
1390		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1391		new_ccr
1392			.commitments
1393			.upward_messages
1394			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1395		new_ccr
1396			.commitments
1397			.upward_messages
1398			.force_push(UMPSignal::ApprovedPeer(vec![4, 5].try_into().unwrap()).encode());
1399
1400		assert_eq!(
1401			new_ccr.parse_ump_signals(&cq),
1402			Err(CommittedCandidateReceiptError::DuplicateUMPSignal)
1403		);
1404
1405		// Too many
1406		new_ccr.commitments.upward_messages.clear();
1407		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1408		new_ccr
1409			.commitments
1410			.upward_messages
1411			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1412		new_ccr
1413			.commitments
1414			.upward_messages
1415			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(0)).encode());
1416		new_ccr
1417			.commitments
1418			.upward_messages
1419			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1420
1421		assert_eq!(
1422			new_ccr.parse_ump_signals(&cq),
1423			Err(CommittedCandidateReceiptError::TooManyUMPSignals)
1424		);
1425	}
1426
1427	#[test]
1428	fn test_version2_receipts_decoded_as_v1() {
1429		let mut new_ccr = dummy_committed_candidate_receipt_v2();
1430		new_ccr.descriptor.core_index = 123;
1431		new_ccr.descriptor.para_id = ParaId::new(1000);
1432
1433		// dummy XCM messages
1434		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1435		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1436
1437		// separator
1438		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1439
1440		// CoreIndex commitment
1441		new_ccr
1442			.commitments
1443			.upward_messages
1444			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1445
1446		let encoded_ccr = new_ccr.encode();
1447		let decoded_ccr: CommittedCandidateReceipt =
1448			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1449
1450		assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent());
1451		assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id());
1452
1453		assert_eq!(new_ccr.hash(), decoded_ccr.hash());
1454
1455		// Encode v1 and decode as V2
1456		let encoded_ccr = new_ccr.encode();
1457		let v2_ccr: CommittedCandidateReceiptV2 =
1458			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1459
1460		assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123)));
1461
1462		let mut cq = BTreeMap::new();
1463		cq.insert(
1464			CoreIndex(123),
1465			vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1466		);
1467
1468		assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
1469
1470		assert_eq!(new_ccr.hash(), v2_ccr.hash());
1471	}
1472
1473	// V1 descriptors are forbidden once the parachain runtime started sending UMP signals.
1474	#[test]
1475	fn test_v1_descriptors_with_ump_signal() {
1476		let mut ccr = dummy_old_committed_candidate_receipt();
1477		ccr.descriptor.para_id = ParaId::new(1024);
1478		// Adding collator signature should make it decode as v1.
1479		ccr.descriptor.signature = dummy_collator_signature();
1480		ccr.descriptor.collator = dummy_collator_id();
1481
1482		ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1483		ccr.commitments
1484			.upward_messages
1485			.force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
1486
1487		ccr.commitments
1488			.upward_messages
1489			.force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1490
1491		let encoded_ccr: Vec<u8> = ccr.encode();
1492
1493		let v1_ccr: CommittedCandidateReceiptV2 =
1494			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1495
1496		assert_eq!(v1_ccr.descriptor.version(), CandidateDescriptorVersion::V1);
1497		assert!(!v1_ccr.commitments.ump_signals().unwrap().is_empty());
1498
1499		let mut cq = BTreeMap::new();
1500		cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into());
1501		cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into());
1502
1503		assert_eq!(v1_ccr.descriptor.core_index(), None);
1504
1505		assert_eq!(
1506			v1_ccr.parse_ump_signals(&transpose_claim_queue(cq)),
1507			Err(CommittedCandidateReceiptError::UMPSignalWithV1Decriptor)
1508		);
1509	}
1510
1511	#[test]
1512	fn test_core_select_is_optional() {
1513		// Testing edge case when collators provide zeroed signature and collator id.
1514		let mut old_ccr = dummy_old_committed_candidate_receipt();
1515		old_ccr.descriptor.para_id = ParaId::new(1000);
1516		let encoded_ccr: Vec<u8> = old_ccr.encode();
1517
1518		let new_ccr: CommittedCandidateReceiptV2 =
1519			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1520
1521		let mut cq = BTreeMap::new();
1522		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1523
1524		// Since collator sig and id are zeroed, it means that the descriptor uses format
1525		// version 2. Should still pass checks without core selector.
1526		assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
1527
1528		let mut cq = BTreeMap::new();
1529		cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1530		cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into());
1531
1532		// Passes even if 2 cores are assigned, because elastic scaling MVP could still inject the
1533		// core index in the `BackedCandidate`.
1534		assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
1535
1536		// Adding collator signature should make it decode as v1.
1537		old_ccr.descriptor.signature = dummy_collator_signature();
1538		old_ccr.descriptor.collator = dummy_collator_id();
1539
1540		let old_ccr_hash = old_ccr.hash();
1541
1542		let encoded_ccr: Vec<u8> = old_ccr.encode();
1543
1544		let new_ccr: CommittedCandidateReceiptV2 =
1545			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1546
1547		assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature));
1548		assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator));
1549
1550		assert_eq!(new_ccr.descriptor.core_index(), None);
1551		assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000));
1552
1553		assert_eq!(old_ccr_hash, new_ccr.hash());
1554	}
1555}
1556
1557// Approval Slashes primitives
1558/// Supercedes the old 'SlashingOffenceKind' enum.
1559#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
1560pub enum DisputeOffenceKind {
1561	/// A severe offence when a validator backed an invalid block
1562	/// (backing only)
1563	#[codec(index = 0)]
1564	ForInvalidBacked,
1565	/// A minor offence when a validator disputed a valid block.
1566	/// (approval checking and dispute vote only)
1567	#[codec(index = 1)]
1568	AgainstValid,
1569	/// A medium offence when a validator approved an invalid block
1570	/// (approval checking and dispute vote only)
1571	#[codec(index = 2)]
1572	ForInvalidApproved,
1573}
1574
1575/// impl for a conversion from SlashingOffenceKind to DisputeOffenceKind
1576/// This creates DisputeOffenceKind that never contains ForInvalidApproved since it was not
1577/// supported in the past
1578impl From<super::v8::slashing::SlashingOffenceKind> for DisputeOffenceKind {
1579	fn from(value: super::v8::slashing::SlashingOffenceKind) -> Self {
1580		match value {
1581			super::v8::slashing::SlashingOffenceKind::ForInvalid => Self::ForInvalidBacked,
1582			super::v8::slashing::SlashingOffenceKind::AgainstValid => Self::AgainstValid,
1583		}
1584	}
1585}
1586
1587/// impl for a tryFrom conversion from DisputeOffenceKind to SlashingOffenceKind
1588impl TryFrom<DisputeOffenceKind> for super::v8::slashing::SlashingOffenceKind {
1589	type Error = ();
1590
1591	fn try_from(value: DisputeOffenceKind) -> Result<Self, Self::Error> {
1592		match value {
1593			DisputeOffenceKind::ForInvalidBacked => Ok(Self::ForInvalid),
1594			DisputeOffenceKind::AgainstValid => Ok(Self::AgainstValid),
1595			DisputeOffenceKind::ForInvalidApproved => Err(()),
1596		}
1597	}
1598}
1599
1600/// Slashes that are waiting to be applied once we have validator key
1601/// identification.
1602#[derive(Encode, Decode, TypeInfo, Debug, Clone)]
1603pub struct PendingSlashes {
1604	/// Indices and keys of the validators who lost a dispute and are pending
1605	/// slashes.
1606	pub keys: BTreeMap<ValidatorIndex, ValidatorId>,
1607	/// The dispute outcome.
1608	pub kind: DisputeOffenceKind,
1609}
1610
1611impl From<super::v8::slashing::PendingSlashes> for PendingSlashes {
1612	fn from(old: super::v8::slashing::PendingSlashes) -> Self {
1613		let keys = old.keys;
1614		let kind = old.kind.into();
1615		Self { keys, kind }
1616	}
1617}
1618
1619impl TryFrom<PendingSlashes> for super::v8::slashing::PendingSlashes {
1620	type Error = ();
1621
1622	fn try_from(value: PendingSlashes) -> Result<Self, Self::Error> {
1623		Ok(Self { keys: value.keys, kind: value.kind.try_into()? })
1624	}
1625}
1626
1627/// We store most of the information about a lost dispute on chain. This struct
1628/// is required to identify and verify it.
1629#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
1630pub struct DisputeProof {
1631	/// Time slot when the dispute occurred.
1632	pub time_slot: DisputesTimeSlot,
1633	/// The dispute outcome.
1634	pub kind: DisputeOffenceKind,
1635	/// The index of the validator who lost a dispute.
1636	pub validator_index: ValidatorIndex,
1637	/// The parachain session key of the validator.
1638	pub validator_id: ValidatorId,
1639}
1640
1641impl From<super::v8::slashing::DisputeProof> for DisputeProof {
1642	fn from(old: super::v8::slashing::DisputeProof) -> Self {
1643		let time_slot = old.time_slot;
1644		let kind = old.kind.into(); // infallible conversion
1645		let validator_index = old.validator_index;
1646		let validator_id = old.validator_id;
1647		Self { time_slot, kind, validator_index, validator_id }
1648	}
1649}
1650
1651impl TryFrom<DisputeProof> for super::v8::slashing::DisputeProof {
1652	type Error = ();
1653
1654	fn try_from(value: DisputeProof) -> Result<Self, Self::Error> {
1655		Ok(Self {
1656			time_slot: value.time_slot,
1657			kind: value.kind.try_into()?,
1658			validator_index: value.validator_index,
1659			validator_id: value.validator_id,
1660		})
1661	}
1662}