referrerpolicy=no-referrer-when-downgrade

polkadot_primitives/v9/
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//! `V9` Primitives.
18
19use alloc::{
20	collections::{BTreeMap, BTreeSet, VecDeque},
21	vec,
22	vec::{IntoIter, Vec},
23};
24
25use bitvec::{field::BitField, prelude::*, slice::BitSlice};
26
27use codec::{Decode, DecodeWithMemTracking, Encode};
28use scale_info::TypeInfo;
29
30use core::{
31	marker::PhantomData,
32	slice::{Iter, IterMut},
33};
34
35#[cfg(feature = "test")]
36use sp_application_crypto::ByteArray;
37use sp_application_crypto::KeyTypeId;
38use sp_arithmetic::{
39	traits::{BaseArithmetic, Saturating},
40	Perbill,
41};
42
43use bounded_collections::BoundedVec;
44use serde::{Deserialize, Serialize};
45use sp_core::ConstU32;
46use sp_inherents::InherentIdentifier;
47
48// ==========
49// PUBLIC RE-EXPORTS
50// ==========
51
52pub use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
53pub use sp_consensus_slots::Slot;
54pub use sp_runtime::traits::{AppVerify, BlakeTwo256, Hash as HashT, Header as HeaderT};
55pub use sp_staking::SessionIndex;
56
57// Export some core primitives.
58pub use polkadot_core_primitives::v2::{
59	AccountId, AccountIndex, AccountPublic, Balance, Block, BlockId, BlockNumber, CandidateHash,
60	ChainId, DownwardMessage, Hash, Header, InboundDownwardMessage, InboundHrmpMessage, Moment,
61	Nonce, OutboundHrmpMessage, Remark, Signature, UncheckedExtrinsic,
62};
63
64// Export some polkadot-parachain primitives
65pub use polkadot_parachain_primitives::primitives::{
66	HeadData, HorizontalMessages, HrmpChannelId, Id, Id as ParaId, UpwardMessage, UpwardMessages,
67	ValidationCode, ValidationCodeHash, LOWEST_PUBLIC_ID,
68};
69
70/// Signed data.
71mod signed;
72pub use signed::{EncodeAs, Signed, UncheckedSigned};
73
74pub mod async_backing;
75pub mod executor_params;
76pub mod slashing;
77
78pub use async_backing::AsyncBackingParams;
79pub use executor_params::{
80	ExecutorHostFunction, ExecutorParam, ExecutorParamError, ExecutorParams, ExecutorParamsHash,
81	ExecutorParamsPrepHash,
82};
83
84mod metrics;
85pub use metrics::{
86	metric_definitions, RuntimeMetricLabel, RuntimeMetricLabelValue, RuntimeMetricLabelValues,
87	RuntimeMetricLabels, RuntimeMetricOp, RuntimeMetricUpdate,
88};
89
90/// The key type ID for a collator key.
91pub const COLLATOR_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"coll");
92const LOG_TARGET: &str = "runtime::primitives";
93
94mod collator_app {
95	use sp_application_crypto::{app_crypto, sr25519};
96	app_crypto!(sr25519, super::COLLATOR_KEY_TYPE_ID);
97}
98
99/// Identity that collators use.
100pub type CollatorId = collator_app::Public;
101
102/// A Parachain collator keypair.
103#[cfg(feature = "std")]
104pub type CollatorPair = collator_app::Pair;
105
106/// Signature on candidate's block data by a collator.
107pub type CollatorSignature = collator_app::Signature;
108
109/// The key type ID for a parachain validator key.
110pub const PARACHAIN_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"para");
111
112mod validator_app {
113	use sp_application_crypto::{app_crypto, sr25519};
114	app_crypto!(sr25519, super::PARACHAIN_KEY_TYPE_ID);
115}
116
117/// Identity that parachain validators use when signing validation messages.
118///
119/// For now we assert that parachain validator set is exactly equivalent to the authority set, and
120/// so we define it to be the same type as `SessionKey`. In the future it may have different crypto.
121pub type ValidatorId = validator_app::Public;
122
123/// Trait required for type specific indices e.g. `ValidatorIndex` and `GroupIndex`
124pub trait TypeIndex {
125	/// Returns the index associated to this value.
126	fn type_index(&self) -> usize;
127}
128
129/// Index of the validator is used as a lightweight replacement of the `ValidatorId` when
130/// appropriate.
131#[derive(
132	Eq,
133	Ord,
134	PartialEq,
135	PartialOrd,
136	Copy,
137	Clone,
138	Encode,
139	Decode,
140	DecodeWithMemTracking,
141	TypeInfo,
142	Debug,
143)]
144#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash))]
145pub struct ValidatorIndex(pub u32);
146
147/// Index of an availability chunk.
148///
149/// The underlying type is identical to `ValidatorIndex`, because
150/// the number of chunks will always be equal to the number of validators.
151/// However, the chunk index held by a validator may not always be equal to its `ValidatorIndex`, so
152/// we use a separate type to make code easier to read.
153#[derive(Eq, Ord, PartialEq, PartialOrd, Copy, Clone, Encode, Decode, TypeInfo, Debug)]
154#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash))]
155pub struct ChunkIndex(pub u32);
156
157impl From<ChunkIndex> for ValidatorIndex {
158	fn from(c_index: ChunkIndex) -> Self {
159		ValidatorIndex(c_index.0)
160	}
161}
162
163impl From<ValidatorIndex> for ChunkIndex {
164	fn from(v_index: ValidatorIndex) -> Self {
165		ChunkIndex(v_index.0)
166	}
167}
168
169impl From<u32> for ChunkIndex {
170	fn from(n: u32) -> Self {
171		ChunkIndex(n)
172	}
173}
174
175// We should really get https://github.com/paritytech/polkadot/issues/2403 going ..
176impl From<u32> for ValidatorIndex {
177	fn from(n: u32) -> Self {
178		ValidatorIndex(n)
179	}
180}
181
182impl TypeIndex for ValidatorIndex {
183	fn type_index(&self) -> usize {
184		self.0 as usize
185	}
186}
187
188sp_application_crypto::with_pair! {
189	/// A Parachain validator keypair.
190	pub type ValidatorPair = validator_app::Pair;
191}
192
193/// Signature with which parachain validators sign blocks.
194///
195/// For now we assert that parachain validator set is exactly equivalent to the authority set, and
196/// so we define it to be the same type as `SessionKey`. In the future it may have different crypto.
197pub type ValidatorSignature = validator_app::Signature;
198
199/// A declarations of storage keys where an external observer can find some interesting data.
200pub mod well_known_keys {
201	use super::{HrmpChannelId, Id, WellKnownKey};
202	use alloc::vec::Vec;
203	use codec::Encode as _;
204	use hex_literal::hex;
205	use sp_io::hashing::twox_64;
206
207	// A note on generating these magic values below:
208	//
209	// The `StorageValue`, such as `ACTIVE_CONFIG` was obtained by calling:
210	//
211	//     ActiveConfig::<T>::hashed_key()
212	//
213	// The `StorageMap` values require `prefix`, and for example for `hrmp_egress_channel_index`,
214	// it could be obtained like:
215	//
216	//     HrmpEgressChannelsIndex::<T>::prefix_hash();
217	//
218
219	/// The current epoch index.
220	///
221	/// The storage item should be access as a `u64` encoded value.
222	pub const EPOCH_INDEX: &[u8] =
223		&hex!["1cb6f36e027abb2091cfb5110ab5087f38316cbf8fa0da822a20ac1c55bf1be3"];
224
225	/// The current relay chain block randomness
226	///
227	/// The storage item should be accessed as a `schnorrkel::Randomness` encoded value.
228	pub const CURRENT_BLOCK_RANDOMNESS: &[u8] =
229		&hex!["1cb6f36e027abb2091cfb5110ab5087fd077dfdb8adb10f78f10a5df8742c545"];
230
231	/// The randomness for one epoch ago
232	///
233	/// The storage item should be accessed as a `schnorrkel::Randomness` encoded value.
234	pub const ONE_EPOCH_AGO_RANDOMNESS: &[u8] =
235		&hex!["1cb6f36e027abb2091cfb5110ab5087f7ce678799d3eff024253b90e84927cc6"];
236
237	/// The randomness for two epochs ago
238	///
239	/// The storage item should be accessed as a `schnorrkel::Randomness` encoded value.
240	pub const TWO_EPOCHS_AGO_RANDOMNESS: &[u8] =
241		&hex!["1cb6f36e027abb2091cfb5110ab5087f7a414cb008e0e61e46722aa60abdd672"];
242
243	/// The current slot number.
244	///
245	/// The storage entry should be accessed as a `Slot` encoded value.
246	pub const CURRENT_SLOT: &[u8] =
247		&hex!["1cb6f36e027abb2091cfb5110ab5087f06155b3cd9a8c9e5e9a23fd5dc13a5ed"];
248
249	/// The currently active host configuration.
250	///
251	/// The storage entry should be accessed as an `AbridgedHostConfiguration` encoded value.
252	pub const ACTIVE_CONFIG: &[u8] =
253		&hex!["06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385"];
254
255	/// The authorities for the current epoch.
256	///
257	/// The storage entry should be accessed as an `Vec<(AuthorityId, BabeAuthorityWeight)>` encoded
258	/// value.
259	pub const AUTHORITIES: &[u8] =
260		&hex!["1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d"];
261
262	/// The authorities for the next epoch.
263	///
264	/// The storage entry should be accessed as an `Vec<(AuthorityId, BabeAuthorityWeight)>` encoded
265	/// value.
266	pub const NEXT_AUTHORITIES: &[u8] =
267		&hex!["1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c"];
268
269	/// Hash of the committed head data for a given registered para.
270	///
271	/// The storage entry stores wrapped `HeadData(Vec<u8>)`.
272	pub fn para_head(para_id: Id) -> Vec<u8> {
273		let prefix = hex!["cd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c3"];
274
275		para_id.using_encoded(|para_id: &[u8]| {
276			prefix
277				.as_ref()
278				.iter()
279				.chain(twox_64(para_id).iter())
280				.chain(para_id.iter())
281				.cloned()
282				.collect()
283		})
284	}
285
286	/// The upward message dispatch queue for the given para id.
287	///
288	/// The storage entry stores a tuple of two values:
289	///
290	/// - `count: u32`, the number of messages currently in the queue for given para,
291	/// - `total_size: u32`, the total size of all messages in the queue.
292	#[deprecated = "Use `relay_dispatch_queue_remaining_capacity` instead"]
293	pub fn relay_dispatch_queue_size(para_id: Id) -> Vec<u8> {
294		let prefix = hex!["f5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e"];
295
296		para_id.using_encoded(|para_id: &[u8]| {
297			prefix
298				.as_ref()
299				.iter()
300				.chain(twox_64(para_id).iter())
301				.chain(para_id.iter())
302				.cloned()
303				.collect()
304		})
305	}
306
307	/// Type safe version of `relay_dispatch_queue_size`.
308	#[deprecated = "Use `relay_dispatch_queue_remaining_capacity` instead"]
309	pub fn relay_dispatch_queue_size_typed(para: Id) -> WellKnownKey<(u32, u32)> {
310		#[allow(deprecated)]
311		relay_dispatch_queue_size(para).into()
312	}
313
314	/// The upward message dispatch queue remaining capacity for the given para id.
315	///
316	/// The storage entry stores a tuple of two values:
317	///
318	/// - `count: u32`, the number of additional messages which may be enqueued for the given para,
319	/// - `total_size: u32`, the total size of additional messages which may be enqueued for the
320	/// given para.
321	pub fn relay_dispatch_queue_remaining_capacity(para_id: Id) -> WellKnownKey<(u32, u32)> {
322		(b":relay_dispatch_queue_remaining_capacity", para_id).encode().into()
323	}
324
325	/// The HRMP channel for the given identifier.
326	///
327	/// The storage entry should be accessed as an `AbridgedHrmpChannel` encoded value.
328	pub fn hrmp_channels(channel: HrmpChannelId) -> Vec<u8> {
329		let prefix = hex!["6a0da05ca59913bc38a8630590f2627cb6604cff828a6e3f579ca6c59ace013d"];
330
331		channel.using_encoded(|channel: &[u8]| {
332			prefix
333				.as_ref()
334				.iter()
335				.chain(twox_64(channel).iter())
336				.chain(channel.iter())
337				.cloned()
338				.collect()
339		})
340	}
341
342	/// The list of inbound channels for the given para.
343	///
344	/// The storage entry stores a `Vec<ParaId>`
345	pub fn hrmp_ingress_channel_index(para_id: Id) -> Vec<u8> {
346		let prefix = hex!["6a0da05ca59913bc38a8630590f2627c1d3719f5b0b12c7105c073c507445948"];
347
348		para_id.using_encoded(|para_id: &[u8]| {
349			prefix
350				.as_ref()
351				.iter()
352				.chain(twox_64(para_id).iter())
353				.chain(para_id.iter())
354				.cloned()
355				.collect()
356		})
357	}
358
359	/// The list of outbound channels for the given para.
360	///
361	/// The storage entry stores a `Vec<ParaId>`
362	pub fn hrmp_egress_channel_index(para_id: Id) -> Vec<u8> {
363		let prefix = hex!["6a0da05ca59913bc38a8630590f2627cf12b746dcf32e843354583c9702cc020"];
364
365		para_id.using_encoded(|para_id: &[u8]| {
366			prefix
367				.as_ref()
368				.iter()
369				.chain(twox_64(para_id).iter())
370				.chain(para_id.iter())
371				.cloned()
372				.collect()
373		})
374	}
375
376	/// The MQC head for the downward message queue of the given para. See more in the `Dmp` module.
377	///
378	/// The storage entry stores a `Hash`. This is polkadot hash which is at the moment
379	/// `blake2b-256`.
380	pub fn dmq_mqc_head(para_id: Id) -> Vec<u8> {
381		let prefix = hex!["63f78c98723ddc9073523ef3beefda0c4d7fefc408aac59dbfe80a72ac8e3ce5"];
382
383		para_id.using_encoded(|para_id: &[u8]| {
384			prefix
385				.as_ref()
386				.iter()
387				.chain(twox_64(para_id).iter())
388				.chain(para_id.iter())
389				.cloned()
390				.collect()
391		})
392	}
393
394	/// The signal that indicates whether the parachain should go-ahead with the proposed validation
395	/// code upgrade.
396	///
397	/// The storage entry stores a value of `UpgradeGoAhead` type.
398	pub fn upgrade_go_ahead_signal(para_id: Id) -> Vec<u8> {
399		let prefix = hex!["cd710b30bd2eab0352ddcc26417aa1949e94c040f5e73d9b7addd6cb603d15d3"];
400
401		para_id.using_encoded(|para_id: &[u8]| {
402			prefix
403				.as_ref()
404				.iter()
405				.chain(twox_64(para_id).iter())
406				.chain(para_id.iter())
407				.cloned()
408				.collect()
409		})
410	}
411
412	/// The signal that indicates whether the parachain is disallowed to signal an upgrade at this
413	/// relay-parent.
414	///
415	/// The storage entry stores a value of `UpgradeRestriction` type.
416	pub fn upgrade_restriction_signal(para_id: Id) -> Vec<u8> {
417		let prefix = hex!["cd710b30bd2eab0352ddcc26417aa194f27bbb460270642b5bcaf032ea04d56a"];
418
419		para_id.using_encoded(|para_id: &[u8]| {
420			prefix
421				.as_ref()
422				.iter()
423				.chain(twox_64(para_id).iter())
424				.chain(para_id.iter())
425				.cloned()
426				.collect()
427		})
428	}
429}
430
431/// Relay chain slot duration in milliseconds, which is the same
432/// value across all networks (e.g. Polkadot, Kusama, Westend, Rococo).
433pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u64 = 6000;
434
435/// Unique identifier for the Parachains Inherent
436pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0";
437
438/// The key type ID for parachain assignment key.
439pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn");
440
441/// Compressed or not the wasm blob can never be less than 9 bytes.
442pub const MIN_CODE_SIZE: u32 = 9;
443
444/// Maximum compressed code size we support right now.
445/// At the moment we have runtime upgrade on chain, which restricts scalability severely. If we want
446/// to have bigger values, we should fix that first.
447///
448/// Used for:
449/// * initial genesis for the Parachains configuration
450/// * checking updates to this stored runtime configuration do not exceed this limit
451// NOTE: This value is used in the runtime so be careful when changing it.
452pub const MAX_CODE_SIZE: u32 = 5 * 1024 * 1024;
453
454/// Maximum head data size we support right now.
455///
456/// Used for:
457/// * initial genesis for the Parachains configuration
458/// * checking updates to this stored runtime configuration do not exceed this limit
459// NOTE: This value is used in the runtime so be careful when changing it.
460pub const MAX_HEAD_DATA_SIZE: u32 = 1 * 1024 * 1024;
461
462/// Maximum PoV size we support right now.
463///
464/// Used for:
465/// * initial genesis for the Parachains configuration
466/// * checking updates to this stored runtime configuration do not exceed this limit
467/// * when detecting a PoV decompression bomb in the client
468// NOTE: This value is used in the runtime so be careful when changing it.
469pub const MAX_POV_SIZE: u32 = 10 * 1024 * 1024;
470
471/// Default queue size we use for the on-demand order book.
472///
473/// Can be adjusted in configuration.
474pub const ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE: u32 = 10_000;
475
476/// Maximum for maximum queue size.
477///
478/// We use this value for benchmarking.
479pub const ON_DEMAND_MAX_QUEUE_MAX_SIZE: u32 = 10_000;
480
481/// Backing votes threshold used from the host prior to runtime API version 6 and from the runtime
482/// prior to v9 configuration migration.
483pub const LEGACY_MIN_BACKING_VOTES: u32 = 2;
484
485/// Default value for `SchedulerParams.lookahead`
486pub const DEFAULT_SCHEDULING_LOOKAHEAD: u32 = 3;
487
488// The public key of a keypair used by a validator for determining assignments
489/// to approve included parachain candidates.
490mod assignment_app {
491	use sp_application_crypto::{app_crypto, sr25519};
492	app_crypto!(sr25519, super::ASSIGNMENT_KEY_TYPE_ID);
493}
494
495/// The public key of a keypair used by a validator for determining assignments
496/// to approve included parachain candidates.
497pub type AssignmentId = assignment_app::Public;
498
499sp_application_crypto::with_pair! {
500	/// The full keypair used by a validator for determining assignments to approve included
501	/// parachain candidates.
502	pub type AssignmentPair = assignment_app::Pair;
503}
504
505/// The index of the candidate in the list of candidates fully included as-of the block.
506pub type CandidateIndex = u32;
507
508/// The validation data provides information about how to create the inputs for validation of a
509/// candidate. This information is derived from the chain state and will vary from para to para,
510/// although some fields may be the same for every para.
511///
512/// Since this data is used to form inputs to the validation function, it needs to be persisted by
513/// the availability system to avoid dependence on availability of the relay-chain state.
514///
515/// Furthermore, the validation data acts as a way to authorize the additional data the collator
516/// needs to pass to the validation function. For example, the validation function can check whether
517/// the incoming messages (e.g. downward messages) were actually sent by using the data provided in
518/// the validation data using so called MQC heads.
519///
520/// Since the commitments of the validation function are checked by the relay-chain, secondary
521/// checkers can rely on the invariant that the relay-chain only includes para-blocks for which
522/// these checks have already been done. As such, there is no need for the validation data used to
523/// inform validators and collators about the checks the relay-chain will perform to be persisted by
524/// the availability system.
525///
526/// The `PersistedValidationData` should be relatively lightweight primarily because it is
527/// constructed during inclusion for each candidate and therefore lies on the critical path of
528/// inclusion.
529#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
530#[cfg_attr(feature = "std", derive(Default))]
531pub struct PersistedValidationData<H = Hash, N = BlockNumber> {
532	/// The parent head-data.
533	pub parent_head: HeadData,
534	/// The relay-chain block number this is in the context of.
535	pub relay_parent_number: N,
536	/// The relay-chain block storage root this is in the context of.
537	pub relay_parent_storage_root: H,
538	/// The maximum legal size of a POV block, in bytes.
539	pub max_pov_size: u32,
540}
541
542impl<H: Encode, N: Encode> PersistedValidationData<H, N> {
543	/// Compute the blake2-256 hash of the persisted validation data.
544	pub fn hash(&self) -> Hash {
545		BlakeTwo256::hash_of(self)
546	}
547}
548
549/// Commitments made in a `CandidateReceipt`. Many of these are outputs of validation.
550#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
551#[cfg_attr(feature = "std", derive(Default, Hash))]
552pub struct CandidateCommitments<N = BlockNumber> {
553	/// Messages destined to be interpreted by the Relay chain itself.
554	pub upward_messages: UpwardMessages,
555	/// Horizontal messages sent by the parachain.
556	pub horizontal_messages: HorizontalMessages,
557	/// New validation code.
558	pub new_validation_code: Option<ValidationCode>,
559	/// The head-data produced as a result of execution.
560	pub head_data: HeadData,
561	/// The number of messages processed from the DMQ.
562	pub processed_downward_messages: u32,
563	/// The mark which specifies the block number up to which all inbound HRMP messages are
564	/// processed.
565	pub hrmp_watermark: N,
566}
567
568impl CandidateCommitments {
569	/// Compute the blake2-256 hash of the commitments.
570	pub fn hash(&self) -> Hash {
571		BlakeTwo256::hash_of(self)
572	}
573}
574
575/// A bitfield concerning availability of backed candidates.
576///
577/// Every bit refers to an availability core index.
578#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
579pub struct AvailabilityBitfield(pub BitVec<u8, bitvec::order::Lsb0>);
580
581impl From<BitVec<u8, bitvec::order::Lsb0>> for AvailabilityBitfield {
582	fn from(inner: BitVec<u8, bitvec::order::Lsb0>) -> Self {
583		AvailabilityBitfield(inner)
584	}
585}
586
587/// A signed compact statement, suitable to be sent to the chain.
588pub type SignedStatement = Signed<CompactStatement>;
589/// A signed compact statement, with signature not yet checked.
590pub type UncheckedSignedStatement = UncheckedSigned<CompactStatement>;
591
592/// A bitfield signed by a particular validator about the availability of pending candidates.
593pub type SignedAvailabilityBitfield = Signed<AvailabilityBitfield>;
594/// A signed bitfield with signature not yet checked.
595pub type UncheckedSignedAvailabilityBitfield = UncheckedSigned<AvailabilityBitfield>;
596
597/// A set of signed availability bitfields. Should be sorted by validator index, ascending.
598pub type SignedAvailabilityBitfields = Vec<SignedAvailabilityBitfield>;
599/// A set of unchecked signed availability bitfields. Should be sorted by validator index,
600/// ascending.
601pub type UncheckedSignedAvailabilityBitfields = Vec<UncheckedSignedAvailabilityBitfield>;
602
603/// Verify the backing of the given candidate.
604///
605/// Provide a lookup from the index of a validator within the group assigned to this para,
606/// as opposed to the index of the validator within the overall validator set, as well as
607/// the number of validators in the group.
608///
609/// Also provide the signing context.
610///
611/// Returns either an error, indicating that one of the signatures was invalid or that the index
612/// was out-of-bounds, or the number of signatures checked.
613pub fn check_candidate_backing<H: AsRef<[u8]> + Clone + Encode + core::fmt::Debug>(
614	candidate_hash: CandidateHash,
615	validity_votes: &[ValidityAttestation],
616	validator_indices: &BitSlice<u8, bitvec::order::Lsb0>,
617	signing_context: &SigningContext<H>,
618	group_len: usize,
619	validator_lookup: impl Fn(usize) -> Option<ValidatorId>,
620) -> Result<usize, ()> {
621	if validator_indices.len() != group_len {
622		log::debug!(
623			target: LOG_TARGET,
624			"Check candidate backing: indices mismatch: group_len = {} , indices_len = {}",
625			group_len,
626			validator_indices.len(),
627		);
628		return Err(());
629	}
630
631	if validity_votes.len() > group_len {
632		log::debug!(
633			target: LOG_TARGET,
634			"Check candidate backing: Too many votes, expected: {}, found: {}",
635			group_len,
636			validity_votes.len(),
637		);
638		return Err(());
639	}
640
641	let mut signed = 0;
642	for ((val_in_group_idx, _), attestation) in validator_indices
643		.iter()
644		.enumerate()
645		.filter(|(_, signed)| **signed)
646		.zip(validity_votes.iter())
647	{
648		let validator_id = validator_lookup(val_in_group_idx).ok_or(())?;
649		let payload = attestation.signed_payload(candidate_hash, signing_context);
650		let sig = attestation.signature();
651
652		if sig.verify(&payload[..], &validator_id) {
653			signed += 1;
654		} else {
655			log::debug!(
656				target: LOG_TARGET,
657				"Check candidate backing: Invalid signature. validator_id = {:?}, validator_index = {} ",
658				validator_id,
659				val_in_group_idx,
660			);
661			return Err(());
662		}
663	}
664
665	if signed != validity_votes.len() {
666		log::error!(
667			target: LOG_TARGET,
668			"Check candidate backing: Too many signatures, expected = {}, found = {}",
669			validity_votes.len(),
670			signed,
671		);
672		return Err(());
673	}
674
675	Ok(signed)
676}
677
678/// The unique (during session) index of a core.
679#[derive(
680	Encode,
681	Decode,
682	DecodeWithMemTracking,
683	Default,
684	PartialOrd,
685	Ord,
686	Eq,
687	PartialEq,
688	Clone,
689	Copy,
690	TypeInfo,
691	Debug,
692)]
693#[cfg_attr(feature = "std", derive(Hash))]
694pub struct CoreIndex(pub u32);
695
696impl From<u32> for CoreIndex {
697	fn from(i: u32) -> CoreIndex {
698		CoreIndex(i)
699	}
700}
701
702impl TypeIndex for CoreIndex {
703	fn type_index(&self) -> usize {
704		self.0 as usize
705	}
706}
707
708/// The unique (during session) index of a validator group.
709#[derive(
710	Encode,
711	Decode,
712	DecodeWithMemTracking,
713	Default,
714	Clone,
715	Copy,
716	Debug,
717	PartialEq,
718	Eq,
719	TypeInfo,
720	PartialOrd,
721	Ord,
722)]
723#[cfg_attr(feature = "std", derive(Hash))]
724pub struct GroupIndex(pub u32);
725
726impl From<u32> for GroupIndex {
727	fn from(i: u32) -> GroupIndex {
728		GroupIndex(i)
729	}
730}
731
732impl TypeIndex for GroupIndex {
733	fn type_index(&self) -> usize {
734		self.0 as usize
735	}
736}
737
738/// A claim on authoring the next block for a given parathread (on-demand parachain).
739#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
740pub struct ParathreadClaim(pub Id, pub Option<CollatorId>);
741
742/// An entry tracking a claim to ensure it does not pass the maximum number of retries.
743#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
744pub struct ParathreadEntry {
745	/// The claim.
746	pub claim: ParathreadClaim,
747	/// Number of retries
748	pub retries: u32,
749}
750
751/// A helper data-type for tracking validator-group rotations.
752#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
753#[cfg_attr(feature = "std", derive(PartialEq))]
754pub struct GroupRotationInfo<N = BlockNumber> {
755	/// The block number where the session started.
756	pub session_start_block: N,
757	/// How often groups rotate. 0 means never.
758	pub group_rotation_frequency: N,
759	/// The current block number.
760	pub now: N,
761}
762
763impl GroupRotationInfo {
764	/// Returns the index of the group needed to validate the core at the given index, assuming
765	/// the given number of cores.
766	///
767	/// `core_index` should be less than `cores`, which is capped at `u32::max()`.
768	pub fn group_for_core(&self, core_index: CoreIndex, cores: usize) -> GroupIndex {
769		if self.group_rotation_frequency == 0 {
770			return GroupIndex(core_index.0);
771		}
772		if cores == 0 {
773			return GroupIndex(0);
774		}
775
776		let cores = core::cmp::min(cores, u32::MAX as usize);
777		let blocks_since_start = self.now.saturating_sub(self.session_start_block);
778		let rotations = blocks_since_start / self.group_rotation_frequency;
779
780		// g = c + r mod cores
781
782		let idx = (core_index.0 as usize + rotations as usize) % cores;
783		GroupIndex(idx as u32)
784	}
785
786	/// Returns the index of the group assigned to the given core. This does no checking or
787	/// whether the group index is in-bounds.
788	///
789	/// `core_index` should be less than `cores`, which is capped at `u32::max()`.
790	pub fn core_for_group(&self, group_index: GroupIndex, cores: usize) -> CoreIndex {
791		if self.group_rotation_frequency == 0 {
792			return CoreIndex(group_index.0);
793		}
794		if cores == 0 {
795			return CoreIndex(0);
796		}
797
798		let cores = core::cmp::min(cores, u32::MAX as usize);
799		let blocks_since_start = self.now.saturating_sub(self.session_start_block);
800		let rotations = blocks_since_start / self.group_rotation_frequency;
801		let rotations = rotations % cores as u32;
802
803		// g = c + r mod cores
804		// c = g - r mod cores
805		// x = x + cores mod cores
806		// c = (g + cores) - r mod cores
807
808		let idx = (group_index.0 as usize + cores - rotations as usize) % cores;
809		CoreIndex(idx as u32)
810	}
811
812	/// Create a new `GroupRotationInfo` with one further rotation applied.
813	pub fn bump_rotation(&self) -> Self {
814		GroupRotationInfo {
815			session_start_block: self.session_start_block,
816			group_rotation_frequency: self.group_rotation_frequency,
817			now: self.next_rotation_at(),
818		}
819	}
820}
821
822impl<N: Saturating + BaseArithmetic + Copy> GroupRotationInfo<N> {
823	/// Returns the block number of the next rotation after the current block. If the current block
824	/// is 10 and the rotation frequency is 5, this should return 15.
825	pub fn next_rotation_at(&self) -> N {
826		let cycle_once = self.now + self.group_rotation_frequency;
827		cycle_once -
828			(cycle_once.saturating_sub(self.session_start_block) % self.group_rotation_frequency)
829	}
830
831	/// Returns the block number of the last rotation before or including the current block. If the
832	/// current block is 10 and the rotation frequency is 5, this should return 10.
833	pub fn last_rotation_at(&self) -> N {
834		self.now -
835			(self.now.saturating_sub(self.session_start_block) % self.group_rotation_frequency)
836	}
837}
838
839/// Information about a core which is currently occupied.
840#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
841#[cfg_attr(feature = "std", derive(PartialEq))]
842pub struct ScheduledCore {
843	/// The ID of a para scheduled.
844	pub para_id: Id,
845	/// DEPRECATED: see: <https://github.com/paritytech/polkadot/issues/7575>
846	///
847	/// Will be removed in a future version.
848	pub collator: Option<CollatorId>,
849}
850
851/// An assumption being made about the state of an occupied core.
852#[derive(Clone, Copy, Encode, Decode, TypeInfo, Debug)]
853#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash))]
854pub enum OccupiedCoreAssumption {
855	/// The candidate occupying the core was made available and included to free the core.
856	#[codec(index = 0)]
857	Included,
858	/// The candidate occupying the core timed out and freed the core without advancing the para.
859	#[codec(index = 1)]
860	TimedOut,
861	/// The core was not occupied to begin with.
862	#[codec(index = 2)]
863	Free,
864}
865
866/// A vote of approval on a candidate.
867#[derive(Clone, Debug)]
868pub struct ApprovalVote(pub CandidateHash);
869
870impl ApprovalVote {
871	/// Yields the signing payload for this approval vote.
872	pub fn signing_payload(&self, session_index: SessionIndex) -> Vec<u8> {
873		const MAGIC: [u8; 4] = *b"APPR";
874
875		(MAGIC, &self.0, session_index).encode()
876	}
877}
878
879/// A vote of approval for multiple candidates.
880#[derive(Clone, Debug)]
881pub struct ApprovalVoteMultipleCandidates<'a>(pub &'a [CandidateHash]);
882
883impl<'a> ApprovalVoteMultipleCandidates<'a> {
884	/// Yields the signing payload for this approval vote.
885	pub fn signing_payload(&self, session_index: SessionIndex) -> Vec<u8> {
886		const MAGIC: [u8; 4] = *b"APPR";
887		// Make this backwards compatible with `ApprovalVote` so if we have just on candidate the
888		// signature will look the same.
889		// This gives us the nice benefit that old nodes can still check signatures when len is 1
890		// and the new node can check the signature coming from old nodes.
891		if self.0.len() == 1 {
892			(MAGIC, self.0.first().expect("QED: we just checked"), session_index).encode()
893		} else {
894			(MAGIC, &self.0, session_index).encode()
895		}
896	}
897}
898
899/// Approval voting configuration parameters
900#[derive(
901	Debug,
902	Copy,
903	Clone,
904	PartialEq,
905	Encode,
906	Decode,
907	DecodeWithMemTracking,
908	TypeInfo,
909	serde::Serialize,
910	serde::Deserialize,
911)]
912pub struct ApprovalVotingParams {
913	/// The maximum number of candidates `approval-voting` can vote for with
914	/// a single signatures.
915	///
916	/// Setting it to 1, means we send the approval as soon as we have it available.
917	pub max_approval_coalesce_count: u32,
918}
919
920impl Default for ApprovalVotingParams {
921	fn default() -> Self {
922		Self { max_approval_coalesce_count: 1 }
923	}
924}
925
926/// Custom validity errors used in Polkadot while validating transactions.
927#[repr(u8)]
928pub enum ValidityError {
929	/// The Ethereum signature is invalid.
930	InvalidEthereumSignature = 0,
931	/// The signer has no claim.
932	SignerHasNoClaim = 1,
933	/// No permission to execute the call.
934	NoPermission = 2,
935	/// An invalid statement was made for a claim.
936	InvalidStatement = 3,
937}
938
939impl From<ValidityError> for u8 {
940	fn from(err: ValidityError) -> Self {
941		err as u8
942	}
943}
944
945/// Abridged version of `HostConfiguration` (from the `Configuration` parachains host runtime
946/// module) meant to be used by a parachain or PDK such as cumulus.
947#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
948#[cfg_attr(feature = "std", derive(PartialEq))]
949pub struct AbridgedHostConfiguration {
950	/// The maximum validation code size, in bytes.
951	pub max_code_size: u32,
952	/// The maximum head-data size, in bytes.
953	pub max_head_data_size: u32,
954	/// Total number of individual messages allowed in the parachain -> relay-chain message queue.
955	pub max_upward_queue_count: u32,
956	/// Total size of messages allowed in the parachain -> relay-chain message queue before which
957	/// no further messages may be added to it. If it exceeds this then the queue may contain only
958	/// a single message.
959	pub max_upward_queue_size: u32,
960	/// The maximum size of an upward message that can be sent by a candidate.
961	///
962	/// This parameter affects the size upper bound of the `CandidateCommitments`.
963	pub max_upward_message_size: u32,
964	/// The maximum number of messages that a candidate can contain.
965	///
966	/// This parameter affects the size upper bound of the `CandidateCommitments`.
967	pub max_upward_message_num_per_candidate: u32,
968	/// The maximum number of outbound HRMP messages can be sent by a candidate.
969	///
970	/// This parameter affects the upper bound of size of `CandidateCommitments`.
971	pub hrmp_max_message_num_per_candidate: u32,
972	/// The minimum period, in blocks, between which parachains can update their validation code.
973	pub validation_upgrade_cooldown: BlockNumber,
974	/// The delay, in blocks, before a validation upgrade is applied.
975	pub validation_upgrade_delay: BlockNumber,
976	/// Asynchronous backing parameters.
977	pub async_backing_params: AsyncBackingParams,
978}
979
980/// Abridged version of `HrmpChannel` (from the `Hrmp` parachains host runtime module) meant to be
981/// used by a parachain or PDK such as cumulus.
982#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
983#[cfg_attr(feature = "std", derive(Default, PartialEq))]
984pub struct AbridgedHrmpChannel {
985	/// The maximum number of messages that can be pending in the channel at once.
986	pub max_capacity: u32,
987	/// The maximum total size of the messages that can be pending in the channel at once.
988	pub max_total_size: u32,
989	/// The maximum message size that could be put into the channel.
990	pub max_message_size: u32,
991	/// The current number of messages pending in the channel.
992	/// Invariant: should be less or equal to `max_capacity`.s`.
993	pub msg_count: u32,
994	/// The total size in bytes of all message payloads in the channel.
995	/// Invariant: should be less or equal to `max_total_size`.
996	pub total_size: u32,
997	/// A head of the Message Queue Chain for this channel. Each link in this chain has a form:
998	/// `(prev_head, B, H(M))`, where
999	/// - `prev_head`: is the previous value of `mqc_head` or zero if none.
1000	/// - `B`: is the [relay-chain] block number in which a message was appended
1001	/// - `H(M)`: is the hash of the message being appended.
1002	/// This value is initialized to a special value that consists of all zeroes which indicates
1003	/// that no messages were previously added.
1004	pub mqc_head: Option<Hash>,
1005}
1006
1007/// A possible upgrade restriction that prevents a parachain from performing an upgrade.
1008#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1009pub enum UpgradeRestriction {
1010	/// There is an upgrade restriction and there are no details about its specifics nor how long
1011	/// it could last.
1012	#[codec(index = 0)]
1013	Present,
1014}
1015
1016/// A struct that the relay-chain communicates to a parachain indicating what course of action the
1017/// parachain should take in the coordinated parachain validation code upgrade process.
1018///
1019/// This data type appears in the last step of the upgrade process. After the parachain observes it
1020/// and reacts to it the upgrade process concludes.
1021#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1022pub enum UpgradeGoAhead {
1023	/// Abort the upgrade process. There is something wrong with the validation code previously
1024	/// submitted by the parachain. This variant can also be used to prevent upgrades by the
1025	/// governance should an emergency emerge.
1026	///
1027	/// The expected reaction on this variant is that the parachain will admit this message and
1028	/// remove all the data about the pending upgrade. Depending on the nature of the problem (to
1029	/// be examined offchain for now), it can try to send another validation code or just retry
1030	/// later.
1031	#[codec(index = 0)]
1032	Abort,
1033	/// Apply the pending code change. The parablock that is built on a relay-parent that is
1034	/// descendant of the relay-parent where the parachain observed this signal must use the
1035	/// upgraded validation code.
1036	#[codec(index = 1)]
1037	GoAhead,
1038}
1039
1040/// Consensus engine id for polkadot v1 consensus engine.
1041pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"POL1";
1042
1043/// A consensus log item for polkadot validation. To be used with [`POLKADOT_ENGINE_ID`].
1044#[derive(Decode, Encode, Clone, PartialEq, Eq)]
1045pub enum ConsensusLog {
1046	/// A parachain upgraded its code.
1047	#[codec(index = 1)]
1048	ParaUpgradeCode(Id, ValidationCodeHash),
1049	/// A parachain scheduled a code upgrade.
1050	#[codec(index = 2)]
1051	ParaScheduleUpgradeCode(Id, ValidationCodeHash, BlockNumber),
1052	/// Governance requests to auto-approve every candidate included up to the given block
1053	/// number in the current chain, inclusive.
1054	#[codec(index = 3)]
1055	ForceApprove(BlockNumber),
1056	/// A signal to revert the block number in the same chain as the
1057	/// header this digest is part of and all of its descendants.
1058	///
1059	/// It is a no-op for a block to contain a revert digest targeting
1060	/// its own number or a higher number.
1061	///
1062	/// In practice, these are issued when on-chain logic has detected an
1063	/// invalid parachain block within its own chain, due to a dispute.
1064	#[codec(index = 4)]
1065	Revert(BlockNumber),
1066}
1067
1068impl ConsensusLog {
1069	/// Attempt to convert a reference to a generic digest item into a consensus log.
1070	pub fn from_digest_item(
1071		digest_item: &sp_runtime::DigestItem,
1072	) -> Result<Option<Self>, codec::Error> {
1073		match digest_item {
1074			sp_runtime::DigestItem::Consensus(id, encoded) if id == &POLKADOT_ENGINE_ID => {
1075				Ok(Some(Self::decode(&mut &encoded[..])?))
1076			},
1077			_ => Ok(None),
1078		}
1079	}
1080}
1081
1082impl From<ConsensusLog> for sp_runtime::DigestItem {
1083	fn from(c: ConsensusLog) -> sp_runtime::DigestItem {
1084		Self::Consensus(POLKADOT_ENGINE_ID, c.encode())
1085	}
1086}
1087
1088/// A statement about a candidate, to be used within the dispute resolution process.
1089///
1090/// Statements are either in favor of the candidate's validity or against it.
1091#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1092pub enum DisputeStatement {
1093	/// A valid statement, of the given kind.
1094	#[codec(index = 0)]
1095	Valid(ValidDisputeStatementKind),
1096	/// An invalid statement, of the given kind.
1097	#[codec(index = 1)]
1098	Invalid(InvalidDisputeStatementKind),
1099}
1100
1101impl DisputeStatement {
1102	/// Get the payload data for this type of dispute statement.
1103	///
1104	/// Returns Error if the candidate_hash is not included in the list of signed
1105	/// candidate from ApprovalCheckingMultipleCandidate.
1106	pub fn payload_data(
1107		&self,
1108		candidate_hash: CandidateHash,
1109		session: SessionIndex,
1110	) -> Result<Vec<u8>, ()> {
1111		match self {
1112			DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => {
1113				Ok(ExplicitDisputeStatement { valid: true, candidate_hash, session }
1114					.signing_payload())
1115			},
1116			DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(
1117				inclusion_parent,
1118			)) => Ok(CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext {
1119				session_index: session,
1120				parent_hash: *inclusion_parent,
1121			})),
1122			DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => {
1123				Ok(CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext {
1124					session_index: session,
1125					parent_hash: *inclusion_parent,
1126				}))
1127			},
1128			DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => {
1129				Ok(ApprovalVote(candidate_hash).signing_payload(session))
1130			},
1131			DisputeStatement::Valid(
1132				ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes),
1133			) => {
1134				if candidate_hashes.contains(&candidate_hash) {
1135					Ok(ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session))
1136				} else {
1137					Err(())
1138				}
1139			},
1140			DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => {
1141				Ok(ExplicitDisputeStatement { valid: false, candidate_hash, session }
1142					.signing_payload())
1143			},
1144		}
1145	}
1146
1147	/// Check the signature on a dispute statement.
1148	pub fn check_signature(
1149		&self,
1150		validator_public: &ValidatorId,
1151		candidate_hash: CandidateHash,
1152		session: SessionIndex,
1153		validator_signature: &ValidatorSignature,
1154	) -> Result<(), ()> {
1155		let payload = self.payload_data(candidate_hash, session)?;
1156
1157		if validator_signature.verify(&payload[..], &validator_public) {
1158			Ok(())
1159		} else {
1160			Err(())
1161		}
1162	}
1163
1164	/// Whether the statement indicates validity.
1165	pub fn indicates_validity(&self) -> bool {
1166		match *self {
1167			DisputeStatement::Valid(_) => true,
1168			DisputeStatement::Invalid(_) => false,
1169		}
1170	}
1171
1172	/// Whether the statement indicates invalidity.
1173	pub fn indicates_invalidity(&self) -> bool {
1174		match *self {
1175			DisputeStatement::Valid(_) => false,
1176			DisputeStatement::Invalid(_) => true,
1177		}
1178	}
1179
1180	/// Statement is backing statement.
1181	pub fn is_backing(&self) -> bool {
1182		match self {
1183			Self::Valid(s) => s.is_backing(),
1184			Self::Invalid(_) => false,
1185		}
1186	}
1187}
1188
1189/// The maximum number of candidate approvals that can be coalesced into a single
1190/// [`ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates`] statement (and the
1191/// corresponding approval vote). It is a hard, type-level ceiling enforced at decode time, and the
1192/// maximum value the runtime accepts for `max_approval_coalesce_count`.
1193pub const MAX_COALESCE_APPROVALS: u32 = 16;
1194
1195/// The candidate hashes coalesced into a single approval vote, bounded at the type level to at most
1196/// [`MAX_COALESCE_APPROVALS`]. Used to carry coalesced approval signatures between subsystems while
1197/// preserving that bound.
1198pub type CoalescedApprovalCandidateHashes =
1199	BoundedVec<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>>;
1200
1201/// Different kinds of statements of validity on  a candidate.
1202#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1203pub enum ValidDisputeStatementKind {
1204	/// An explicit statement issued as part of a dispute.
1205	#[codec(index = 0)]
1206	Explicit,
1207	/// A seconded statement on a candidate from the backing phase.
1208	#[codec(index = 1)]
1209	BackingSeconded(Hash),
1210	/// A valid statement on a candidate from the backing phase.
1211	#[codec(index = 2)]
1212	BackingValid(Hash),
1213	/// An approval vote from the approval checking phase.
1214	#[codec(index = 3)]
1215	ApprovalChecking,
1216	/// An approval vote from the new version.
1217	/// We can't create this version until all nodes
1218	/// have been updated to support it and max_approval_coalesce_count
1219	/// is set to more than 1.
1220	///
1221	/// The number of coalesced candidates is bounded at the type level to at most
1222	/// [`MAX_COALESCE_APPROVALS`]. This is a hard ceiling enforced at decode time; the effective
1223	/// per-session limit is `max_approval_coalesce_count`, enforced dynamically when the statement
1224	/// is validated.
1225	#[codec(index = 4)]
1226	ApprovalCheckingMultipleCandidates(
1227		BoundedVec<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>>,
1228	),
1229}
1230
1231impl ValidDisputeStatementKind {
1232	/// Whether the statement is from the backing phase.
1233	pub fn is_backing(&self) -> bool {
1234		match self {
1235			ValidDisputeStatementKind::BackingSeconded(_) |
1236			ValidDisputeStatementKind::BackingValid(_) => true,
1237			ValidDisputeStatementKind::Explicit |
1238			ValidDisputeStatementKind::ApprovalChecking |
1239			ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_) => false,
1240		}
1241	}
1242}
1243
1244/// Different kinds of statements of invalidity on a candidate.
1245#[derive(Encode, Decode, DecodeWithMemTracking, Copy, Clone, PartialEq, Debug, TypeInfo)]
1246pub enum InvalidDisputeStatementKind {
1247	/// An explicit statement issued as part of a dispute.
1248	#[codec(index = 0)]
1249	Explicit,
1250}
1251
1252/// An explicit statement on a candidate issued as part of a dispute.
1253#[derive(Clone, PartialEq, Debug)]
1254pub struct ExplicitDisputeStatement {
1255	/// Whether the candidate is valid
1256	pub valid: bool,
1257	/// The candidate hash.
1258	pub candidate_hash: CandidateHash,
1259	/// The session index of the candidate.
1260	pub session: SessionIndex,
1261}
1262
1263impl ExplicitDisputeStatement {
1264	/// Produce the payload used for signing this type of statement.
1265	pub fn signing_payload(&self) -> Vec<u8> {
1266		const MAGIC: [u8; 4] = *b"DISP";
1267
1268		(MAGIC, self.valid, self.candidate_hash, self.session).encode()
1269	}
1270}
1271
1272/// A set of statements about a specific candidate.
1273#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1274pub struct DisputeStatementSet {
1275	/// The candidate referenced by this set.
1276	pub candidate_hash: CandidateHash,
1277	/// The session index of the candidate.
1278	pub session: SessionIndex,
1279	/// Statements about the candidate.
1280	pub statements: Vec<(DisputeStatement, ValidatorIndex, ValidatorSignature)>,
1281}
1282
1283impl From<CheckedDisputeStatementSet> for DisputeStatementSet {
1284	fn from(other: CheckedDisputeStatementSet) -> Self {
1285		other.0
1286	}
1287}
1288
1289impl AsRef<DisputeStatementSet> for DisputeStatementSet {
1290	fn as_ref(&self) -> &DisputeStatementSet {
1291		&self
1292	}
1293}
1294
1295/// A set of dispute statements.
1296pub type MultiDisputeStatementSet = Vec<DisputeStatementSet>;
1297
1298/// A _checked_ set of dispute statements.
1299#[derive(Clone, PartialEq, Debug, Encode)]
1300pub struct CheckedDisputeStatementSet(DisputeStatementSet);
1301
1302impl AsRef<DisputeStatementSet> for CheckedDisputeStatementSet {
1303	fn as_ref(&self) -> &DisputeStatementSet {
1304		&self.0
1305	}
1306}
1307
1308impl core::cmp::PartialEq<DisputeStatementSet> for CheckedDisputeStatementSet {
1309	fn eq(&self, other: &DisputeStatementSet) -> bool {
1310		self.0.eq(other)
1311	}
1312}
1313
1314impl CheckedDisputeStatementSet {
1315	/// Convert from an unchecked, the verification of correctness of the `unchecked` statement set
1316	/// _must_ be done before calling this function!
1317	pub fn unchecked_from_unchecked(unchecked: DisputeStatementSet) -> Self {
1318		Self(unchecked)
1319	}
1320}
1321
1322/// A set of _checked_ dispute statements.
1323pub type CheckedMultiDisputeStatementSet = Vec<CheckedDisputeStatementSet>;
1324
1325/// The entire state of a dispute.
1326#[derive(Encode, Decode, Clone, Debug, PartialEq, TypeInfo)]
1327pub struct DisputeState<N = BlockNumber> {
1328	/// A bitfield indicating all validators for the candidate.
1329	pub validators_for: BitVec<u8, bitvec::order::Lsb0>, // one bit per validator.
1330	/// A bitfield indicating all validators against the candidate.
1331	pub validators_against: BitVec<u8, bitvec::order::Lsb0>, // one bit per validator.
1332	/// The block number at which the dispute started on-chain.
1333	pub start: N,
1334	/// The block number at which the dispute concluded on-chain.
1335	pub concluded_at: Option<N>,
1336}
1337
1338/// An either implicit or explicit attestation to the validity of a parachain
1339/// candidate.
1340#[derive(Clone, Eq, PartialEq, Decode, DecodeWithMemTracking, Encode, Debug, TypeInfo)]
1341pub enum ValidityAttestation {
1342	/// Implicit validity attestation by issuing.
1343	/// This corresponds to issuance of a `Candidate` statement.
1344	#[codec(index = 1)]
1345	Implicit(ValidatorSignature),
1346	/// An explicit attestation. This corresponds to issuance of a
1347	/// `Valid` statement.
1348	#[codec(index = 2)]
1349	Explicit(ValidatorSignature),
1350}
1351
1352impl ValidityAttestation {
1353	/// Produce the underlying signed payload of the attestation, given the hash of the candidate,
1354	/// which should be known in context.
1355	pub fn to_compact_statement(&self, candidate_hash: CandidateHash) -> CompactStatement {
1356		// Explicit and implicit map directly from
1357		// `ValidityVote::Valid` and `ValidityVote::Issued`, and hence there is a
1358		// `1:1` relationship which enables the conversion.
1359		match *self {
1360			ValidityAttestation::Implicit(_) => CompactStatement::Seconded(candidate_hash),
1361			ValidityAttestation::Explicit(_) => CompactStatement::Valid(candidate_hash),
1362		}
1363	}
1364
1365	/// Get a reference to the signature.
1366	pub fn signature(&self) -> &ValidatorSignature {
1367		match *self {
1368			ValidityAttestation::Implicit(ref sig) => sig,
1369			ValidityAttestation::Explicit(ref sig) => sig,
1370		}
1371	}
1372
1373	/// Produce the underlying signed payload of the attestation, given the hash of the candidate,
1374	/// which should be known in context.
1375	pub fn signed_payload<H: Encode>(
1376		&self,
1377		candidate_hash: CandidateHash,
1378		signing_context: &SigningContext<H>,
1379	) -> Vec<u8> {
1380		match *self {
1381			ValidityAttestation::Implicit(_) => {
1382				(CompactStatement::Seconded(candidate_hash), signing_context).encode()
1383			},
1384			ValidityAttestation::Explicit(_) => {
1385				(CompactStatement::Valid(candidate_hash), signing_context).encode()
1386			},
1387		}
1388	}
1389}
1390
1391/// A type returned by runtime with current session index and a parent hash.
1392#[derive(Clone, Eq, PartialEq, Default, Decode, Encode, Debug)]
1393pub struct SigningContext<H = Hash> {
1394	/// Current session index.
1395	pub session_index: sp_staking::SessionIndex,
1396	/// Hash of the parent.
1397	pub parent_hash: H,
1398}
1399
1400const BACKING_STATEMENT_MAGIC: [u8; 4] = *b"BKNG";
1401
1402/// Statements that can be made about parachain candidates. These are the
1403/// actual values that are signed.
1404#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
1405#[cfg_attr(feature = "std", derive(Hash))]
1406pub enum CompactStatement {
1407	/// Proposal of a parachain candidate.
1408	Seconded(CandidateHash),
1409	/// State that a parachain candidate is valid.
1410	Valid(CandidateHash),
1411}
1412
1413impl CompactStatement {
1414	/// Yields the payload used for validator signatures on this kind
1415	/// of statement.
1416	pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
1417		(self, context).encode()
1418	}
1419
1420	/// Get the underlying candidate hash this references.
1421	pub fn candidate_hash(&self) -> &CandidateHash {
1422		match *self {
1423			CompactStatement::Seconded(ref h) | CompactStatement::Valid(ref h) => h,
1424		}
1425	}
1426}
1427
1428// Inner helper for codec on `CompactStatement`.
1429#[derive(Encode, Decode, TypeInfo)]
1430enum CompactStatementInner {
1431	#[codec(index = 1)]
1432	Seconded(CandidateHash),
1433	#[codec(index = 2)]
1434	Valid(CandidateHash),
1435}
1436
1437impl From<CompactStatement> for CompactStatementInner {
1438	fn from(s: CompactStatement) -> Self {
1439		match s {
1440			CompactStatement::Seconded(h) => CompactStatementInner::Seconded(h),
1441			CompactStatement::Valid(h) => CompactStatementInner::Valid(h),
1442		}
1443	}
1444}
1445
1446impl codec::Encode for CompactStatement {
1447	fn size_hint(&self) -> usize {
1448		// magic + discriminant + payload
1449		4 + 1 + 32
1450	}
1451
1452	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
1453		dest.write(&BACKING_STATEMENT_MAGIC);
1454		CompactStatementInner::from(self.clone()).encode_to(dest)
1455	}
1456}
1457
1458impl codec::Decode for CompactStatement {
1459	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
1460		let maybe_magic = <[u8; 4]>::decode(input)?;
1461		if maybe_magic != BACKING_STATEMENT_MAGIC {
1462			return Err(codec::Error::from("invalid magic string"));
1463		}
1464
1465		Ok(match CompactStatementInner::decode(input)? {
1466			CompactStatementInner::Seconded(h) => CompactStatement::Seconded(h),
1467			CompactStatementInner::Valid(h) => CompactStatement::Valid(h),
1468		})
1469	}
1470}
1471
1472/// `IndexedVec` struct indexed by type specific indices.
1473#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1474#[cfg_attr(feature = "std", derive(PartialEq))]
1475pub struct IndexedVec<K, V>(Vec<V>, PhantomData<fn(K) -> K>);
1476
1477impl<K, V> Default for IndexedVec<K, V> {
1478	fn default() -> Self {
1479		Self(vec![], PhantomData)
1480	}
1481}
1482
1483impl<K, V> From<Vec<V>> for IndexedVec<K, V> {
1484	fn from(validators: Vec<V>) -> Self {
1485		Self(validators, PhantomData)
1486	}
1487}
1488
1489impl<K, V> FromIterator<V> for IndexedVec<K, V> {
1490	fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
1491		Self(Vec::from_iter(iter), PhantomData)
1492	}
1493}
1494
1495impl<K, V> IndexedVec<K, V>
1496where
1497	V: Clone,
1498{
1499	/// Returns a reference to an element indexed using `K`.
1500	pub fn get(&self, index: K) -> Option<&V>
1501	where
1502		K: TypeIndex,
1503	{
1504		self.0.get(index.type_index())
1505	}
1506
1507	/// Returns a mutable reference to an element indexed using `K`.
1508	pub fn get_mut(&mut self, index: K) -> Option<&mut V>
1509	where
1510		K: TypeIndex,
1511	{
1512		self.0.get_mut(index.type_index())
1513	}
1514
1515	/// Returns number of elements in vector.
1516	pub fn len(&self) -> usize {
1517		self.0.len()
1518	}
1519
1520	/// Returns contained vector.
1521	pub fn to_vec(&self) -> Vec<V> {
1522		self.0.clone()
1523	}
1524
1525	/// Returns an iterator over the underlying vector.
1526	pub fn iter(&self) -> Iter<'_, V> {
1527		self.0.iter()
1528	}
1529
1530	/// Returns a mutable iterator over the underlying vector.
1531	pub fn iter_mut(&mut self) -> IterMut<'_, V> {
1532		self.0.iter_mut()
1533	}
1534
1535	/// Creates a consuming iterator.
1536	pub fn into_iter(self) -> IntoIter<V> {
1537		self.0.into_iter()
1538	}
1539
1540	/// Returns true if the underlying container is empty.
1541	pub fn is_empty(&self) -> bool {
1542		self.0.is_empty()
1543	}
1544}
1545
1546/// The maximum number of validators `f` which may safely be faulty.
1547///
1548/// The total number of validators is `n = 3f + e` where `e in { 1, 2, 3 }`.
1549pub const fn byzantine_threshold(n: usize) -> usize {
1550	n.saturating_sub(1) / 3
1551}
1552
1553/// The supermajority threshold of validators which represents a subset
1554/// guaranteed to have at least f+1 honest validators.
1555pub const fn supermajority_threshold(n: usize) -> usize {
1556	n - byzantine_threshold(n)
1557}
1558
1559/// Adjust the configured needed backing votes with the size of the backing group.
1560pub fn effective_minimum_backing_votes(
1561	group_len: usize,
1562	configured_minimum_backing_votes: u32,
1563) -> usize {
1564	core::cmp::min(group_len, configured_minimum_backing_votes as usize)
1565}
1566
1567/// Information about validator sets of a session.
1568///
1569/// NOTE: `SessionInfo` is frozen. Do not include new fields, consider creating a separate runtime
1570/// API. Reasoning and further outlook [here](https://github.com/paritytech/polkadot/issues/6586).
1571#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1572#[cfg_attr(feature = "std", derive(PartialEq))]
1573pub struct SessionInfo {
1574	/// **** New in v2 ******
1575	/// All the validators actively participating in parachain consensus.
1576	/// Indices are into the broader validator set.
1577	pub active_validator_indices: Vec<ValidatorIndex>,
1578	/// A secure random seed for the session, gathered from BABE.
1579	pub random_seed: [u8; 32],
1580	/// The amount of sessions to keep for disputes.
1581	pub dispute_period: SessionIndex,
1582
1583	/// **** Old fields *****
1584	/// Validators in canonical ordering.
1585	///
1586	/// NOTE: There might be more authorities in the current session, than `validators`
1587	/// participating in parachain consensus. See
1588	/// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
1589	///
1590	/// `SessionInfo::validators` will be limited to `max_validators` when set.
1591	pub validators: IndexedVec<ValidatorIndex, ValidatorId>,
1592	/// Validators' authority discovery keys for the session in canonical ordering.
1593	///
1594	/// NOTE: The first `validators.len()` entries will match the corresponding validators in
1595	/// `validators`, afterwards any remaining authorities can be found. This is any authorities
1596	/// not participating in parachain consensus - see
1597	/// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148)
1598	pub discovery_keys: Vec<AuthorityDiscoveryId>,
1599	/// The assignment keys for validators.
1600	///
1601	/// NOTE: There might be more authorities in the current session, than validators participating
1602	/// in parachain consensus. See
1603	/// [`max_validators`](https://github.com/paritytech/polkadot/blob/a52dca2be7840b23c19c153cf7e110b1e3e475f8/runtime/parachains/src/configuration.rs#L148).
1604	///
1605	/// Therefore:
1606	/// ```ignore
1607	/// 	assignment_keys.len() == validators.len() && validators.len() <= discovery_keys.len()
1608	/// ```
1609	pub assignment_keys: Vec<AssignmentId>,
1610	/// Validators in shuffled ordering - these are the validator groups as produced
1611	/// by the `Scheduler` module for the session and are typically referred to by
1612	/// `GroupIndex`.
1613	pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
1614	/// The number of availability cores used by the protocol during this session.
1615	pub n_cores: u32,
1616	/// The zeroth delay tranche width.
1617	pub zeroth_delay_tranche_width: u32,
1618	/// The number of samples we do of `relay_vrf_modulo`.
1619	pub relay_vrf_modulo_samples: u32,
1620	/// The number of delay tranches in total.
1621	pub n_delay_tranches: u32,
1622	/// How many slots (BABE / SASSAFRAS) must pass before an assignment is considered a
1623	/// no-show.
1624	pub no_show_slots: u32,
1625	/// The number of validators needed to approve a block.
1626	pub needed_approvals: u32,
1627}
1628
1629/// A statement from the specified validator whether the given validation code passes PVF
1630/// pre-checking or not anchored to the given session index.
1631#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1632pub struct PvfCheckStatement {
1633	/// `true` if the subject passed pre-checking and `false` otherwise.
1634	pub accept: bool,
1635	/// The validation code hash that was checked.
1636	pub subject: ValidationCodeHash,
1637	/// The index of a session during which this statement is considered valid.
1638	pub session_index: SessionIndex,
1639	/// The index of the validator from which this statement originates.
1640	pub validator_index: ValidatorIndex,
1641}
1642
1643impl PvfCheckStatement {
1644	/// Produce the payload used for signing this type of statement.
1645	///
1646	/// It is expected that it will be signed by the validator at `validator_index` in the
1647	/// `session_index`.
1648	pub fn signing_payload(&self) -> Vec<u8> {
1649		const MAGIC: [u8; 4] = *b"VCPC"; // for "validation code pre-checking"
1650		(MAGIC, self.accept, self.subject, self.session_index, self.validator_index).encode()
1651	}
1652}
1653
1654/// A well-known and typed storage key.
1655///
1656/// Allows for type-safe access to raw well-known storage keys.
1657pub struct WellKnownKey<T> {
1658	/// The raw storage key.
1659	pub key: Vec<u8>,
1660	_p: core::marker::PhantomData<T>,
1661}
1662
1663impl<T> From<Vec<u8>> for WellKnownKey<T> {
1664	fn from(key: Vec<u8>) -> Self {
1665		Self { key, _p: Default::default() }
1666	}
1667}
1668
1669impl<T> AsRef<[u8]> for WellKnownKey<T> {
1670	fn as_ref(&self) -> &[u8] {
1671		self.key.as_ref()
1672	}
1673}
1674
1675impl<T: Decode> WellKnownKey<T> {
1676	/// Gets the value or `None` if it does not exist or decoding failed.
1677	pub fn get(&self) -> Option<T> {
1678		sp_io::storage::get(&self.key)
1679			.and_then(|raw| codec::DecodeAll::decode_all(&mut raw.as_ref()).ok())
1680	}
1681}
1682
1683impl<T: Encode> WellKnownKey<T> {
1684	/// Sets the value.
1685	pub fn set(&self, value: T) {
1686		sp_io::storage::set(&self.key, &value.encode());
1687	}
1688}
1689
1690/// Type discriminator for PVF preparation.
1691#[derive(
1692	Encode,
1693	Decode,
1694	DecodeWithMemTracking,
1695	TypeInfo,
1696	Clone,
1697	Copy,
1698	Debug,
1699	PartialEq,
1700	Eq,
1701	Serialize,
1702	Deserialize,
1703)]
1704pub enum PvfPrepKind {
1705	/// For prechecking requests.
1706	Precheck,
1707
1708	/// For execution and heads-up requests.
1709	Prepare,
1710}
1711
1712/// Type discriminator for PVF execution.
1713#[derive(
1714	Encode,
1715	Decode,
1716	DecodeWithMemTracking,
1717	TypeInfo,
1718	Clone,
1719	Copy,
1720	Debug,
1721	PartialEq,
1722	Eq,
1723	Serialize,
1724	Deserialize,
1725)]
1726pub enum PvfExecKind {
1727	/// For backing requests.
1728	Backing,
1729	/// For approval and dispute request.
1730	Approval,
1731}
1732
1733/// Bit indices in the `HostConfiguration.node_features` that correspond to different node features.
1734pub type NodeFeatures = BitVec<u8, bitvec::order::Lsb0>;
1735
1736/// Module containing feature-specific bit indices into the `NodeFeatures` bitvec.
1737pub mod node_features {
1738	use crate::NodeFeatures;
1739
1740	/// A feature index used to identify a bit into the node_features array stored
1741	/// in the HostConfiguration.
1742	#[repr(u8)]
1743	#[derive(Clone, Copy)]
1744	pub enum FeatureIndex {
1745		/// Tells if tranch0 assignments could be sent in a single certificate.
1746		/// Reserved for: `<https://github.com/paritytech/polkadot-sdk/issues/628>`
1747		EnableAssignmentsV2 = 0,
1748		/// This feature enables the extension of `BackedCandidate::validator_indices` by 8 bits.
1749		/// The value stored there represents the assumed core index where the candidates
1750		/// are backed. This is needed for the elastic scaling MVP.
1751		ElasticScalingMVP = 1,
1752		/// Tells if the chunk mapping feature is enabled.
1753		/// Enables the implementation of
1754		/// [RFC-47](https://github.com/polkadot-fellows/RFCs/blob/main/text/0047-assignment-of-availability-chunks.md).
1755		/// Must not be enabled unless all validators and collators have stopped using `req_chunk`
1756		/// protocol version 1. If it is enabled, validators can start systematic chunk recovery.
1757		AvailabilityChunkMapping = 2,
1758		/// Enables node side support of `CoreIndex` committed candidate receipts.
1759		/// See [RFC-103](https://github.com/polkadot-fellows/RFCs/pull/103) for details.
1760		/// Only enable if at least 2/3 of nodes support the feature.
1761		CandidateReceiptV2 = 3,
1762		/// Enables support for scheduling information in the Candidate Descriptor.
1763		CandidateReceiptV3 = 4,
1764		/// First unassigned feature bit.
1765		/// Every time a new feature flag is assigned it should take this value.
1766		/// and this should be incremented.
1767		FirstUnassigned = 5,
1768	}
1769
1770	impl FeatureIndex {
1771		/// Check wheter the feature is enabled.
1772		pub fn is_set(self, node_features: &NodeFeatures) -> bool {
1773			node_features.get(self as usize).map(|v| *v).unwrap_or(false)
1774		}
1775	}
1776}
1777
1778/// Scheduler configuration parameters. All coretime/ondemand parameters are here.
1779#[derive(
1780	Debug,
1781	Copy,
1782	Clone,
1783	PartialEq,
1784	Encode,
1785	Decode,
1786	DecodeWithMemTracking,
1787	TypeInfo,
1788	serde::Serialize,
1789	serde::Deserialize,
1790)]
1791pub struct SchedulerParams<BlockNumber> {
1792	/// How often parachain groups should be rotated across parachains.
1793	///
1794	/// Must be non-zero.
1795	pub group_rotation_frequency: BlockNumber,
1796	/// Availability timeout for a block on a core, measured in blocks.
1797	///
1798	/// This is the maximum amount of blocks after a core became occupied that validators have time
1799	/// to make the block available.
1800	///
1801	/// This value only has effect on group rotations. If backers backed something at the end of
1802	/// their rotation, the occupied core affects the backing group that comes afterwards. We limit
1803	/// the effect one backing group can have on the next to `paras_availability_period` blocks.
1804	///
1805	/// Within a group rotation there is no timeout as backers are only affecting themselves.
1806	///
1807	/// Must be at least 1. With a value of 1, the previous group will not be able to negatively
1808	/// affect the following group at the expense of a tight availability timeline at group
1809	/// rotation boundaries.
1810	pub paras_availability_period: BlockNumber,
1811	/// The maximum number of validators to have per core.
1812	///
1813	/// `None` means no maximum.
1814	pub max_validators_per_core: Option<u32>,
1815	/// The amount of blocks ahead to schedule paras.
1816	pub lookahead: u32,
1817	/// How many cores are managed by the coretime chain.
1818	pub num_cores: u32,
1819	/// Deprecated and no longer used by the runtime.
1820	/// Removal is tracked by <https://github.com/paritytech/polkadot-sdk/issues/6067>.
1821	#[deprecated]
1822	pub max_availability_timeouts: u32,
1823	/// The maximum queue size of the pay as you go module.
1824	pub on_demand_queue_max_size: u32,
1825	/// The target utilization of the spot price queue in percentages.
1826	pub on_demand_target_queue_utilization: Perbill,
1827	/// How quickly the fee rises in reaction to increased utilization.
1828	/// The lower the number the slower the increase.
1829	pub on_demand_fee_variability: Perbill,
1830	/// The minimum amount needed to claim a slot in the spot pricing queue.
1831	pub on_demand_base_fee: Balance,
1832	/// Deprecated and no longer used by the runtime.
1833	/// Removal is tracked by <https://github.com/paritytech/polkadot-sdk/issues/6067>.
1834	#[deprecated]
1835	pub ttl: BlockNumber,
1836}
1837
1838impl<BlockNumber: Default + From<u32>> Default for SchedulerParams<BlockNumber> {
1839	#[allow(deprecated)]
1840	fn default() -> Self {
1841		Self {
1842			group_rotation_frequency: 1u32.into(),
1843			paras_availability_period: 1u32.into(),
1844			max_validators_per_core: Default::default(),
1845			lookahead: 1,
1846			num_cores: Default::default(),
1847			max_availability_timeouts: Default::default(),
1848			on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE,
1849			on_demand_target_queue_utilization: Perbill::from_percent(25),
1850			on_demand_fee_variability: Perbill::from_percent(3),
1851			on_demand_base_fee: 10_000_000u128,
1852			ttl: 5u32.into(),
1853		}
1854	}
1855}
1856
1857/// A type representing the version of the candidate descriptor.
1858#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, TypeInfo, Debug, PartialOrd, Ord, Hash)]
1859pub enum CandidateDescriptorVersion {
1860	/// with deprecated collator id and collator signature.
1861	V1,
1862	/// First properly versioned candidate.
1863	///
1864	/// - Removes collator signature and collator id fields.
1865	/// - Introduces:
1866	/// -- A version field.
1867	/// -- session index field.
1868	/// -- core index field.
1869	V2,
1870	/// Candidate with scheduling info.
1871	V3,
1872	/// An unknown/not yet supported version.
1873	///
1874	/// Such a candidate must be dropped by the runtime and rejected by backers.
1875	Unknown,
1876}
1877
1878/// Error returned by [`CandidateDescriptorV2::check_version_acceptance`].
1879#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1880pub enum CandidateDescriptorVersionCheckError {
1881	/// Old-style and new-style version detection disagree, and this is not the
1882	/// expected V3 disagreement (old rules → V1, new rules → V3) with V3 enabled.
1883	Inconsistency,
1884	/// The descriptor is V3 but the V3 feature is not enabled.
1885	V3NotEnabled,
1886}
1887
1888// Manual Display impl required because this type is used in `no_std` runtime
1889// code (paras_inherent) where thiserror::Error is not available.
1890impl core::fmt::Display for CandidateDescriptorVersionCheckError {
1891	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1892		match self {
1893			Self::Inconsistency => {
1894				write!(f, "Descriptor version detection inconsistency (old vs new rules disagree)")
1895			},
1896			Self::V3NotEnabled => write!(f, "V3 candidate descriptor but V3 feature not enabled"),
1897		}
1898	}
1899}
1900
1901/// A unique descriptor of the candidate receipt.
1902#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1903pub struct CandidateDescriptorV2<H = Hash> {
1904	/// The ID of the para this is a candidate for.
1905	pub(super) para_id: ParaId,
1906	/// The hash of the relay-chain block this is executed in the context of.
1907	relay_parent: H,
1908	/// Version field. The raw value here is not exposed, instead it is used
1909	/// to determine the `CandidateDescriptorVersion`, see `fn version()`.
1910	/// For the current version this field is set to `0` and will be incremented
1911	/// by next versions.
1912	pub(super) version: u8,
1913	/// The core index where the candidate is backed.
1914	pub(super) core_index: u16,
1915	/// The session index of the candidate relay parent.
1916	session_index: SessionIndex,
1917	/// Offset from `session_index` to derive the scheduling session (introduced in v3).
1918	///
1919	/// Stored as a `u8` offset rather than a full `SessionIndex` to fit within the
1920	/// descriptor layout: `scheduling_session = session_index + scheduling_session_offset`.
1921	scheduling_session_offset: u8,
1922	/// Reserved bytes.
1923	reserved1: [u8; 24],
1924	/// The blake2-256 hash of the persisted validation data. This is extra data derived from
1925	/// relay-chain state which may vary based on bitfields included before the candidate.
1926	/// Thus it cannot be derived entirely from the relay-parent.
1927	persisted_validation_data_hash: Hash,
1928	/// The blake2-256 hash of the PoV.
1929	pov_hash: Hash,
1930	/// The root of a block's erasure encoding Merkle tree.
1931	erasure_root: Hash,
1932	/// The relay chain block determining scheduling.
1933	scheduling_parent: H, // Introduced in v3
1934	/// Reserved bytes.
1935	reserved2: [u8; 32],
1936	/// Hash of the para header that is being generated by this candidate.
1937	para_head: Hash,
1938	/// The blake2-256 hash of the validation code bytes.
1939	validation_code_hash: ValidationCodeHash,
1940}
1941
1942impl<H: AsRef<[u8]>> CandidateDescriptorV2<H> {
1943	/// Returns the candidate descriptor version.
1944	///
1945	/// NOTE: The candidate descriptor versioning is subtle for as long as we
1946	/// need to support the unversioned V1. The issue is that by default we
1947	/// assume a V1 descriptor - as soon as any of the reserved bytes are
1948	/// non-zero. Now if we introduce any new fields, then there will exist
1949	/// candidates where any old node will think that descriptors of that new
1950	/// version are actually V1 (non-zero contents), while upgraded nodes will
1951	/// either see v3 or an unknown version.
1952	///
1953	/// We solve this by completely gating v3 behavior behind the v3 node
1954	/// feature, which must only be enabled once enough validators have upgraded
1955	/// to support it. Any backers still running on the old version are
1956	/// protected by the relay chain runtime, which will drop any illegally
1957	/// (under v3) backed candidates.
1958	///
1959	/// For this to work we now also require a present UMP signal for any
1960	/// version higher or equal than V3. This is enforced by the runtime.
1961	///
1962	/// Via this, if an old node was presented a v3 candidate, which it would
1963	/// consider a V1, it would either detect itself that it is invalid, because
1964	/// of present UMP signals - which is illegal on v1 or the candidate would
1965	/// get rejected by the runtime, because for v3 UMP signals are mandatory.
1966	/// In both cases the backer wont't be slashed.
1967	///
1968	/// There are also candidates that would be treated as v1 by old nodes, but
1969	/// would result in an Unknown version on updated clients. For this
1970	/// scenario, also the runtime provides protection:
1971	///
1972	/// 1. Before the feature is enabled, all nodes will behave as if no v3
1973	/// would exist - all nodes would detect a V1.
1974	/// 2. After the upgrade, the runtime will also (in addition to upgraded
1975	/// nodes) detect an unknown version and no v1 and thus would drop it.
1976	///
1977	/// TL;DR: Yes old nodes will errorneously treat v3 candidates as v1, but we
1978	/// ensure via the relay chain runtime that this stays harmless for backers.
1979	/// V2 approval voters would get disabled, which means a super majority must
1980	/// have updated before enabling the v3 node feature.
1981	///
1982	/// Crucially for this to work: Behavior must not change before the node
1983	/// feature is present and enabled, together with new UMP signal
1984	/// requirements, the runtime can provide the necessary protection.
1985	///
1986	/// To ease future upgrades, we reduced the v1 check once v3 is enabled, so
1987	/// some actually unused bytes are available (don't affect the v1 version
1988	/// check).
1989	///
1990	/// Always uses the relaxed (v3-capable) detection logic. This means
1991	/// version detection is self-contained and does not require knowing
1992	/// whether the V3 node feature is enabled.
1993	///
1994	/// The safety invariant is maintained by the runtime and backing
1995	/// subsystem: they reject candidates where `version()` and
1996	/// `version_old_rules()` disagree when V3 is not yet enabled, and
1997	/// reject V3 candidates outright when V3 is not enabled.
1998	///
1999	/// During the V3 transition, approval checkers, dispute participants,
2000	/// and on-chain vote scrapers must use [`Self::version_for_candidate_validation`]
2001	/// (and the corresponding `scheduling_parent_for_candidate_validation` /
2002	/// `scheduling_session_for_candidate_validation`) instead of `version()`
2003	/// directly. This ensures they match old backer semantics before the V3
2004	/// node feature is confirmed enabled. See those methods for the full
2005	/// safety argument.
2006	pub fn version(&self) -> CandidateDescriptorVersion {
2007		self.v3_version()
2008	}
2009
2010	/// Detect the version using the pre-V3 (stricter) rules.
2011	///
2012	/// Under these rules, all reserved fields, `scheduling_parent`, and
2013	/// `scheduling_session_offset` must be zero for a descriptor to be
2014	/// considered V2. Any non-zero value in those fields causes V1
2015	/// detection. V3 descriptors appear as V1 under these rules.
2016	///
2017	/// Used together with `version()` in consistency checks: if the two
2018	/// methods disagree, the candidate is ambiguous and must be rejected
2019	/// when V3 is not enabled.
2020	pub fn version_old_rules(&self) -> CandidateDescriptorVersion {
2021		self.v2_version()
2022	}
2023
2024	/// Returns `true` if the old-style and new-style version detection agree.
2025	///
2026	/// When V3 is not enabled, both runtime and backing must reject candidates
2027	/// where this returns `false`, preventing ambiguous candidates from landing
2028	/// on-chain. Once V3 is enabled, disagreement is expected for V3 candidates
2029	/// (old rules see V1, new rules see V3) and this check is skipped.
2030	pub fn check_version_consistency(&self) -> bool {
2031		self.version() == self.version_old_rules()
2032	}
2033
2034	/// Validates that the descriptor version is acceptable given whether V3 is enabled.
2035	///
2036	/// Used by both the runtime (`check_descriptor_version_and_signals`) and the
2037	/// backing subsystem. Serves two distinct purposes:
2038	///
2039	/// 1. **V2 ambiguity protection (long-lived):** Old-style and new-style version detection must
2040	///    agree, unless the candidate is V3 and V3 is enabled (the expected disagreement: old rules
2041	///    see V1, new rules see V3). This prevents a crafted candidate from being treated as V2 (no
2042	///    mandatory UMP signals) by new nodes but as V1 by old nodes. Needed as long as V1 exists
2043	///    (maximum safety) or until we could have valiators not yet using the new rules.
2044	///
2045	/// 2. **V3 gating (transitional):** V3 candidates are rejected when V3 is not enabled.
2046	///
2047	/// Note: Consistent `Unknown` versions are not our concern here — they are caught upstream
2048	/// by the runtime (`check_descriptor_version_and_signals`) and the collator
2049	/// protocol (`descriptor_version_sanity_check`).
2050	pub fn check_version_acceptance(
2051		&self,
2052		v3_enabled: bool,
2053	) -> Result<(), CandidateDescriptorVersionCheckError> {
2054		let version = self.version();
2055
2056		// Version consistency: old and new detection must agree, unless this is the
2057		// expected V3 disagreement (old rules → V1, new rules → V3) with V3 enabled.
2058		let is_expected_v3_disagreement = version == CandidateDescriptorVersion::V3 && v3_enabled;
2059		if !self.check_version_consistency() && !is_expected_v3_disagreement {
2060			return Err(CandidateDescriptorVersionCheckError::Inconsistency);
2061		}
2062
2063		// V3 gating: reject V3 candidates before the feature is enabled.
2064		if version == CandidateDescriptorVersion::V3 && !v3_enabled {
2065			return Err(CandidateDescriptorVersionCheckError::V3NotEnabled);
2066		}
2067
2068		Ok(())
2069	}
2070
2071	fn v2_version(&self) -> CandidateDescriptorVersion {
2072		// V1 detected using the pre-v3 (stricter) check: all reserved and new
2073		// fields must be zero. Once v3 is enabled, the v1 check is relaxed in
2074		// `v3_version()` to free up more bytes for future use.
2075		let old_v1_detected = self.reserved2 != [0u8; 32] ||
2076			self.reserved1 != [0u8; 24] ||
2077			self.scheduling_session_offset != 0 ||
2078			self.scheduling_parent.as_ref() != &[0u8; 32];
2079
2080		if old_v1_detected {
2081			return CandidateDescriptorVersion::V1;
2082		}
2083
2084		match self.version {
2085			0 => CandidateDescriptorVersion::V2,
2086			_ => CandidateDescriptorVersion::Unknown,
2087		}
2088	}
2089}
2090
2091impl<H> CandidateDescriptorV2<H> {
2092	fn v3_version(&self) -> CandidateDescriptorVersion {
2093		// Reduce checked bits for v1 significantly to make more bytes easier
2094		// usable in future upgrades. 16 bytes is 32 hexadecimal digits which
2095		// must all be 0 by accident to cause any issues. Bitcoin hardest
2096		// difficulty so far has been 24 digits/12 bytes
2097		//
2098		// Impact if it still happened would also be fairly minimal: We would
2099		// drop a parachain block, which is not a big deal on v1, where we are
2100		// not aiming for perfect block confidence.
2101		let new_v1_detected = self.reserved1[0..16] != [0u8; 16];
2102
2103		if new_v1_detected {
2104			return CandidateDescriptorVersion::V1;
2105		}
2106		match self.version {
2107			0 => CandidateDescriptorVersion::V2,
2108			1 => CandidateDescriptorVersion::V3,
2109			_ => CandidateDescriptorVersion::Unknown,
2110		}
2111	}
2112}
2113
2114macro_rules! impl_getter {
2115	($field:ident, $type:ident) => {
2116		/// Returns the value of `$field` field.
2117		pub fn $field(&self) -> $type {
2118			self.$field
2119		}
2120	};
2121}
2122
2123impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2124	impl_getter!(erasure_root, Hash);
2125	impl_getter!(para_head, Hash);
2126	impl_getter!(relay_parent, H);
2127	impl_getter!(para_id, ParaId);
2128	impl_getter!(persisted_validation_data_hash, Hash);
2129	impl_getter!(pov_hash, Hash);
2130	impl_getter!(validation_code_hash, ValidationCodeHash);
2131
2132	#[cfg(feature = "test")]
2133	fn rebuild_collator_field(&self) -> CollatorId {
2134		let mut collator_id = Vec::with_capacity(32);
2135		let core_index: [u8; 2] = self.core_index.to_ne_bytes();
2136		let session_index: [u8; 4] = self.session_index.to_ne_bytes();
2137
2138		collator_id.push(self.version);
2139		collator_id.extend_from_slice(core_index.as_slice());
2140		collator_id.extend_from_slice(session_index.as_slice());
2141		collator_id.push(self.scheduling_session_offset);
2142		collator_id.extend_from_slice(self.reserved1.as_slice());
2143
2144		CollatorId::from_slice(&collator_id.as_slice())
2145			.expect("Slice size is exactly 32 bytes; qed")
2146	}
2147
2148	/// Returns the collator id if this is a v1 `CandidateDescriptor`
2149	#[cfg(feature = "test")]
2150	pub fn collator(&self) -> Option<CollatorId> {
2151		if self.version() == CandidateDescriptorVersion::V1 {
2152			Some(self.rebuild_collator_field())
2153		} else {
2154			None
2155		}
2156	}
2157
2158	#[cfg(feature = "test")]
2159	fn rebuild_signature_field(&self) -> CollatorSignature {
2160		let mut signature_bytes = Vec::with_capacity(64);
2161		signature_bytes.extend_from_slice(self.scheduling_parent.as_ref());
2162		signature_bytes.extend_from_slice(self.reserved2.as_slice());
2163
2164		CollatorSignature::from_slice(&signature_bytes)
2165			.expect("Slice size is exactly 64 bytes; qed")
2166	}
2167
2168	#[cfg(feature = "test")]
2169	#[doc(hidden)]
2170	pub fn rebuild_collator_field_for_tests(&self) -> CollatorId {
2171		self.rebuild_collator_field()
2172	}
2173
2174	#[cfg(feature = "test")]
2175	#[doc(hidden)]
2176	pub fn rebuild_signature_field_for_tests(&self) -> CollatorSignature {
2177		self.rebuild_signature_field()
2178	}
2179
2180	/// Returns the collator signature of `V1` candidate descriptors, `None` otherwise.
2181	#[cfg(feature = "test")]
2182	pub fn signature(&self) -> Option<CollatorSignature> {
2183		if self.version() == CandidateDescriptorVersion::V1 {
2184			return Some(self.rebuild_signature_field());
2185		}
2186
2187		None
2188	}
2189
2190	/// Returns the `core_index` of `V2` and `V3` candidate descriptors, `None` for `V1`.
2191	pub fn core_index(&self) -> Option<CoreIndex> {
2192		if self.version() == CandidateDescriptorVersion::V1 {
2193			return None;
2194		}
2195
2196		Some(CoreIndex(self.core_index as u32))
2197	}
2198
2199	/// Returns the `session_index` of `V2` and `V3` candidate descriptors, `None` for `V1`.
2200	pub fn session_index(&self) -> Option<SessionIndex> {
2201		if self.version() == CandidateDescriptorVersion::V1 {
2202			return None;
2203		}
2204
2205		Some(self.session_index)
2206	}
2207
2208	/// Return the scheduling parent of the descriptor.
2209	///
2210	///
2211	/// On v1 and v2 this function will return the relay parent as under these versions the relay
2212	/// parent is also the scheduling parent.
2213	pub fn scheduling_parent(&self) -> H {
2214		match self.version() {
2215			CandidateDescriptorVersion::V1 => self.relay_parent,
2216			CandidateDescriptorVersion::V2 => self.relay_parent,
2217			CandidateDescriptorVersion::V3 => self.scheduling_parent,
2218			CandidateDescriptorVersion::Unknown => self.relay_parent,
2219		}
2220	}
2221
2222	/// Return the scheduling session index of the descriptor.
2223	///
2224	///
2225	/// On v1: Return None.
2226	/// On v2: Return the session index as it equals the scheduling session on v2.
2227	/// On v3: Return the provided scheduling session index.
2228	pub fn scheduling_session(&self) -> Option<SessionIndex> {
2229		match self.version() {
2230			CandidateDescriptorVersion::V1 => None,
2231			CandidateDescriptorVersion::V2 => Some(self.session_index),
2232			CandidateDescriptorVersion::V3 => {
2233				Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2234			},
2235			CandidateDescriptorVersion::Unknown => None,
2236		}
2237	}
2238
2239	/// Version for use in candidate validation during the V3 transition period.
2240	///
2241	/// Before the `CandidateReceiptV3` node feature is observed, uses
2242	/// [`Self::version_old_rules`] to match old backer behavior. After the feature
2243	/// is seen, trusts [`Self::version`].
2244	///
2245	/// This prevents slashing honest old backers when a malicious collator crafts
2246	/// a pseudo-V3 descriptor that old nodes interpret as V1 but new nodes would
2247	/// interpret as V3 (different PVF inputs → dispute → 100% slash).
2248	///
2249	/// Safety argument: The node feature can only be enabled well after the runtime upgrade that
2250	/// adds `check_version_acceptance()` protection at inclusion time. Once the feature is seen,
2251	/// the runtime has long been upgraded and already rejecting pseudo-V3 candidates (candidates
2252	/// that are valid v1 under the old rules, but are v3 without UMP signals under the new
2253	/// rules), so no ambiguous candidates can exist on-chain.
2254	///
2255	/// Only needed during the V3 transition. Once V3 is universally deployed,
2256	/// callers can switch to [`Self::version`] directly.
2257	pub fn version_for_candidate_validation(
2258		&self,
2259		v3_ever_seen: bool,
2260	) -> CandidateDescriptorVersion {
2261		if v3_ever_seen {
2262			self.version()
2263		} else {
2264			self.version_old_rules()
2265		}
2266	}
2267
2268	/// Scheduling parent for use in candidate validation.
2269	///
2270	/// See [`Self::version_for_candidate_validation`] for the safety argument.
2271	pub fn scheduling_parent_for_candidate_validation(&self, v3_ever_seen: bool) -> H
2272	where
2273		H: Copy,
2274	{
2275		match self.version_for_candidate_validation(v3_ever_seen) {
2276			CandidateDescriptorVersion::V3 => self.scheduling_parent,
2277			_ => self.relay_parent,
2278		}
2279	}
2280
2281	/// Scheduling session for candidate validation.
2282	///
2283	/// See [`Self::version_for_candidate_validation`] for the safety argument.
2284	pub fn scheduling_session_for_candidate_validation(
2285		&self,
2286		v3_ever_seen: bool,
2287	) -> Option<SessionIndex> {
2288		match self.version_for_candidate_validation(v3_ever_seen) {
2289			CandidateDescriptorVersion::V1 => None,
2290			CandidateDescriptorVersion::V2 => Some(self.session_index),
2291			CandidateDescriptorVersion::V3 => {
2292				Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2293			},
2294			CandidateDescriptorVersion::Unknown => None,
2295		}
2296	}
2297
2298	/// Session index (relay parent session) for candidate validation.
2299	///
2300	/// See [`Self::version_for_candidate_validation`] for the safety argument.
2301	pub fn session_index_for_candidate_validation(
2302		&self,
2303		v3_ever_seen: bool,
2304	) -> Option<SessionIndex> {
2305		match self.version_for_candidate_validation(v3_ever_seen) {
2306			CandidateDescriptorVersion::V1 | CandidateDescriptorVersion::Unknown => None,
2307			CandidateDescriptorVersion::V2 | CandidateDescriptorVersion::V3 => {
2308				Some(self.session_index)
2309			},
2310		}
2311	}
2312}
2313
2314impl<H> core::fmt::Debug for CandidateDescriptorV2<H>
2315where
2316	H: core::fmt::Debug,
2317{
2318	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2319		// A bit imprecise, but should not matter in practice for debug output. (Keeps trait bounds
2320		// sane.)
2321		match self.v3_version() {
2322			CandidateDescriptorVersion::V1 => f
2323				.debug_struct("CandidateDescriptorV1")
2324				.field("para_id", &self.para_id)
2325				.field("relay_parent", &self.relay_parent)
2326				.field("persisted_validation_hash", &self.persisted_validation_data_hash)
2327				.field("pov_hash", &self.pov_hash)
2328				.field("erasure_root", &self.erasure_root)
2329				.field("para_head", &self.para_head)
2330				.field("validation_code_hash", &self.validation_code_hash)
2331				.finish(),
2332			CandidateDescriptorVersion::V2 => f
2333				.debug_struct("CandidateDescriptorV2")
2334				.field("para_id", &self.para_id)
2335				.field("relay_parent", &self.relay_parent)
2336				.field("core_index", &self.core_index)
2337				.field("session_index", &self.session_index)
2338				.field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2339				.field("pov_hash", &self.pov_hash)
2340				.field("erasure_root", &self.erasure_root)
2341				.field("para_head", &self.para_head)
2342				.field("validation_code_hash", &self.validation_code_hash)
2343				.finish(),
2344			CandidateDescriptorVersion::V3 => f
2345				.debug_struct("CandidateDescriptorV3")
2346				.field("para_id", &self.para_id)
2347				.field("relay_parent", &self.relay_parent)
2348				.field("core_index", &self.core_index)
2349				.field("session_index", &self.session_index)
2350				.field("scheduling_session_offset", &self.scheduling_session_offset)
2351				.field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2352				.field("pov_hash", &self.pov_hash)
2353				.field("erasure_root", &self.erasure_root)
2354				.field("scheduling_parent", &self.scheduling_parent)
2355				.field("para_head", &self.para_head)
2356				.field("validation_code_hash", &self.validation_code_hash)
2357				.finish(),
2358			CandidateDescriptorVersion::Unknown => {
2359				write!(f, "CandidateDescriptorV2(unknown version={})", self.version)
2360			},
2361		}
2362	}
2363}
2364
2365impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2366	/// Constructor for V2 candidate descriptor (scheduling_parent = zero).
2367	pub fn new(
2368		para_id: Id,
2369		relay_parent: H,
2370		core_index: CoreIndex,
2371		session_index: SessionIndex,
2372		persisted_validation_data_hash: Hash,
2373		pov_hash: Hash,
2374		erasure_root: Hash,
2375		para_head: Hash,
2376		validation_code_hash: ValidationCodeHash,
2377	) -> Self
2378	where
2379		H: Default,
2380	{
2381		Self {
2382			para_id,
2383			relay_parent,
2384			version: 0,
2385			core_index: core_index.0 as u16,
2386			session_index,
2387			scheduling_session_offset: 0,
2388			reserved1: [0; 24],
2389			persisted_validation_data_hash,
2390			pov_hash,
2391			erasure_root,
2392			scheduling_parent: H::default(),
2393			reserved2: [0; 32],
2394			para_head,
2395			validation_code_hash,
2396		}
2397	}
2398
2399	/// Constructor for V3 candidate descriptor with explicit scheduling_parent.
2400	///
2401	/// V3 descriptors are identified by `version == 1` and have a non-zero scheduling_parent
2402	/// field, which indicates the relay chain block that was used for scheduling (may differ
2403	/// from relay_parent). V3 descriptors require UMP signals to be present.
2404	pub fn new_v3(
2405		para_id: Id,
2406		relay_parent: H,
2407		core_index: CoreIndex,
2408		session_index: SessionIndex,
2409		scheduling_session_index: SessionIndex,
2410		persisted_validation_data_hash: Hash,
2411		pov_hash: Hash,
2412		erasure_root: Hash,
2413		para_head: Hash,
2414		validation_code_hash: ValidationCodeHash,
2415		scheduling_parent: H,
2416	) -> Self {
2417		Self {
2418			para_id,
2419			relay_parent,
2420			version: 1,
2421			core_index: core_index.0 as u16,
2422			session_index,
2423			scheduling_session_offset: scheduling_session_index
2424				.saturating_sub(session_index)
2425				.try_into()
2426				.expect("scheduling session offset should fit in u8"),
2427			reserved1: [0; 24],
2428			persisted_validation_data_hash,
2429			pov_hash,
2430			erasure_root,
2431			scheduling_parent,
2432			reserved2: [0; 32],
2433			para_head,
2434			validation_code_hash,
2435		}
2436	}
2437
2438	/// Constructor for a V1-like candidate descriptor with non-zero collator
2439	/// fields so that `version()` returns [`CandidateDescriptorVersion::V1`].
2440	pub fn new_v1(
2441		para_id: Id,
2442		relay_parent: H,
2443		persisted_validation_data_hash: Hash,
2444		pov_hash: Hash,
2445		erasure_root: Hash,
2446		para_head: Hash,
2447		validation_code_hash: ValidationCodeHash,
2448	) -> Self
2449	where
2450		H: Default,
2451	{
2452		Self {
2453			para_id,
2454			relay_parent,
2455			version: 0,
2456			core_index: 0,
2457			session_index: 0,
2458			scheduling_session_offset: 0,
2459			reserved1: [1u8; 24],
2460			persisted_validation_data_hash,
2461			pov_hash,
2462			erasure_root,
2463			scheduling_parent: H::default(),
2464			reserved2: [1u8; 32],
2465			para_head,
2466			validation_code_hash,
2467		}
2468	}
2469
2470	#[cfg(feature = "test")]
2471	#[doc(hidden)]
2472	pub fn new_from_raw(
2473		para_id: Id,
2474		relay_parent: H,
2475		version: u8,
2476		core_index: u16,
2477		session_index: SessionIndex,
2478		scheduling_session_offset: u8,
2479		reserved1: [u8; 24],
2480		persisted_validation_data_hash: Hash,
2481		pov_hash: Hash,
2482		erasure_root: Hash,
2483		scheduling_parent: H,
2484		reserved2: [u8; 32],
2485		para_head: Hash,
2486		validation_code_hash: ValidationCodeHash,
2487	) -> Self {
2488		Self {
2489			para_id,
2490			relay_parent,
2491			version,
2492			core_index,
2493			session_index,
2494			scheduling_session_offset,
2495			reserved1,
2496			persisted_validation_data_hash,
2497			pov_hash,
2498			erasure_root,
2499			scheduling_parent,
2500			reserved2,
2501			para_head,
2502			validation_code_hash,
2503		}
2504	}
2505}
2506
2507/// A trait to allow changing the descriptor field values in tests.
2508#[cfg(feature = "test")]
2509pub trait MutateDescriptorV2<H> {
2510	/// Set the relay parent of the descriptor.
2511	fn set_relay_parent(&mut self, relay_parent: H);
2512	/// Set the `ParaId` of the descriptor.
2513	fn set_para_id(&mut self, para_id: Id);
2514	/// Set the PoV hash of the descriptor.
2515	fn set_pov_hash(&mut self, pov_hash: Hash);
2516	/// Set the raw version field of the descriptor.
2517	fn set_version(&mut self, version: u8);
2518	/// Set the PVD of the descriptor.
2519	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash);
2520	/// Set the validation code hash of the descriptor.
2521	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash);
2522	/// Set the erasure root of the descriptor.
2523	fn set_erasure_root(&mut self, erasure_root: Hash);
2524	/// Set the para head of the descriptor.
2525	fn set_para_head(&mut self, para_head: Hash);
2526	/// Set the core index of the descriptor.
2527	fn set_core_index(&mut self, core_index: CoreIndex);
2528	/// Set the session index of the descriptor.
2529	fn set_session_index(&mut self, session_index: SessionIndex);
2530	/// Set the reserved2 field of the descriptor.
2531	fn set_reserved2(&mut self, reserved2: [u8; 32]);
2532	/// Set the scheduling parent of the descriptor.
2533	fn set_scheduling_parent(&mut self, scheduling_parent: H);
2534	/// Set the scheduling session offset of the descriptor.
2535	fn set_scheduling_session_offset(&mut self, offset: u8);
2536}
2537
2538#[cfg(feature = "test")]
2539impl<H> MutateDescriptorV2<H> for CandidateDescriptorV2<H> {
2540	fn set_para_id(&mut self, para_id: Id) {
2541		self.para_id = para_id;
2542	}
2543
2544	fn set_relay_parent(&mut self, relay_parent: H) {
2545		self.relay_parent = relay_parent;
2546	}
2547
2548	fn set_pov_hash(&mut self, pov_hash: Hash) {
2549		self.pov_hash = pov_hash;
2550	}
2551
2552	fn set_version(&mut self, version: u8) {
2553		self.version = version;
2554	}
2555
2556	fn set_core_index(&mut self, core_index: CoreIndex) {
2557		self.core_index = core_index.0 as u16;
2558	}
2559
2560	fn set_session_index(&mut self, session_index: SessionIndex) {
2561		self.session_index = session_index;
2562	}
2563
2564	fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) {
2565		self.persisted_validation_data_hash = persisted_validation_data_hash;
2566	}
2567
2568	fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) {
2569		self.validation_code_hash = validation_code_hash;
2570	}
2571
2572	fn set_erasure_root(&mut self, erasure_root: Hash) {
2573		self.erasure_root = erasure_root;
2574	}
2575
2576	fn set_para_head(&mut self, para_head: Hash) {
2577		self.para_head = para_head;
2578	}
2579
2580	fn set_reserved2(&mut self, reserved2: [u8; 32]) {
2581		self.reserved2 = reserved2;
2582	}
2583
2584	fn set_scheduling_parent(&mut self, scheduling_parent: H) {
2585		self.scheduling_parent = scheduling_parent;
2586	}
2587
2588	fn set_scheduling_session_offset(&mut self, offset: u8) {
2589		self.scheduling_session_offset = offset;
2590	}
2591}
2592
2593/// A candidate-receipt at version 2.
2594#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2595pub struct CandidateReceiptV2<H = Hash> {
2596	/// The descriptor of the candidate.
2597	pub descriptor: CandidateDescriptorV2<H>,
2598	/// The hash of the encoded commitments made as a result of candidate execution.
2599	pub commitments_hash: Hash,
2600}
2601
2602/// A candidate-receipt with commitments directly included.
2603#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2604pub struct CommittedCandidateReceiptV2<H = Hash> {
2605	/// The descriptor of the candidate.
2606	pub descriptor: CandidateDescriptorV2<H>,
2607	/// The commitments of the candidate receipt.
2608	pub commitments: CandidateCommitments,
2609}
2610
2611/// An event concerning a candidate.
2612#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
2613#[cfg_attr(feature = "std", derive(PartialEq))]
2614pub enum CandidateEvent<H = Hash> {
2615	/// This candidate receipt was backed in the most recent block.
2616	/// This includes the core index the candidate is now occupying.
2617	#[codec(index = 0)]
2618	CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2619	/// This candidate receipt was included and became a parablock at the most recent block.
2620	/// This includes the core index the candidate was occupying as well as the group responsible
2621	/// for backing the candidate.
2622	#[codec(index = 1)]
2623	CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2624	/// This candidate receipt was not made available in time and timed out.
2625	/// This includes the core index the candidate was occupying.
2626	#[codec(index = 2)]
2627	CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
2628}
2629
2630impl<H> CandidateReceiptV2<H> {
2631	/// Get a reference to the candidate descriptor.
2632	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
2633		&self.descriptor
2634	}
2635
2636	/// Computes the blake2-256 hash of the receipt.
2637	pub fn hash(&self) -> CandidateHash
2638	where
2639		H: Encode,
2640	{
2641		CandidateHash(BlakeTwo256::hash_of(self))
2642	}
2643}
2644
2645impl<H: Clone> CommittedCandidateReceiptV2<H> {
2646	/// Transforms this into a plain `CandidateReceipt`.
2647	pub fn to_plain(&self) -> CandidateReceiptV2<H> {
2648		CandidateReceiptV2 {
2649			descriptor: self.descriptor.clone(),
2650			commitments_hash: self.commitments.hash(),
2651		}
2652	}
2653
2654	/// Computes the hash of the committed candidate receipt.
2655	///
2656	/// This computes the canonical hash, not the hash of the directly encoded data.
2657	/// Thus this is a shortcut for `candidate.to_plain().hash()`.
2658	pub fn hash(&self) -> CandidateHash
2659	where
2660		H: Encode,
2661	{
2662		self.to_plain().hash()
2663	}
2664
2665	/// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]?
2666	pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
2667	where
2668		H: PartialEq,
2669	{
2670		receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
2671	}
2672}
2673
2674impl PartialOrd for CommittedCandidateReceiptV2 {
2675	fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
2676		Some(self.cmp(other))
2677	}
2678}
2679
2680impl Ord for CommittedCandidateReceiptV2 {
2681	fn cmp(&self, other: &Self) -> core::cmp::Ordering {
2682		self.descriptor
2683			.para_id
2684			.cmp(&other.descriptor.para_id)
2685			.then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
2686	}
2687}
2688
2689/// A strictly increasing sequence number, typically this would be the least significant byte of the
2690/// block number.
2691#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2692pub struct CoreSelector(pub u8);
2693
2694impl From<u8> for CoreSelector {
2695	fn from(value: u8) -> Self {
2696		Self(value)
2697	}
2698}
2699
2700/// An offset in the relay chain claim queue.
2701#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2702pub struct ClaimQueueOffset(pub u8);
2703
2704impl From<u8> for ClaimQueueOffset {
2705	fn from(value: u8) -> Self {
2706		Self(value)
2707	}
2708}
2709
2710/// Signals that a parachain can send to the relay chain via the UMP queue.
2711#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2712pub enum UMPSignal {
2713	/// A message sent by a parachain to select the core the candidate is committed to.
2714	/// Relay chain validators, in particular backers, use the `CoreSelector` and
2715	/// `ClaimQueueOffset` to compute the index of the core the candidate has committed to.
2716	SelectCore(CoreSelector, ClaimQueueOffset),
2717	/// A message sent by a parachain to promote the reputation of a given peerid.
2718	ApprovedPeer(ApprovedPeerId),
2719}
2720
2721/// The default claim queue offset to be used if it's not configured/accessible in the parachain
2722/// runtime
2723pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0;
2724
2725/// Approved PeerId type. PeerIds in polkadot should typically be 32 bytes long but for identity
2726/// multihash can go up to 64. Cannot reuse the PeerId type definition from the networking code as
2727/// it's too generic and extensible.
2728pub type ApprovedPeerId = BoundedVec<u8, ConstU32<64>>;
2729
2730#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Default)]
2731/// User-friendly representation of a candidate's UMP signals.
2732pub struct CandidateUMPSignals {
2733	pub(super) select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2734	pub(super) approved_peer: Option<ApprovedPeerId>,
2735}
2736
2737impl CandidateUMPSignals {
2738	/// Get the core selector UMP signal.
2739	pub fn core_selector(&self) -> Option<(CoreSelector, ClaimQueueOffset)> {
2740		self.select_core
2741	}
2742
2743	/// Get a reference to the approved peer UMP signal.
2744	pub fn approved_peer(&self) -> Option<&ApprovedPeerId> {
2745		self.approved_peer.as_ref()
2746	}
2747
2748	/// Returns `true` if UMP signals are empty.
2749	pub fn is_empty(&self) -> bool {
2750		self.select_core.is_none() && self.approved_peer.is_none()
2751	}
2752
2753	fn try_decode_signal(
2754		&mut self,
2755		buffer: &mut impl codec::Input,
2756	) -> Result<(), CommittedCandidateReceiptError> {
2757		match UMPSignal::decode(buffer)
2758			.map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)?
2759		{
2760			UMPSignal::ApprovedPeer(approved_peer_id) if self.approved_peer.is_none() => {
2761				self.approved_peer = Some(approved_peer_id);
2762			},
2763			UMPSignal::SelectCore(core_selector, cq_offset) if self.select_core.is_none() => {
2764				self.select_core = Some((core_selector, cq_offset));
2765			},
2766			_ => {
2767				// This means that we got duplicate UMP signals.
2768				return Err(CommittedCandidateReceiptError::DuplicateUMPSignal);
2769			},
2770		};
2771
2772		Ok(())
2773	}
2774
2775	#[cfg(feature = "test")]
2776	#[doc(hidden)]
2777	pub fn dummy(
2778		select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2779		approved_peer: Option<ApprovedPeerId>,
2780	) -> Self {
2781		Self { select_core, approved_peer }
2782	}
2783}
2784
2785/// Separator between `XCM` and `UMPSignal`.
2786pub const UMP_SEPARATOR: Vec<u8> = vec![];
2787
2788/// Utility function for skipping the ump signals.
2789pub fn skip_ump_signals<'a>(
2790	upward_messages: impl Iterator<Item = &'a Vec<u8>>,
2791) -> impl Iterator<Item = &'a Vec<u8>> {
2792	upward_messages.take_while(|message| *message != &UMP_SEPARATOR)
2793}
2794
2795impl CandidateCommitments {
2796	/// Returns the ump signals of this candidate, if any, or an error if they violate the expected
2797	/// format.
2798	pub fn ump_signals(&self) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2799		let mut res = CandidateUMPSignals::default();
2800
2801		let mut signals_iter =
2802			self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR);
2803
2804		if signals_iter.next().is_none() {
2805			// No UMP separator
2806			return Ok(res);
2807		}
2808
2809		// Process first signal
2810		let Some(first_signal) = signals_iter.next() else { return Ok(res) };
2811		res.try_decode_signal(&mut first_signal.as_slice())?;
2812
2813		// Process second signal
2814		let Some(second_signal) = signals_iter.next() else { return Ok(res) };
2815		res.try_decode_signal(&mut second_signal.as_slice())?;
2816
2817		// At most two signals are allowed
2818		if signals_iter.next().is_some() {
2819			return Err(CommittedCandidateReceiptError::TooManyUMPSignals);
2820		}
2821
2822		Ok(res)
2823	}
2824}
2825
2826/// CommittedCandidateReceiptError construction errors.
2827#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2828#[cfg_attr(feature = "std", derive(thiserror::Error))]
2829pub enum CommittedCandidateReceiptError {
2830	/// The specified core index is invalid.
2831	#[cfg_attr(feature = "std", error("The specified core index is invalid"))]
2832	InvalidCoreIndex,
2833	/// The core index in commitments doesn't match the one in descriptor
2834	#[cfg_attr(
2835		feature = "std",
2836		error("The core index in commitments ({commitments:?}) doesn't match the one in descriptor ({descriptor:?})")
2837	)]
2838	CoreIndexMismatch {
2839		/// The core index as found in the descriptor.
2840		descriptor: CoreIndex,
2841		/// The core index as found in the commitments.
2842		commitments: CoreIndex,
2843	},
2844	/// The core selector or claim queue offset is invalid.
2845	#[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))]
2846	InvalidSelectedCore,
2847	#[cfg_attr(feature = "std", error("Could not decode UMP signal"))]
2848	/// Could not decode UMP signal.
2849	UmpSignalDecode,
2850	/// The parachain is not assigned to any core at specified claim queue offset.
2851	#[cfg_attr(
2852		feature = "std",
2853		error("The parachain is not assigned to any core at specified claim queue offset")
2854	)]
2855	NoAssignment,
2856	/// Unknown version.
2857	#[cfg_attr(feature = "std", error("Unknown internal version"))]
2858	UnknownVersion(u8),
2859	/// The allowed number of `UMPSignal` messages in the queue was exceeded.
2860	#[cfg_attr(feature = "std", error("Too many UMP signals"))]
2861	TooManyUMPSignals,
2862	/// Duplicated UMP signal.
2863	#[cfg_attr(feature = "std", error("Duplicate UMP signal"))]
2864	DuplicateUMPSignal,
2865	/// If the parachain runtime started sending ump signals, v1 descriptors are no longer
2866	/// allowed.
2867	#[cfg_attr(feature = "std", error("Version 1 receipt does not support ump signals"))]
2868	UMPSignalWithV1Descriptor,
2869	/// Starting with v3 ump signals are mandatory.
2870	///
2871	/// This is to avoid nodes only understanding v1 and v2 to getting tricked
2872	/// into backing a candidate that looks like a valid v1 to them, but is
2873	/// actually an invalid v3.
2874	///
2875	/// This is prevented by the runtime rejecting v3 candidates without ump
2876	/// signals. Therefore a candidate that was erroneously backed as v1, while
2877	/// it actually was a v3 would get rejected by the runtime due to missing
2878	/// signals, thus preventing the backer from getting slashed. This is given,
2879	/// because v1 and v2 only nodes would not back a v1 candidate with UMP
2880	/// signals, as that is seen as invalid by them already.
2881	#[cfg_attr(feature = "std", error("Version 3 receipt requires ump signals"))]
2882	NoUMPSignalWithV3Descriptor,
2883}
2884
2885impl<H: Copy + AsRef<[u8]>> CommittedCandidateReceiptV2<H> {
2886	/// Performs checks on the UMP signals and returns them.
2887	///
2888	/// Also checks if descriptor core index is equal to the committed core index.
2889	///
2890	/// Params:
2891	/// - `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as
2892	/// a mapping between `ParaId` and the cores assigned per depth.
2893	///
2894	/// NOTE: This must only be called in the runtime and backing - never in approval voting nor
2895	/// disputes! At least not as long as nodes exist which don't understand v3 candidate
2896	/// descriptors. Not checking there is fine, because it is checked by the runtime - if it can be
2897	/// disputed, it has been checked already!
2898	pub fn parse_ump_signals(
2899		&self,
2900		cores_per_para: &TransposedClaimQueue,
2901	) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2902		let signals = self.commitments.ump_signals()?;
2903
2904		match self.descriptor.version() {
2905			CandidateDescriptorVersion::V1 => {
2906				// If the parachain runtime started sending ump signals, v1 descriptors are no
2907				// longer allowed.
2908				if !signals.is_empty() {
2909					return Err(CommittedCandidateReceiptError::UMPSignalWithV1Descriptor);
2910				} else {
2911					// Nothing else to check for v1 descriptors.
2912					return Ok(CandidateUMPSignals::default());
2913				}
2914			},
2915			CandidateDescriptorVersion::V2 => {},
2916			CandidateDescriptorVersion::Unknown => {
2917				return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version))
2918			},
2919			_ if signals.is_empty() => {
2920				// V3 and above require UMP signals.
2921				return Err(CommittedCandidateReceiptError::NoUMPSignalWithV3Descriptor);
2922			},
2923			_ => {},
2924		}
2925
2926		// Check the core index
2927		let (maybe_core_index_selector, cq_offset) = signals
2928			.core_selector()
2929			.map(|(selector, offset)| (Some(selector), offset))
2930			.unwrap_or_else(|| (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)));
2931
2932		self.check_core_index(cores_per_para, maybe_core_index_selector, cq_offset)?;
2933
2934		// Nothing to further check for the approved peer. If everything passed so far, return the
2935		// signals.
2936		Ok(signals)
2937	}
2938
2939	/// Checks if descriptor core index is equal to the committed core index.
2940	/// Input `cores_per_para` is a claim queue snapshot at the candidate's relay parent, stored as
2941	/// a mapping between `ParaId` and the cores assigned per depth.
2942	fn check_core_index(
2943		&self,
2944		cores_per_para: &TransposedClaimQueue,
2945		maybe_core_index_selector: Option<CoreSelector>,
2946		cq_offset: ClaimQueueOffset,
2947	) -> Result<(), CommittedCandidateReceiptError> {
2948		let assigned_cores = cores_per_para
2949			.get(&self.descriptor.para_id())
2950			.ok_or(CommittedCandidateReceiptError::NoAssignment)?
2951			.get(&cq_offset.0)
2952			.ok_or(CommittedCandidateReceiptError::NoAssignment)?;
2953
2954		if assigned_cores.is_empty() {
2955			return Err(CommittedCandidateReceiptError::NoAssignment);
2956		}
2957
2958		let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
2959
2960		let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector {
2961			// We have a committed core selector, we can use it.
2962			core_index_selector
2963		} else if assigned_cores.len() > 1 {
2964			// We got more than one assigned core and no core selector. Special care is needed.
2965			if !assigned_cores.contains(&descriptor_core_index) {
2966				// core index in the descriptor is not assigned to the para. Error.
2967				return Err(CommittedCandidateReceiptError::InvalidCoreIndex);
2968			} else {
2969				// the descriptor core index is indeed assigned to the para. This is the most we can
2970				// check for now
2971				return Ok(());
2972			}
2973		} else {
2974			// No core selector but there's only one assigned core, use it.
2975			CoreSelector(0)
2976		};
2977
2978		let core_index = assigned_cores
2979			.iter()
2980			.nth(core_index_selector.0 as usize % assigned_cores.len())
2981			.ok_or(CommittedCandidateReceiptError::InvalidSelectedCore)
2982			.copied()?;
2983
2984		if core_index != descriptor_core_index {
2985			return Err(CommittedCandidateReceiptError::CoreIndexMismatch {
2986				descriptor: descriptor_core_index,
2987				commitments: core_index,
2988			});
2989		}
2990
2991		Ok(())
2992	}
2993}
2994
2995/// A backed (or backable, depending on context) candidate.
2996#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug, TypeInfo)]
2997pub struct BackedCandidate<H = Hash> {
2998	/// The candidate referred to.
2999	candidate: CommittedCandidateReceiptV2<H>,
3000	/// The validity votes themselves, expressed as signatures.
3001	validity_votes: Vec<ValidityAttestation>,
3002	/// The indices of the validators within the group, expressed as a bitfield. May be extended
3003	/// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is
3004	/// enabled.
3005	validator_indices: BitVec<u8, bitvec::order::Lsb0>,
3006}
3007
3008/// Parachains inherent-data passed into the runtime by a block author
3009#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
3010pub struct InherentData<HDR: HeaderT = Header> {
3011	/// Signed bitfields by validators about availability.
3012	pub bitfields: UncheckedSignedAvailabilityBitfields,
3013	/// Backed candidates for inclusion in the block.
3014	pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
3015	/// Sets of dispute votes for inclusion,
3016	pub disputes: MultiDisputeStatementSet,
3017	/// The parent block header. Used for checking state proofs.
3018	pub parent_header: HDR,
3019}
3020
3021impl<H> BackedCandidate<H> {
3022	/// Constructor
3023	pub fn new(
3024		candidate: CommittedCandidateReceiptV2<H>,
3025		validity_votes: Vec<ValidityAttestation>,
3026		validator_indices: BitVec<u8, bitvec::order::Lsb0>,
3027		core_index: CoreIndex,
3028	) -> Self {
3029		let mut instance = Self { candidate, validity_votes, validator_indices };
3030		instance.inject_core_index(core_index);
3031		instance
3032	}
3033
3034	/// Get a reference to the committed candidate receipt of the candidate.
3035	pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
3036		&self.candidate
3037	}
3038
3039	/// Get a mutable reference to the committed candidate receipt of the candidate.
3040	/// Only for testing.
3041	#[cfg(feature = "test")]
3042	pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2<H> {
3043		&mut self.candidate
3044	}
3045	/// Get a reference to the descriptor of the candidate.
3046	pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
3047		&self.candidate.descriptor
3048	}
3049
3050	/// Get a mutable reference to the descriptor of the candidate. Only for testing.
3051	#[cfg(feature = "test")]
3052	pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
3053		&mut self.candidate.descriptor
3054	}
3055
3056	/// Get a reference to the validity votes of the candidate.
3057	pub fn validity_votes(&self) -> &[ValidityAttestation] {
3058		&self.validity_votes
3059	}
3060
3061	/// Get a mutable reference to validity votes of the para.
3062	pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
3063		&mut self.validity_votes
3064	}
3065
3066	/// Compute this candidate's hash.
3067	pub fn hash(&self) -> CandidateHash
3068	where
3069		H: Clone + Encode,
3070	{
3071		self.candidate.to_plain().hash()
3072	}
3073
3074	/// Get this candidate's receipt.
3075	pub fn receipt(&self) -> CandidateReceiptV2<H>
3076	where
3077		H: Clone,
3078	{
3079		self.candidate.to_plain()
3080	}
3081
3082	/// Get a copy of the raw validator indices.
3083	#[cfg(feature = "test")]
3084	pub fn raw_validator_indices(&self) -> BitVec<u8, bitvec::order::Lsb0> {
3085		self.validator_indices.clone()
3086	}
3087
3088	/// Get a copy of the validator indices and the assumed core index, if any.
3089	pub fn validator_indices_and_core_index(
3090		&self,
3091	) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
3092		// `BackedCandidate::validity_indices` are extended to store a 8 bit core index.
3093		let core_idx_offset = self.validator_indices.len().saturating_sub(8);
3094		if core_idx_offset > 0 {
3095			let (validator_indices_slice, core_idx_slice) =
3096				self.validator_indices.split_at(core_idx_offset);
3097			return (validator_indices_slice, Some(CoreIndex(core_idx_slice.load::<u8>() as u32)));
3098		}
3099
3100		(&self.validator_indices, None)
3101	}
3102
3103	/// Inject a core index in the validator_indices bitvec.
3104	fn inject_core_index(&mut self, core_index: CoreIndex) {
3105		let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
3106			BitVec::from_vec(vec![core_index.0 as u8]);
3107		self.validator_indices.extend(core_index_to_inject);
3108	}
3109
3110	/// Update the validator indices and core index in the candidate.
3111	pub fn set_validator_indices_and_core_index(
3112		&mut self,
3113		new_indices: BitVec<u8, bitvec::order::Lsb0>,
3114		maybe_core_index: Option<CoreIndex>,
3115	) {
3116		self.validator_indices = new_indices;
3117
3118		if let Some(core_index) = maybe_core_index {
3119			self.inject_core_index(core_index);
3120		}
3121	}
3122}
3123
3124/// Scraped runtime backing votes and resolved disputes.
3125#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
3126#[cfg_attr(feature = "std", derive(PartialEq))]
3127pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
3128	/// The session in which the block was included.
3129	pub session: SessionIndex,
3130	/// Set of backing validators for each candidate, represented by its candidate
3131	/// receipt.
3132	pub backing_validators_per_candidate:
3133		Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
3134	/// On-chain-recorded set of disputes.
3135	/// Note that the above `backing_validators` are
3136	/// unrelated to the backers of the disputes candidates.
3137	pub disputes: MultiDisputeStatementSet,
3138}
3139
3140/// Information about a core which is currently occupied.
3141#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3142#[cfg_attr(feature = "std", derive(PartialEq))]
3143pub struct OccupiedCore<H = Hash, N = BlockNumber> {
3144	// NOTE: this has no ParaId as it can be deduced from the candidate descriptor.
3145	/// If this core is freed by availability, this is the assignment that is next up on this
3146	/// core, if any. None if there is nothing queued for this core.
3147	pub next_up_on_available: Option<ScheduledCore>,
3148	/// The relay-chain block number this began occupying the core at.
3149	pub occupied_since: N,
3150	/// The relay-chain block this will time-out at, if any.
3151	pub time_out_at: N,
3152	/// If this core is freed by being timed-out, this is the assignment that is next up on this
3153	/// core. None if there is nothing queued for this core or there is no possibility of timing
3154	/// out.
3155	pub next_up_on_time_out: Option<ScheduledCore>,
3156	/// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
3157	/// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
3158	/// this will be available.
3159	pub availability: BitVec<u8, bitvec::order::Lsb0>,
3160	/// The group assigned to distribute availability pieces of this candidate.
3161	pub group_responsible: GroupIndex,
3162	/// The hash of the candidate occupying the core.
3163	pub candidate_hash: CandidateHash,
3164	/// The descriptor of the candidate occupying the core.
3165	pub candidate_descriptor: CandidateDescriptorV2<H>,
3166}
3167
3168impl<H, N> OccupiedCore<H, N> {
3169	/// Get the Para currently occupying this core.
3170	pub fn para_id(&self) -> Id {
3171		self.candidate_descriptor.para_id
3172	}
3173}
3174
3175/// The state of a particular availability core.
3176#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3177#[cfg_attr(feature = "std", derive(PartialEq))]
3178pub enum CoreState<H = Hash, N = BlockNumber> {
3179	/// The core is currently occupied.
3180	#[codec(index = 0)]
3181	Occupied(OccupiedCore<H, N>),
3182	/// The core is currently free, with a para scheduled and given the opportunity
3183	/// to occupy.
3184	///
3185	/// If a particular Collator is required to author this block, that is also present in this
3186	/// variant.
3187	#[codec(index = 1)]
3188	Scheduled(ScheduledCore),
3189	/// The core is currently free and there is nothing scheduled. This can be the case for
3190	/// parathread cores when there are no parathread blocks queued. Parachain cores will never be
3191	/// left idle.
3192	#[codec(index = 2)]
3193	Free,
3194}
3195
3196impl<N> CoreState<N> {
3197	/// Returns the scheduled `ParaId` for the core or `None` if nothing is scheduled.
3198	///
3199	/// This function is deprecated. `ClaimQueue` should be used to obtain the scheduled `ParaId`s
3200	/// for each core.
3201	#[deprecated(
3202		note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead."
3203	)]
3204	pub fn para_id(&self) -> Option<Id> {
3205		match self {
3206			Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id),
3207			Self::Scheduled(core) => Some(core.para_id),
3208			Self::Free => None,
3209		}
3210	}
3211
3212	/// Is this core state `Self::Occupied`?
3213	pub fn is_occupied(&self) -> bool {
3214		matches!(self, Self::Occupied(_))
3215	}
3216}
3217
3218/// The claim queue mapped by parachain id.
3219pub type TransposedClaimQueue = BTreeMap<ParaId, BTreeMap<u8, BTreeSet<CoreIndex>>>;
3220
3221/// Returns a mapping between the para id and the core indices assigned at different
3222/// depths in the claim queue.
3223pub fn transpose_claim_queue(
3224	claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
3225) -> TransposedClaimQueue {
3226	let mut per_para_claim_queue = BTreeMap::new();
3227
3228	for (core, paras) in claim_queue {
3229		// Iterate paras assigned to this core at each depth.
3230		for (depth, para) in paras.into_iter().enumerate() {
3231			let depths: &mut BTreeMap<u8, BTreeSet<CoreIndex>> =
3232				per_para_claim_queue.entry(para).or_insert_with(|| Default::default());
3233
3234			depths.entry(depth as u8).or_default().insert(core);
3235		}
3236	}
3237
3238	per_para_claim_queue
3239}
3240
3241// Approval Slashes primitives
3242/// Supercedes the old 'SlashingOffenceKind' enum.
3243#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
3244pub enum DisputeOffenceKind {
3245	/// A severe offence when a validator backed an invalid block
3246	/// (backing only)
3247	#[codec(index = 0)]
3248	ForInvalidBacked,
3249	/// A minor offence when a validator disputed a valid block.
3250	/// (approval checking and dispute vote only)
3251	#[codec(index = 1)]
3252	AgainstValid,
3253	/// A medium offence when a validator approved an invalid block
3254	/// (approval checking and dispute vote only)
3255	#[codec(index = 2)]
3256	ForInvalidApproved,
3257}
3258
3259/// impl for a conversion from SlashingOffenceKind to DisputeOffenceKind
3260/// This creates DisputeOffenceKind that never contains ForInvalidApproved since it was not
3261/// supported in the past
3262impl From<super::v9::slashing::SlashingOffenceKind> for DisputeOffenceKind {
3263	fn from(value: super::v9::slashing::SlashingOffenceKind) -> Self {
3264		match value {
3265			super::v9::slashing::SlashingOffenceKind::ForInvalid => Self::ForInvalidBacked,
3266			super::v9::slashing::SlashingOffenceKind::AgainstValid => Self::AgainstValid,
3267		}
3268	}
3269}
3270
3271/// impl for a tryFrom conversion from DisputeOffenceKind to SlashingOffenceKind
3272impl TryFrom<DisputeOffenceKind> for super::v9::slashing::SlashingOffenceKind {
3273	type Error = ();
3274
3275	fn try_from(value: DisputeOffenceKind) -> Result<Self, Self::Error> {
3276		match value {
3277			DisputeOffenceKind::ForInvalidBacked => Ok(Self::ForInvalid),
3278			DisputeOffenceKind::AgainstValid => Ok(Self::AgainstValid),
3279			DisputeOffenceKind::ForInvalidApproved => Err(()),
3280		}
3281	}
3282}
3283
3284#[cfg(test)]
3285/// Basic tests
3286pub mod tests {
3287	use super::*;
3288
3289	#[test]
3290	fn group_rotation_info_calculations() {
3291		let info =
3292			GroupRotationInfo { session_start_block: 10u32, now: 15, group_rotation_frequency: 5 };
3293
3294		assert_eq!(info.next_rotation_at(), 20);
3295		assert_eq!(info.last_rotation_at(), 15);
3296	}
3297
3298	#[test]
3299	fn group_for_core_is_core_for_group() {
3300		for cores in 1..=256 {
3301			for rotations in 0..(cores * 2) {
3302				let info = GroupRotationInfo {
3303					session_start_block: 0u32,
3304					now: rotations,
3305					group_rotation_frequency: 1,
3306				};
3307
3308				for core in 0..cores {
3309					let group = info.group_for_core(CoreIndex(core), cores as usize);
3310					assert_eq!(info.core_for_group(group, cores as usize).0, core);
3311				}
3312			}
3313		}
3314	}
3315
3316	#[test]
3317	fn test_byzantine_threshold() {
3318		assert_eq!(byzantine_threshold(0), 0);
3319		assert_eq!(byzantine_threshold(1), 0);
3320		assert_eq!(byzantine_threshold(2), 0);
3321		assert_eq!(byzantine_threshold(3), 0);
3322		assert_eq!(byzantine_threshold(4), 1);
3323		assert_eq!(byzantine_threshold(5), 1);
3324		assert_eq!(byzantine_threshold(6), 1);
3325		assert_eq!(byzantine_threshold(7), 2);
3326	}
3327
3328	#[test]
3329	fn test_supermajority_threshold() {
3330		assert_eq!(supermajority_threshold(0), 0);
3331		assert_eq!(supermajority_threshold(1), 1);
3332		assert_eq!(supermajority_threshold(2), 2);
3333		assert_eq!(supermajority_threshold(3), 3);
3334		assert_eq!(supermajority_threshold(4), 3);
3335		assert_eq!(supermajority_threshold(5), 4);
3336		assert_eq!(supermajority_threshold(6), 5);
3337		assert_eq!(supermajority_threshold(7), 5);
3338	}
3339
3340	#[test]
3341	fn balance_bigger_than_usize() {
3342		let zero_b: Balance = 0;
3343		let zero_u: usize = 0;
3344
3345		assert!(zero_b.leading_zeros() >= zero_u.leading_zeros());
3346	}
3347
3348	fn make_v2_descriptor() -> CandidateDescriptorV2 {
3349		CandidateDescriptorV2::new(
3350			Id::from(1u32),
3351			Hash::repeat_byte(1),
3352			CoreIndex(0),
3353			1,
3354			Hash::repeat_byte(2),
3355			Hash::repeat_byte(3),
3356			Hash::repeat_byte(4),
3357			Hash::repeat_byte(5),
3358			ValidationCodeHash::from(Hash::repeat_byte(6)),
3359		)
3360	}
3361
3362	fn make_v3_descriptor() -> CandidateDescriptorV2 {
3363		CandidateDescriptorV2::new_v3(
3364			Id::from(1u32),
3365			Hash::repeat_byte(1),
3366			CoreIndex(0),
3367			1, // session_index
3368			1, // scheduling_session_index
3369			Hash::repeat_byte(2),
3370			Hash::repeat_byte(3),
3371			Hash::repeat_byte(4),
3372			Hash::repeat_byte(5),
3373			ValidationCodeHash::from(Hash::repeat_byte(6)),
3374			Hash::repeat_byte(7), // scheduling_parent
3375		)
3376	}
3377
3378	#[test]
3379	fn check_version_acceptance_v1_consistent() {
3380		// A V1 descriptor (created from old-style with non-zero collator fields)
3381		// Both old and new rules agree → passes regardless of v3_enabled.
3382		let mut desc = make_v2_descriptor();
3383		// Put non-zero bytes in first 16 bytes of reserved1 to trigger V1 in both
3384		// old and new detection.
3385		desc.reserved1[0] = 0xFF;
3386
3387		assert_eq!(desc.version(), CandidateDescriptorVersion::V1);
3388		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3389		assert!(desc.check_version_consistency());
3390
3391		assert!(desc.check_version_acceptance(false).is_ok());
3392		assert!(desc.check_version_acceptance(true).is_ok());
3393	}
3394
3395	#[test]
3396	fn check_version_acceptance_v2_consistent() {
3397		// A clean V2 descriptor: both rules agree → passes always.
3398		let desc = make_v2_descriptor();
3399
3400		assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3401		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V2);
3402		assert!(desc.check_version_consistency());
3403
3404		assert!(desc.check_version_acceptance(false).is_ok());
3405		assert!(desc.check_version_acceptance(true).is_ok());
3406	}
3407
3408	#[test]
3409	fn check_version_acceptance_v3_when_enabled() {
3410		// V3 descriptor with v3_enabled=true → passes.
3411		let desc = make_v3_descriptor();
3412
3413		assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3414		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3415		assert!(!desc.check_version_consistency());
3416
3417		assert!(desc.check_version_acceptance(true).is_ok());
3418	}
3419
3420	#[test]
3421	fn check_version_acceptance_v3_when_disabled() {
3422		// V3 descriptor with v3_enabled=false → rejected.
3423		// The consistency check fires first (old rules see V1, new rules see V3,
3424		// and V3 disagreement is not expected when v3_enabled=false).
3425		let desc = make_v3_descriptor();
3426
3427		assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3428		assert_eq!(
3429			desc.check_version_acceptance(false),
3430			Err(CandidateDescriptorVersionCheckError::Inconsistency)
3431		);
3432	}
3433
3434	#[test]
3435	fn check_version_acceptance_ambiguous_rejected() {
3436		// Craft descriptor where old rules see V1, new rules see V2.
3437		// reserved1[16..24] non-zero, reserved1[0..16] all zero, version=0.
3438		let mut desc = make_v2_descriptor();
3439		desc.reserved1[16] = 0xFF; // triggers old V1 check but not new
3440
3441		assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3442		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3443		assert!(!desc.check_version_consistency());
3444
3445		// Rejected regardless of v3_enabled.
3446		assert_eq!(
3447			desc.check_version_acceptance(false),
3448			Err(CandidateDescriptorVersionCheckError::Inconsistency)
3449		);
3450		assert_eq!(
3451			desc.check_version_acceptance(true),
3452			Err(CandidateDescriptorVersionCheckError::Inconsistency)
3453		);
3454	}
3455
3456	#[test]
3457	fn check_version_consistency_v3_expected_disagreement() {
3458		// V3 descriptor: version() returns V3, version_old_rules() returns V1.
3459		// check_version_consistency() is false — but this is expected.
3460		let desc = make_v3_descriptor();
3461
3462		assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3463		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3464		assert!(!desc.check_version_consistency());
3465		// Accepted when V3 is enabled.
3466		assert!(desc.check_version_acceptance(true).is_ok());
3467	}
3468
3469	#[test]
3470	fn v3_feature_activation_changes_descriptor_interpretation() {
3471		let desc = make_v3_descriptor();
3472
3473		// Sanity: the descriptor IS V3 under new rules but looks like V1 under old rules.
3474		assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3475		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3476
3477		// Before V3 activation: descriptor is treated as V1 — relay_parent is used.
3478		assert_eq!(desc.version_for_candidate_validation(false), CandidateDescriptorVersion::V1,);
3479		assert_eq!(
3480			desc.scheduling_parent_for_candidate_validation(false),
3481			Hash::repeat_byte(1), // relay_parent
3482		);
3483		assert_eq!(
3484			desc.scheduling_session_for_candidate_validation(false),
3485			None,
3486			"V1 has no embedded session — must be fetched from runtime",
3487		);
3488
3489		// After V3 activation: descriptor is correctly identified as V3.
3490		assert_eq!(desc.version_for_candidate_validation(true), CandidateDescriptorVersion::V3,);
3491		assert_eq!(
3492			desc.scheduling_parent_for_candidate_validation(true),
3493			Hash::repeat_byte(7), // scheduling_parent
3494		);
3495		assert_eq!(
3496			desc.scheduling_session_for_candidate_validation(true),
3497			Some(1), // session_index from descriptor, offset=0
3498		);
3499	}
3500
3501	#[test]
3502	fn check_version_acceptance_ambiguous_scheduling_parent_nonzero() {
3503		// Descriptor with scheduling_parent non-zero but version=0.
3504		// Old rules: V1 (scheduling_parent non-zero triggers old_v1_detected).
3505		// New rules: V2 (only checks reserved1[0..16], which is zero).
3506		let mut desc = make_v2_descriptor();
3507		desc.scheduling_parent = Hash::repeat_byte(0xAB);
3508
3509		assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3510		assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3511		assert!(!desc.check_version_consistency());
3512
3513		assert_eq!(
3514			desc.check_version_acceptance(false),
3515			Err(CandidateDescriptorVersionCheckError::Inconsistency)
3516		);
3517		assert_eq!(
3518			desc.check_version_acceptance(true),
3519			Err(CandidateDescriptorVersionCheckError::Inconsistency)
3520		);
3521	}
3522
3523	/// Codec index of the `ApprovalCheckingMultipleCandidates` variant (`#[codec(index = 4)]`).
3524	const APPROVAL_MULTIPLE_CANDIDATES_INDEX: u8 = 4;
3525
3526	fn coalesced_candidate_hashes(n: usize) -> Vec<CandidateHash> {
3527		(0..n).map(|i| CandidateHash(Hash::repeat_byte(i as u8))).collect()
3528	}
3529
3530	/// SCALE bytes for the `ApprovalCheckingMultipleCandidates` variant with a plain
3531	/// `Vec<CandidateHash>` body: the codec index byte followed by the `Vec` body (compact length
3532	/// + elements).
3533	fn approval_multiple_candidates_vec_bytes(hashes: &[CandidateHash]) -> Vec<u8> {
3534		let mut bytes = vec![APPROVAL_MULTIPLE_CANDIDATES_INDEX];
3535		bytes.extend(hashes.encode());
3536		bytes
3537	}
3538
3539	#[test]
3540	fn approval_multiple_candidates_bounded_vec_encodes_like_vec() {
3541		let limit = MAX_COALESCE_APPROVALS as usize;
3542		for n in [0, 1, 2, 8, limit - 1, limit] {
3543			let hashes = coalesced_candidate_hashes(n);
3544			let bounded: BoundedVec<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>> =
3545				hashes.clone().try_into().expect("n <= MAX_COALESCE_APPROVALS; qed");
3546
3547			// The inner collections encode to identical bytes.
3548			assert_eq!(hashes.encode(), bounded.encode(), "inner encoding differs for n = {n}");
3549
3550			// The full enum encodes identically to a plain `Vec` body.
3551			assert_eq!(
3552				ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(bounded).encode(),
3553				approval_multiple_candidates_vec_bytes(&hashes),
3554				"enum encoding differs from the plain Vec encoding for n = {n}",
3555			);
3556		}
3557	}
3558
3559	#[test]
3560	fn approval_multiple_candidates_decodes_vec_encoding() {
3561		let limit = MAX_COALESCE_APPROVALS as usize;
3562		for n in [0, 1, limit - 1, limit] {
3563			let hashes = coalesced_candidate_hashes(n);
3564			let vec_bytes = approval_multiple_candidates_vec_bytes(&hashes);
3565
3566			// A plain `Vec` encoding decodes into the `BoundedVec`-based variant.
3567			let decoded = ValidDisputeStatementKind::decode(&mut &vec_bytes[..])
3568				.expect("a Vec encoding within the limit must decode; qed");
3569			let expected = ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(
3570				hashes.clone().try_into().expect("n <= MAX_COALESCE_APPROVALS; qed"),
3571			);
3572			assert_eq!(decoded, expected, "decode mismatch for n = {n}");
3573
3574			// A full round-trip through the type is stable.
3575			assert_eq!(
3576				ValidDisputeStatementKind::decode(&mut &expected.encode()[..]).unwrap(),
3577				expected,
3578			);
3579		}
3580	}
3581
3582	#[test]
3583	fn approval_multiple_candidates_rejects_above_limit() {
3584		let limit = MAX_COALESCE_APPROVALS as usize;
3585
3586		// One past the limit is rejected at the length prefix, directly as the bounded collection.
3587		let too_many = coalesced_candidate_hashes(limit + 1);
3588		assert!(
3589			BoundedVec::<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>>::decode(
3590				&mut &too_many.encode()[..]
3591			)
3592			.is_err(),
3593			"BoundedVec must reject more than MAX_COALESCE_APPROVALS elements",
3594		);
3595
3596		// Through the enum, using a plain `Vec` body carrying one too many candidates.
3597		let vec_bytes = approval_multiple_candidates_vec_bytes(&too_many);
3598		assert!(
3599			ValidDisputeStatementKind::decode(&mut &vec_bytes[..]).is_err(),
3600			"enum must reject more than MAX_COALESCE_APPROVALS coalesced candidates",
3601		);
3602
3603		// The boundary value of exactly MAX_COALESCE_APPROVALS is accepted.
3604		let at_limit = approval_multiple_candidates_vec_bytes(&coalesced_candidate_hashes(limit));
3605		assert!(ValidDisputeStatementKind::decode(&mut &at_limit[..]).is_ok());
3606	}
3607
3608	#[test]
3609	fn approval_multiple_candidates_signing_payload_matches_vec() {
3610		let session = 7;
3611		for n in [1, 2, MAX_COALESCE_APPROVALS as usize] {
3612			let hashes = coalesced_candidate_hashes(n);
3613			let bounded: BoundedVec<CandidateHash, ConstU32<{ MAX_COALESCE_APPROVALS }>> =
3614				hashes.clone().try_into().expect("n <= MAX_COALESCE_APPROVALS; qed");
3615
3616			// The signing payload is taken over a slice, so `Vec` and `BoundedVec` produce the
3617			// same payload bytes for the same candidate set.
3618			assert_eq!(
3619				ApprovalVoteMultipleCandidates(&hashes).signing_payload(session),
3620				ApprovalVoteMultipleCandidates(&bounded).signing_payload(session),
3621				"signing payload differs for n = {n}",
3622			);
3623		}
3624	}
3625}