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::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments,
23	CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash,
24	HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore,
25	UncheckedSignedAvailabilityBitfields, ValidationCodeHash,
26};
27use bitvec::prelude::*;
28use sp_application_crypto::ByteArray;
29
30use alloc::{vec, vec::Vec};
31use codec::{Decode, Encode};
32use scale_info::TypeInfo;
33use sp_core::RuntimeDebug;
34use sp_runtime::traits::Header as HeaderT;
35use sp_staking::SessionIndex;
36/// Async backing primitives
37pub mod async_backing;
38
39/// A type representing the version of the candidate descriptor and internal version number.
40#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)]
41#[cfg_attr(feature = "std", derive(Hash))]
42pub struct InternalVersion(pub u8);
43
44/// A type representing the version of the candidate descriptor.
45#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)]
46#[cfg_attr(feature = "std", derive(Hash))]
47pub enum CandidateDescriptorVersion {
48	/// The old candidate descriptor version.
49	V1,
50	/// The new `CandidateDescriptorV2`.
51	V2,
52	/// An unknown version.
53	Unknown,
54}
55
56/// A unique descriptor of the candidate receipt.
57#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
58#[cfg_attr(feature = "std", derive(Hash))]
59pub struct CandidateDescriptorV2<H = Hash> {
60	/// The ID of the para this is a candidate for.
61	para_id: ParaId,
62	/// The hash of the relay-chain block this is executed in the context of.
63	relay_parent: H,
64	/// Version field. The raw value here is not exposed, instead it is used
65	/// to determine the `CandidateDescriptorVersion`, see `fn version()`.
66	/// For the current version this field is set to `0` and will be incremented
67	/// by next versions.
68	version: InternalVersion,
69	/// The core index where the candidate is backed.
70	core_index: u16,
71	/// The session index of the candidate relay parent.
72	session_index: SessionIndex,
73	/// Reserved bytes.
74	reserved1: [u8; 25],
75	/// The blake2-256 hash of the persisted validation data. This is extra data derived from
76	/// relay-chain state which may vary based on bitfields included before the candidate.
77	/// Thus it cannot be derived entirely from the relay-parent.
78	persisted_validation_data_hash: Hash,
79	/// The blake2-256 hash of the PoV.
80	pov_hash: Hash,
81	/// The root of a block's erasure encoding Merkle tree.
82	erasure_root: Hash,
83	/// Reserved bytes.
84	reserved2: [u8; 64],
85	/// Hash of the para header that is being generated by this candidate.
86	para_head: Hash,
87	/// The blake2-256 hash of the validation code bytes.
88	validation_code_hash: ValidationCodeHash,
89}
90
91impl<H: Copy> From<CandidateDescriptorV2<H>> for CandidateDescriptor<H> {
92	fn from(value: CandidateDescriptorV2<H>) -> Self {
93		Self {
94			para_id: value.para_id,
95			relay_parent: value.relay_parent,
96			collator: value.rebuild_collator_field(),
97			persisted_validation_data_hash: value.persisted_validation_data_hash,
98			pov_hash: value.pov_hash,
99			erasure_root: value.erasure_root,
100			signature: value.rebuild_signature_field(),
101			para_head: value.para_head,
102			validation_code_hash: value.validation_code_hash,
103		}
104	}
105}
106
107#[cfg(any(feature = "runtime-benchmarks", feature = "test"))]
108impl<H: Encode + Decode + Copy> From<CandidateDescriptor<H>> for CandidateDescriptorV2<H> {
109	fn from(value: CandidateDescriptor<H>) -> Self {
110		Decode::decode(&mut value.encode().as_slice()).unwrap()
111	}
112}
113
114impl<H> CandidateDescriptorV2<H> {
115	/// Constructor
116	pub fn new(
117		para_id: Id,
118		relay_parent: H,
119		core_index: CoreIndex,
120		session_index: SessionIndex,
121		persisted_validation_data_hash: Hash,
122		pov_hash: Hash,
123		erasure_root: Hash,
124		para_head: Hash,
125		validation_code_hash: ValidationCodeHash,
126	) -> Self {
127		Self {
128			para_id,
129			relay_parent,
130			version: InternalVersion(0),
131			core_index: core_index.0 as u16,
132			session_index,
133			reserved1: [0; 25],
134			persisted_validation_data_hash,
135			pov_hash,
136			erasure_root,
137			reserved2: [0; 64],
138			para_head,
139			validation_code_hash,
140		}
141	}
142
143	/// Set the PoV size in the descriptor. Only for tests.
144	#[cfg(feature = "test")]
145	pub fn set_pov_hash(&mut self, pov_hash: Hash) {
146		self.pov_hash = pov_hash;
147	}
148
149	/// Set the version in the descriptor. Only for tests.
150	#[cfg(feature = "test")]
151	pub fn set_version(&mut self, version: InternalVersion) {
152		self.version = version;
153	}
154}
155
156/// A candidate-receipt at version 2.
157#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
158#[cfg_attr(feature = "std", derive(Hash))]
159pub struct CandidateReceiptV2<H = Hash> {
160	/// The descriptor of the candidate.
161	pub descriptor: CandidateDescriptorV2<H>,
162	/// The hash of the encoded commitments made as a result of candidate execution.
163	pub commitments_hash: Hash,
164}
165
166/// A candidate-receipt with commitments directly included.
167#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
168#[cfg_attr(feature = "std", derive(Hash))]
169pub struct CommittedCandidateReceiptV2<H = Hash> {
170	/// The descriptor of the candidate.
171	pub descriptor: CandidateDescriptorV2<H>,
172	/// The commitments of the candidate receipt.
173	pub commitments: CandidateCommitments,
174}
175
176/// An event concerning a candidate.
177#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
178#[cfg_attr(feature = "std", derive(PartialEq))]
179pub enum CandidateEvent<H = Hash> {
180	/// This candidate receipt was backed in the most recent block.
181	/// This includes the core index the candidate is now occupying.
182	#[codec(index = 0)]
183	CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
184	/// This candidate receipt was included and became a parablock at the most recent block.
185	/// This includes the core index the candidate was occupying as well as the group responsible
186	/// for backing the candidate.
187	#[codec(index = 1)]
188	CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
189	/// This candidate receipt was not made available in time and timed out.
190	/// This includes the core index the candidate was occupying.
191	#[codec(index = 2)]
192	CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
193}
194
195impl<H: Encode + Copy> From<CandidateEvent<H>> for super::v8::CandidateEvent<H> {
196	fn from(value: CandidateEvent<H>) -> Self {
197		match value {
198			CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) =>
199				super::v8::CandidateEvent::CandidateBacked(
200					receipt.into(),
201					head_data,
202					core_index,
203					group_index,
204				),
205			CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) =>
206				super::v8::CandidateEvent::CandidateIncluded(
207					receipt.into(),
208					head_data,
209					core_index,
210					group_index,
211				),
212			CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) =>
213				super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index),
214		}
215	}
216}
217
218impl<H> CandidateReceiptV2<H> {
219	/// Get a reference to the candidate descriptor.
220	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
221		&self.descriptor
222	}
223
224	/// Computes the blake2-256 hash of the receipt.
225	pub fn hash(&self) -> CandidateHash
226	where
227		H: Encode,
228	{
229		CandidateHash(BlakeTwo256::hash_of(self))
230	}
231}
232
233impl<H: Clone> CommittedCandidateReceiptV2<H> {
234	/// Transforms this into a plain `CandidateReceipt`.
235	pub fn to_plain(&self) -> CandidateReceiptV2<H> {
236		CandidateReceiptV2 {
237			descriptor: self.descriptor.clone(),
238			commitments_hash: self.commitments.hash(),
239		}
240	}
241
242	/// Computes the hash of the committed candidate receipt.
243	///
244	/// This computes the canonical hash, not the hash of the directly encoded data.
245	/// Thus this is a shortcut for `candidate.to_plain().hash()`.
246	pub fn hash(&self) -> CandidateHash
247	where
248		H: Encode,
249	{
250		self.to_plain().hash()
251	}
252
253	/// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]?
254	pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
255	where
256		H: PartialEq,
257	{
258		receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
259	}
260}
261
262impl PartialOrd for CommittedCandidateReceiptV2 {
263	fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
264		Some(self.cmp(other))
265	}
266}
267
268impl Ord for CommittedCandidateReceiptV2 {
269	fn cmp(&self, other: &Self) -> core::cmp::Ordering {
270		self.descriptor
271			.para_id
272			.cmp(&other.descriptor.para_id)
273			.then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
274	}
275}
276
277impl<H: Copy> From<CommittedCandidateReceiptV2<H>> for super::v8::CommittedCandidateReceipt<H> {
278	fn from(value: CommittedCandidateReceiptV2<H>) -> Self {
279		Self { descriptor: value.descriptor.into(), commitments: value.commitments }
280	}
281}
282
283impl<H: Copy> From<CandidateReceiptV2<H>> for super::v8::CandidateReceipt<H> {
284	fn from(value: CandidateReceiptV2<H>) -> Self {
285		Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash }
286	}
287}
288
289/// A strictly increasing sequence number, typically this would be the least significant byte of the
290/// block number.
291#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
292pub struct CoreSelector(pub u8);
293
294/// An offset in the relay chain claim queue.
295#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
296pub struct ClaimQueueOffset(pub u8);
297
298/// Signals that a parachain can send to the relay chain via the UMP queue.
299#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
300pub enum UMPSignal {
301	/// A message sent by a parachain to select the core the candidate is commited to.
302	/// Relay chain validators, in particular backers, use the `CoreSelector` and
303	/// `ClaimQueueOffset` to compute the index of the core the candidate has commited to.
304	SelectCore(CoreSelector, ClaimQueueOffset),
305}
306/// Separator between `XCM` and `UMPSignal`.
307pub const UMP_SEPARATOR: Vec<u8> = vec![];
308
309impl CandidateCommitments {
310	/// Returns the core selector and claim queue offset the candidate has committed to, if any.
311	pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> {
312		// We need at least 2 messages for the separator and core selector
313		if self.upward_messages.len() < 2 {
314			return None
315		}
316
317		let separator_pos =
318			self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?;
319
320		// Use first commitment
321		let message = self.upward_messages.get(separator_pos + 1)?;
322
323		match UMPSignal::decode(&mut message.as_slice()).ok()? {
324			UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)),
325		}
326	}
327}
328
329/// CandidateReceipt construction errors.
330#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
331pub enum CandidateReceiptError {
332	/// The specified core index is invalid.
333	InvalidCoreIndex,
334	/// The core index in commitments doesn't match the one in descriptor
335	CoreIndexMismatch,
336	/// The core selector or claim queue offset is invalid.
337	InvalidSelectedCore,
338	/// The parachain is not assigned to any core at specified claim queue offset.
339	NoAssignment,
340	/// No core was selected.
341	NoCoreSelected,
342	/// Unknown version.
343	UnknownVersion(InternalVersion),
344}
345
346macro_rules! impl_getter {
347	($field:ident, $type:ident) => {
348		/// Returns the value of $field field.
349		pub fn $field(&self) -> $type {
350			self.$field
351		}
352	};
353}
354
355impl<H: Copy> CandidateDescriptorV2<H> {
356	impl_getter!(erasure_root, Hash);
357	impl_getter!(para_head, Hash);
358	impl_getter!(relay_parent, H);
359	impl_getter!(para_id, ParaId);
360	impl_getter!(persisted_validation_data_hash, Hash);
361	impl_getter!(pov_hash, Hash);
362	impl_getter!(validation_code_hash, ValidationCodeHash);
363
364	/// Returns the candidate descriptor version.
365	/// The candidate is at version 2 if the reserved fields are zeroed out
366	/// and the internal `version` field is 0.
367	pub fn version(&self) -> CandidateDescriptorVersion {
368		if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] {
369			return CandidateDescriptorVersion::V1
370		}
371
372		match self.version.0 {
373			0 => CandidateDescriptorVersion::V2,
374			_ => CandidateDescriptorVersion::Unknown,
375		}
376	}
377
378	fn rebuild_collator_field(&self) -> CollatorId {
379		let mut collator_id = Vec::with_capacity(32);
380		let core_index: [u8; 2] = self.core_index.to_ne_bytes();
381		let session_index: [u8; 4] = self.session_index.to_ne_bytes();
382
383		collator_id.push(self.version.0);
384		collator_id.extend_from_slice(core_index.as_slice());
385		collator_id.extend_from_slice(session_index.as_slice());
386		collator_id.extend_from_slice(self.reserved1.as_slice());
387
388		CollatorId::from_slice(&collator_id.as_slice())
389			.expect("Slice size is exactly 32 bytes; qed")
390	}
391
392	/// Returns the collator id if this is a v1 `CandidateDescriptor`
393	pub fn collator(&self) -> Option<CollatorId> {
394		if self.version() == CandidateDescriptorVersion::V1 {
395			Some(self.rebuild_collator_field())
396		} else {
397			None
398		}
399	}
400
401	fn rebuild_signature_field(&self) -> CollatorSignature {
402		CollatorSignature::from_slice(self.reserved2.as_slice())
403			.expect("Slice size is exactly 64 bytes; qed")
404	}
405
406	/// Returns the collator signature of `V1` candidate descriptors, `None` otherwise.
407	pub fn signature(&self) -> Option<CollatorSignature> {
408		if self.version() == CandidateDescriptorVersion::V1 {
409			return Some(self.rebuild_signature_field())
410		}
411
412		None
413	}
414
415	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
416	pub fn core_index(&self) -> Option<CoreIndex> {
417		if self.version() == CandidateDescriptorVersion::V1 {
418			return None
419		}
420
421		Some(CoreIndex(self.core_index as u32))
422	}
423
424	/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
425	pub fn session_index(&self) -> Option<SessionIndex> {
426		if self.version() == CandidateDescriptorVersion::V1 {
427			return None
428		}
429
430		Some(self.session_index)
431	}
432}
433
434impl<H: Copy> CommittedCandidateReceiptV2<H> {
435	/// Checks if descriptor core index is equal to the commited core index.
436	/// Input `assigned_cores` must contain the sorted cores assigned to the para at
437	/// the committed claim queue offset.
438	pub fn check(&self, assigned_cores: &[CoreIndex]) -> Result<(), CandidateReceiptError> {
439		// Don't check v1 descriptors.
440		if self.descriptor.version() == CandidateDescriptorVersion::V1 {
441			return Ok(())
442		}
443
444		if self.descriptor.version() == CandidateDescriptorVersion::Unknown {
445			return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version))
446		}
447
448		if assigned_cores.is_empty() {
449			return Err(CandidateReceiptError::NoAssignment)
450		}
451
452		let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
453
454		let (core_selector, _cq_offset) =
455			self.commitments.selected_core().ok_or(CandidateReceiptError::NoCoreSelected)?;
456
457		let core_index = assigned_cores
458			.get(core_selector.0 as usize % assigned_cores.len())
459			.ok_or(CandidateReceiptError::InvalidCoreIndex)?;
460
461		if *core_index != descriptor_core_index {
462			return Err(CandidateReceiptError::CoreIndexMismatch)
463		}
464
465		Ok(())
466	}
467}
468
469/// A backed (or backable, depending on context) candidate.
470#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
471pub struct BackedCandidate<H = Hash> {
472	/// The candidate referred to.
473	candidate: CommittedCandidateReceiptV2<H>,
474	/// The validity votes themselves, expressed as signatures.
475	validity_votes: Vec<ValidityAttestation>,
476	/// The indices of the validators within the group, expressed as a bitfield. May be extended
477	/// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is
478	/// enabled.
479	validator_indices: BitVec<u8, bitvec::order::Lsb0>,
480}
481
482/// Parachains inherent-data passed into the runtime by a block author
483#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)]
484pub struct InherentData<HDR: HeaderT = Header> {
485	/// Signed bitfields by validators about availability.
486	pub bitfields: UncheckedSignedAvailabilityBitfields,
487	/// Backed candidates for inclusion in the block.
488	pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
489	/// Sets of dispute votes for inclusion,
490	pub disputes: MultiDisputeStatementSet,
491	/// The parent block header. Used for checking state proofs.
492	pub parent_header: HDR,
493}
494
495impl<H> BackedCandidate<H> {
496	/// Constructor
497	pub fn new(
498		candidate: CommittedCandidateReceiptV2<H>,
499		validity_votes: Vec<ValidityAttestation>,
500		validator_indices: BitVec<u8, bitvec::order::Lsb0>,
501		core_index: Option<CoreIndex>,
502	) -> Self {
503		let mut instance = Self { candidate, validity_votes, validator_indices };
504		if let Some(core_index) = core_index {
505			instance.inject_core_index(core_index);
506		}
507		instance
508	}
509
510	/// Get a reference to the committed candidate receipt of the candidate.
511	pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
512		&self.candidate
513	}
514
515	/// Get a reference to the descriptor of the candidate.
516	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
517		&self.candidate.descriptor
518	}
519
520	/// Get a mutable reference to the descriptor of the candidate. Only for testing.
521	#[cfg(feature = "test")]
522	pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
523		&mut self.candidate.descriptor
524	}
525
526	/// Get a reference to the validity votes of the candidate.
527	pub fn validity_votes(&self) -> &[ValidityAttestation] {
528		&self.validity_votes
529	}
530
531	/// Get a mutable reference to validity votes of the para.
532	pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
533		&mut self.validity_votes
534	}
535
536	/// Compute this candidate's hash.
537	pub fn hash(&self) -> CandidateHash
538	where
539		H: Clone + Encode,
540	{
541		self.candidate.to_plain().hash()
542	}
543
544	/// Get this candidate's receipt.
545	pub fn receipt(&self) -> CandidateReceiptV2<H>
546	where
547		H: Clone,
548	{
549		self.candidate.to_plain()
550	}
551
552	/// Get a copy of the validator indices and the assumed core index, if any.
553	pub fn validator_indices_and_core_index(
554		&self,
555		core_index_enabled: bool,
556	) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
557		// This flag tells us if the block producers must enable Elastic Scaling MVP hack.
558		// It extends `BackedCandidate::validity_indices` to store a 8 bit core index.
559		if core_index_enabled {
560			let core_idx_offset = self.validator_indices.len().saturating_sub(8);
561			if core_idx_offset > 0 {
562				let (validator_indices_slice, core_idx_slice) =
563					self.validator_indices.split_at(core_idx_offset);
564				return (
565					validator_indices_slice,
566					Some(CoreIndex(core_idx_slice.load::<u8>() as u32)),
567				);
568			}
569		}
570
571		(&self.validator_indices, None)
572	}
573
574	/// Inject a core index in the validator_indices bitvec.
575	fn inject_core_index(&mut self, core_index: CoreIndex) {
576		let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
577			BitVec::from_vec(vec![core_index.0 as u8]);
578		self.validator_indices.extend(core_index_to_inject);
579	}
580
581	/// Update the validator indices and core index in the candidate.
582	pub fn set_validator_indices_and_core_index(
583		&mut self,
584		new_indices: BitVec<u8, bitvec::order::Lsb0>,
585		maybe_core_index: Option<CoreIndex>,
586	) {
587		self.validator_indices = new_indices;
588
589		if let Some(core_index) = maybe_core_index {
590			self.inject_core_index(core_index);
591		}
592	}
593}
594
595/// Scraped runtime backing votes and resolved disputes.
596#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
597#[cfg_attr(feature = "std", derive(PartialEq))]
598pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
599	/// The session in which the block was included.
600	pub session: SessionIndex,
601	/// Set of backing validators for each candidate, represented by its candidate
602	/// receipt.
603	pub backing_validators_per_candidate:
604		Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
605	/// On-chain-recorded set of disputes.
606	/// Note that the above `backing_validators` are
607	/// unrelated to the backers of the disputes candidates.
608	pub disputes: MultiDisputeStatementSet,
609}
610
611impl<H: Encode + Decode + Copy> From<ScrapedOnChainVotes<H>> for super::v8::ScrapedOnChainVotes<H> {
612	fn from(value: ScrapedOnChainVotes<H>) -> Self {
613		Self {
614			session: value.session,
615			backing_validators_per_candidate: value
616				.backing_validators_per_candidate
617				.into_iter()
618				.map(|(receipt, validators)| (receipt.into(), validators))
619				.collect::<Vec<_>>(),
620			disputes: value.disputes,
621		}
622	}
623}
624
625/// Information about a core which is currently occupied.
626#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
627#[cfg_attr(feature = "std", derive(PartialEq))]
628pub struct OccupiedCore<H = Hash, N = BlockNumber> {
629	// NOTE: this has no ParaId as it can be deduced from the candidate descriptor.
630	/// If this core is freed by availability, this is the assignment that is next up on this
631	/// core, if any. None if there is nothing queued for this core.
632	pub next_up_on_available: Option<ScheduledCore>,
633	/// The relay-chain block number this began occupying the core at.
634	pub occupied_since: N,
635	/// The relay-chain block this will time-out at, if any.
636	pub time_out_at: N,
637	/// If this core is freed by being timed-out, this is the assignment that is next up on this
638	/// core. None if there is nothing queued for this core or there is no possibility of timing
639	/// out.
640	pub next_up_on_time_out: Option<ScheduledCore>,
641	/// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
642	/// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
643	/// this will be available.
644	pub availability: BitVec<u8, bitvec::order::Lsb0>,
645	/// The group assigned to distribute availability pieces of this candidate.
646	pub group_responsible: GroupIndex,
647	/// The hash of the candidate occupying the core.
648	pub candidate_hash: CandidateHash,
649	/// The descriptor of the candidate occupying the core.
650	pub candidate_descriptor: CandidateDescriptorV2<H>,
651}
652
653/// The state of a particular availability core.
654#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)]
655#[cfg_attr(feature = "std", derive(PartialEq))]
656pub enum CoreState<H = Hash, N = BlockNumber> {
657	/// The core is currently occupied.
658	#[codec(index = 0)]
659	Occupied(OccupiedCore<H, N>),
660	/// The core is currently free, with a para scheduled and given the opportunity
661	/// to occupy.
662	///
663	/// If a particular Collator is required to author this block, that is also present in this
664	/// variant.
665	#[codec(index = 1)]
666	Scheduled(ScheduledCore),
667	/// The core is currently free and there is nothing scheduled. This can be the case for
668	/// parathread cores when there are no parathread blocks queued. Parachain cores will never be
669	/// left idle.
670	#[codec(index = 2)]
671	Free,
672}
673
674impl<H: Copy> From<OccupiedCore<H>> for super::v8::OccupiedCore<H> {
675	fn from(value: OccupiedCore<H>) -> Self {
676		Self {
677			next_up_on_available: value.next_up_on_available,
678			occupied_since: value.occupied_since,
679			time_out_at: value.time_out_at,
680			next_up_on_time_out: value.next_up_on_time_out,
681			availability: value.availability,
682			group_responsible: value.group_responsible,
683			candidate_hash: value.candidate_hash,
684			candidate_descriptor: value.candidate_descriptor.into(),
685		}
686	}
687}
688
689impl<H: Copy> From<CoreState<H>> for super::v8::CoreState<H> {
690	fn from(value: CoreState<H>) -> Self {
691		match value {
692			CoreState::Free => super::v8::CoreState::Free,
693			CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core),
694			CoreState::Occupied(occupied_core) =>
695				super::v8::CoreState::Occupied(occupied_core.into()),
696		}
697	}
698}
699
700#[cfg(test)]
701mod tests {
702	use super::*;
703	use crate::{
704		v8::{
705			tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt,
706			CommittedCandidateReceipt, Hash, HeadData, ValidationCode,
707		},
708		vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2},
709	};
710
711	fn dummy_collator_signature() -> CollatorSignature {
712		CollatorSignature::from_slice(&mut (0..64).into_iter().collect::<Vec<_>>().as_slice())
713			.expect("64 bytes; qed")
714	}
715
716	fn dummy_collator_id() -> CollatorId {
717		CollatorId::from_slice(&mut (0..32).into_iter().collect::<Vec<_>>().as_slice())
718			.expect("32 bytes; qed")
719	}
720
721	pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 {
722		let zeros = Hash::zero();
723		let reserved2 = [0; 64];
724
725		CommittedCandidateReceiptV2 {
726			descriptor: CandidateDescriptorV2 {
727				para_id: 0.into(),
728				relay_parent: zeros,
729				version: InternalVersion(0),
730				core_index: 123,
731				session_index: 1,
732				reserved1: Default::default(),
733				persisted_validation_data_hash: zeros,
734				pov_hash: zeros,
735				erasure_root: zeros,
736				reserved2,
737				para_head: zeros,
738				validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(),
739			},
740			commitments: CandidateCommitments {
741				head_data: HeadData(vec![]),
742				upward_messages: vec![].try_into().expect("empty vec fits within bounds"),
743				new_validation_code: None,
744				horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"),
745				processed_downward_messages: 0,
746				hrmp_watermark: 0_u32,
747			},
748		}
749	}
750
751	#[test]
752	fn is_binary_compatibile() {
753		let old_ccr = dummy_old_committed_candidate_receipt();
754		let new_ccr = dummy_committed_candidate_receipt_v2();
755
756		assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size());
757
758		let encoded_old = old_ccr.encode();
759
760		// Deserialize from old candidate receipt.
761		let new_ccr: CommittedCandidateReceiptV2 =
762			Decode::decode(&mut encoded_old.as_slice()).unwrap();
763
764		// We get same candidate hash.
765		assert_eq!(old_ccr.hash(), new_ccr.hash());
766	}
767
768	#[test]
769	fn invalid_version_descriptor() {
770		let mut new_ccr = dummy_committed_candidate_receipt_v2();
771		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2);
772		// Put some unknown version.
773		new_ccr.descriptor.version = InternalVersion(100);
774
775		// Deserialize as V1.
776		let new_ccr: CommittedCandidateReceiptV2 =
777			Decode::decode(&mut new_ccr.encode().as_slice()).unwrap();
778
779		assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown);
780		assert_eq!(
781			new_ccr.check(&vec![].as_slice()),
782			Err(CandidateReceiptError::UnknownVersion(InternalVersion(100)))
783		)
784	}
785
786	#[test]
787	fn test_ump_commitment() {
788		let mut new_ccr = dummy_committed_candidate_receipt_v2();
789		new_ccr.descriptor.core_index = 123;
790		new_ccr.descriptor.para_id = ParaId::new(1000);
791
792		// dummy XCM messages
793		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
794		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
795
796		// separator
797		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
798
799		// CoreIndex commitment
800		new_ccr
801			.commitments
802			.upward_messages
803			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
804
805		assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(()));
806	}
807
808	#[test]
809	fn test_invalid_ump_commitment() {
810		let mut new_ccr = dummy_committed_candidate_receipt_v2();
811		new_ccr.descriptor.core_index = 0;
812		new_ccr.descriptor.para_id = ParaId::new(1000);
813
814		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
815		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
816
817		// The check should fail because no `SelectCore` signal was sent.
818		assert_eq!(
819			new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]),
820			Err(CandidateReceiptError::NoCoreSelected)
821		);
822
823		// Garbage message.
824		new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode());
825
826		// No `SelectCore` can be decoded.
827		assert_eq!(new_ccr.commitments.selected_core(), None);
828
829		// Failure is expected.
830		assert_eq!(
831			new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]),
832			Err(CandidateReceiptError::NoCoreSelected)
833		);
834
835		new_ccr.commitments.upward_messages.clear();
836		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
837
838		new_ccr
839			.commitments
840			.upward_messages
841			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
842
843		// Duplicate
844		new_ccr
845			.commitments
846			.upward_messages
847			.force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
848
849		// Duplicate doesn't override first signal.
850		assert_eq!(new_ccr.check(&vec![CoreIndex(0), CoreIndex(100)]), Ok(()));
851	}
852
853	#[test]
854	fn test_version2_receipts_decoded_as_v1() {
855		let mut new_ccr = dummy_committed_candidate_receipt_v2();
856		new_ccr.descriptor.core_index = 123;
857		new_ccr.descriptor.para_id = ParaId::new(1000);
858
859		// dummy XCM messages
860		new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
861		new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
862
863		// separator
864		new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
865
866		// CoreIndex commitment
867		new_ccr
868			.commitments
869			.upward_messages
870			.force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
871
872		let encoded_ccr = new_ccr.encode();
873		let decoded_ccr: CommittedCandidateReceipt =
874			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
875
876		assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent());
877		assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id());
878
879		assert_eq!(new_ccr.hash(), decoded_ccr.hash());
880
881		// Encode v1 and decode as V2
882		let encoded_ccr = new_ccr.encode();
883		let v2_ccr: CommittedCandidateReceiptV2 =
884			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
885
886		assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123)));
887		assert_eq!(new_ccr.check(&vec![CoreIndex(123)]), Ok(()));
888
889		assert_eq!(new_ccr.hash(), v2_ccr.hash());
890	}
891
892	#[test]
893	fn test_core_select_is_mandatory() {
894		// Testing edge case when collators provide zeroed signature and collator id.
895		let mut old_ccr = dummy_old_committed_candidate_receipt();
896		old_ccr.descriptor.para_id = ParaId::new(1000);
897		let encoded_ccr: Vec<u8> = old_ccr.encode();
898
899		let new_ccr: CommittedCandidateReceiptV2 =
900			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
901
902		// Since collator sig and id are zeroed, it means that the descriptor uses format
903		// version 2.
904		// We expect the check to fail in such case because there will be no `SelectCore`
905		// commitment.
906		assert_eq!(new_ccr.check(&vec![CoreIndex(0)]), Err(CandidateReceiptError::NoCoreSelected));
907
908		// Adding collator signature should make it decode as v1.
909		old_ccr.descriptor.signature = dummy_collator_signature();
910		old_ccr.descriptor.collator = dummy_collator_id();
911
912		let old_ccr_hash = old_ccr.hash();
913
914		let encoded_ccr: Vec<u8> = old_ccr.encode();
915
916		let new_ccr: CommittedCandidateReceiptV2 =
917			Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
918
919		assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature));
920		assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator));
921
922		assert_eq!(new_ccr.descriptor.core_index(), None);
923		assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000));
924
925		assert_eq!(old_ccr_hash, new_ccr.hash());
926	}
927}