1use 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
48pub 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
57pub 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
64pub use polkadot_parachain_primitives::primitives::{
66 HeadData, HorizontalMessages, HrmpChannelId, Id, Id as ParaId, UpwardMessage, UpwardMessages,
67 ValidationCode, ValidationCodeHash, LOWEST_PUBLIC_ID,
68};
69
70mod 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
90pub 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
99pub type CollatorId = collator_app::Public;
101
102#[cfg(feature = "std")]
104pub type CollatorPair = collator_app::Pair;
105
106pub type CollatorSignature = collator_app::Signature;
108
109pub 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
117pub type ValidatorId = validator_app::Public;
122
123pub trait TypeIndex {
125 fn type_index(&self) -> usize;
127}
128
129#[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#[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
175impl 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 pub type ValidatorPair = validator_app::Pair;
191}
192
193pub type ValidatorSignature = validator_app::Signature;
198
199pub 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 pub const EPOCH_INDEX: &[u8] =
223 &hex!["1cb6f36e027abb2091cfb5110ab5087f38316cbf8fa0da822a20ac1c55bf1be3"];
224
225 pub const CURRENT_BLOCK_RANDOMNESS: &[u8] =
229 &hex!["1cb6f36e027abb2091cfb5110ab5087fd077dfdb8adb10f78f10a5df8742c545"];
230
231 pub const ONE_EPOCH_AGO_RANDOMNESS: &[u8] =
235 &hex!["1cb6f36e027abb2091cfb5110ab5087f7ce678799d3eff024253b90e84927cc6"];
236
237 pub const TWO_EPOCHS_AGO_RANDOMNESS: &[u8] =
241 &hex!["1cb6f36e027abb2091cfb5110ab5087f7a414cb008e0e61e46722aa60abdd672"];
242
243 pub const CURRENT_SLOT: &[u8] =
247 &hex!["1cb6f36e027abb2091cfb5110ab5087f06155b3cd9a8c9e5e9a23fd5dc13a5ed"];
248
249 pub const ACTIVE_CONFIG: &[u8] =
253 &hex!["06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385"];
254
255 pub const AUTHORITIES: &[u8] =
260 &hex!["1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d"];
261
262 pub const NEXT_AUTHORITIES: &[u8] =
267 &hex!["1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c"];
268
269 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 #[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 #[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 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 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 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 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 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 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 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
431pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u64 = 6000;
434
435pub const PARACHAINS_INHERENT_IDENTIFIER: InherentIdentifier = *b"parachn0";
437
438pub const ASSIGNMENT_KEY_TYPE_ID: KeyTypeId = KeyTypeId(*b"asgn");
440
441pub const MIN_CODE_SIZE: u32 = 9;
443
444pub const MAX_CODE_SIZE: u32 = 3 * 1024 * 1024;
454
455pub const MAX_HEAD_DATA_SIZE: u32 = 1 * 1024 * 1024;
462
463pub const MAX_POV_SIZE: u32 = 10 * 1024 * 1024;
471
472pub const ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE: u32 = 10_000;
476
477pub const ON_DEMAND_MAX_QUEUE_MAX_SIZE: u32 = 10_000;
481
482pub const LEGACY_MIN_BACKING_VOTES: u32 = 2;
485
486pub const DEFAULT_SCHEDULING_LOOKAHEAD: u32 = 3;
488
489mod assignment_app {
492 use sp_application_crypto::{app_crypto, sr25519};
493 app_crypto!(sr25519, super::ASSIGNMENT_KEY_TYPE_ID);
494}
495
496pub type AssignmentId = assignment_app::Public;
499
500sp_application_crypto::with_pair! {
501 pub type AssignmentPair = assignment_app::Pair;
504}
505
506pub type CandidateIndex = u32;
508
509#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
531#[cfg_attr(feature = "std", derive(Default))]
532pub struct PersistedValidationData<H = Hash, N = BlockNumber> {
533 pub parent_head: HeadData,
535 pub relay_parent_number: N,
537 pub relay_parent_storage_root: H,
539 pub max_pov_size: u32,
541}
542
543impl<H: Encode, N: Encode> PersistedValidationData<H, N> {
544 pub fn hash(&self) -> Hash {
546 BlakeTwo256::hash_of(self)
547 }
548}
549
550#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
552#[cfg_attr(feature = "std", derive(Default, Hash))]
553pub struct CandidateCommitments<N = BlockNumber> {
554 pub upward_messages: UpwardMessages,
556 pub horizontal_messages: HorizontalMessages,
558 pub new_validation_code: Option<ValidationCode>,
560 pub head_data: HeadData,
562 pub processed_downward_messages: u32,
564 pub hrmp_watermark: N,
567}
568
569impl CandidateCommitments {
570 pub fn hash(&self) -> Hash {
572 BlakeTwo256::hash_of(self)
573 }
574}
575
576#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
580pub struct AvailabilityBitfield(pub BitVec<u8, bitvec::order::Lsb0>);
581
582impl From<BitVec<u8, bitvec::order::Lsb0>> for AvailabilityBitfield {
583 fn from(inner: BitVec<u8, bitvec::order::Lsb0>) -> Self {
584 AvailabilityBitfield(inner)
585 }
586}
587
588pub type SignedStatement = Signed<CompactStatement>;
590pub type UncheckedSignedStatement = UncheckedSigned<CompactStatement>;
592
593pub type SignedAvailabilityBitfield = Signed<AvailabilityBitfield>;
595pub type UncheckedSignedAvailabilityBitfield = UncheckedSigned<AvailabilityBitfield>;
597
598pub type SignedAvailabilityBitfields = Vec<SignedAvailabilityBitfield>;
600pub type UncheckedSignedAvailabilityBitfields = Vec<UncheckedSignedAvailabilityBitfield>;
603
604pub fn check_candidate_backing<H: AsRef<[u8]> + Clone + Encode + core::fmt::Debug>(
615 candidate_hash: CandidateHash,
616 validity_votes: &[ValidityAttestation],
617 validator_indices: &BitSlice<u8, bitvec::order::Lsb0>,
618 signing_context: &SigningContext<H>,
619 group_len: usize,
620 validator_lookup: impl Fn(usize) -> Option<ValidatorId>,
621) -> Result<usize, ()> {
622 if validator_indices.len() != group_len {
623 log::debug!(
624 target: LOG_TARGET,
625 "Check candidate backing: indices mismatch: group_len = {} , indices_len = {}",
626 group_len,
627 validator_indices.len(),
628 );
629 return Err(());
630 }
631
632 if validity_votes.len() > group_len {
633 log::debug!(
634 target: LOG_TARGET,
635 "Check candidate backing: Too many votes, expected: {}, found: {}",
636 group_len,
637 validity_votes.len(),
638 );
639 return Err(());
640 }
641
642 let mut signed = 0;
643 for ((val_in_group_idx, _), attestation) in validator_indices
644 .iter()
645 .enumerate()
646 .filter(|(_, signed)| **signed)
647 .zip(validity_votes.iter())
648 {
649 let validator_id = validator_lookup(val_in_group_idx).ok_or(())?;
650 let payload = attestation.signed_payload(candidate_hash, signing_context);
651 let sig = attestation.signature();
652
653 if sig.verify(&payload[..], &validator_id) {
654 signed += 1;
655 } else {
656 log::debug!(
657 target: LOG_TARGET,
658 "Check candidate backing: Invalid signature. validator_id = {:?}, validator_index = {} ",
659 validator_id,
660 val_in_group_idx,
661 );
662 return Err(());
663 }
664 }
665
666 if signed != validity_votes.len() {
667 log::error!(
668 target: LOG_TARGET,
669 "Check candidate backing: Too many signatures, expected = {}, found = {}",
670 validity_votes.len(),
671 signed,
672 );
673 return Err(());
674 }
675
676 Ok(signed)
677}
678
679#[derive(
681 Encode,
682 Decode,
683 DecodeWithMemTracking,
684 Default,
685 PartialOrd,
686 Ord,
687 Eq,
688 PartialEq,
689 Clone,
690 Copy,
691 TypeInfo,
692 Debug,
693)]
694#[cfg_attr(feature = "std", derive(Hash))]
695pub struct CoreIndex(pub u32);
696
697impl From<u32> for CoreIndex {
698 fn from(i: u32) -> CoreIndex {
699 CoreIndex(i)
700 }
701}
702
703impl TypeIndex for CoreIndex {
704 fn type_index(&self) -> usize {
705 self.0 as usize
706 }
707}
708
709#[derive(
711 Encode,
712 Decode,
713 DecodeWithMemTracking,
714 Default,
715 Clone,
716 Copy,
717 Debug,
718 PartialEq,
719 Eq,
720 TypeInfo,
721 PartialOrd,
722 Ord,
723)]
724#[cfg_attr(feature = "std", derive(Hash))]
725pub struct GroupIndex(pub u32);
726
727impl From<u32> for GroupIndex {
728 fn from(i: u32) -> GroupIndex {
729 GroupIndex(i)
730 }
731}
732
733impl TypeIndex for GroupIndex {
734 fn type_index(&self) -> usize {
735 self.0 as usize
736 }
737}
738
739#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
741pub struct ParathreadClaim(pub Id, pub Option<CollatorId>);
742
743#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
745pub struct ParathreadEntry {
746 pub claim: ParathreadClaim,
748 pub retries: u32,
750}
751
752#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
754#[cfg_attr(feature = "std", derive(PartialEq))]
755pub struct GroupRotationInfo<N = BlockNumber> {
756 pub session_start_block: N,
758 pub group_rotation_frequency: N,
760 pub now: N,
762}
763
764impl GroupRotationInfo {
765 pub fn group_for_core(&self, core_index: CoreIndex, cores: usize) -> GroupIndex {
770 if self.group_rotation_frequency == 0 {
771 return GroupIndex(core_index.0);
772 }
773 if cores == 0 {
774 return GroupIndex(0);
775 }
776
777 let cores = core::cmp::min(cores, u32::MAX as usize);
778 let blocks_since_start = self.now.saturating_sub(self.session_start_block);
779 let rotations = blocks_since_start / self.group_rotation_frequency;
780
781 let idx = (core_index.0 as usize + rotations as usize) % cores;
784 GroupIndex(idx as u32)
785 }
786
787 pub fn core_for_group(&self, group_index: GroupIndex, cores: usize) -> CoreIndex {
792 if self.group_rotation_frequency == 0 {
793 return CoreIndex(group_index.0);
794 }
795 if cores == 0 {
796 return CoreIndex(0);
797 }
798
799 let cores = core::cmp::min(cores, u32::MAX as usize);
800 let blocks_since_start = self.now.saturating_sub(self.session_start_block);
801 let rotations = blocks_since_start / self.group_rotation_frequency;
802 let rotations = rotations % cores as u32;
803
804 let idx = (group_index.0 as usize + cores - rotations as usize) % cores;
810 CoreIndex(idx as u32)
811 }
812
813 pub fn bump_rotation(&self) -> Self {
815 GroupRotationInfo {
816 session_start_block: self.session_start_block,
817 group_rotation_frequency: self.group_rotation_frequency,
818 now: self.next_rotation_at(),
819 }
820 }
821}
822
823impl<N: Saturating + BaseArithmetic + Copy> GroupRotationInfo<N> {
824 pub fn next_rotation_at(&self) -> N {
827 let cycle_once = self.now + self.group_rotation_frequency;
828 cycle_once -
829 (cycle_once.saturating_sub(self.session_start_block) % self.group_rotation_frequency)
830 }
831
832 pub fn last_rotation_at(&self) -> N {
835 self.now -
836 (self.now.saturating_sub(self.session_start_block) % self.group_rotation_frequency)
837 }
838}
839
840#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
842#[cfg_attr(feature = "std", derive(PartialEq))]
843pub struct ScheduledCore {
844 pub para_id: Id,
846 pub collator: Option<CollatorId>,
850}
851
852#[derive(Clone, Copy, Encode, Decode, TypeInfo, Debug)]
854#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash))]
855pub enum OccupiedCoreAssumption {
856 #[codec(index = 0)]
858 Included,
859 #[codec(index = 1)]
861 TimedOut,
862 #[codec(index = 2)]
864 Free,
865}
866
867#[derive(Clone, Debug)]
869pub struct ApprovalVote(pub CandidateHash);
870
871impl ApprovalVote {
872 pub fn signing_payload(&self, session_index: SessionIndex) -> Vec<u8> {
874 const MAGIC: [u8; 4] = *b"APPR";
875
876 (MAGIC, &self.0, session_index).encode()
877 }
878}
879
880#[derive(Clone, Debug)]
882pub struct ApprovalVoteMultipleCandidates<'a>(pub &'a [CandidateHash]);
883
884impl<'a> ApprovalVoteMultipleCandidates<'a> {
885 pub fn signing_payload(&self, session_index: SessionIndex) -> Vec<u8> {
887 const MAGIC: [u8; 4] = *b"APPR";
888 if self.0.len() == 1 {
893 (MAGIC, self.0.first().expect("QED: we just checked"), session_index).encode()
894 } else {
895 (MAGIC, &self.0, session_index).encode()
896 }
897 }
898}
899
900#[derive(
902 Debug,
903 Copy,
904 Clone,
905 PartialEq,
906 Encode,
907 Decode,
908 DecodeWithMemTracking,
909 TypeInfo,
910 serde::Serialize,
911 serde::Deserialize,
912)]
913pub struct ApprovalVotingParams {
914 pub max_approval_coalesce_count: u32,
919}
920
921impl Default for ApprovalVotingParams {
922 fn default() -> Self {
923 Self { max_approval_coalesce_count: 1 }
924 }
925}
926
927#[repr(u8)]
929pub enum ValidityError {
930 InvalidEthereumSignature = 0,
932 SignerHasNoClaim = 1,
934 NoPermission = 2,
936 InvalidStatement = 3,
938}
939
940impl From<ValidityError> for u8 {
941 fn from(err: ValidityError) -> Self {
942 err as u8
943 }
944}
945
946#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
949#[cfg_attr(feature = "std", derive(PartialEq))]
950pub struct AbridgedHostConfiguration {
951 pub max_code_size: u32,
953 pub max_head_data_size: u32,
955 pub max_upward_queue_count: u32,
957 pub max_upward_queue_size: u32,
961 pub max_upward_message_size: u32,
965 pub max_upward_message_num_per_candidate: u32,
969 pub hrmp_max_message_num_per_candidate: u32,
973 pub validation_upgrade_cooldown: BlockNumber,
975 pub validation_upgrade_delay: BlockNumber,
977 pub async_backing_params: AsyncBackingParams,
979}
980
981#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
984#[cfg_attr(feature = "std", derive(Default, PartialEq))]
985pub struct AbridgedHrmpChannel {
986 pub max_capacity: u32,
988 pub max_total_size: u32,
990 pub max_message_size: u32,
992 pub msg_count: u32,
995 pub total_size: u32,
998 pub mqc_head: Option<Hash>,
1006}
1007
1008#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1010pub enum UpgradeRestriction {
1011 #[codec(index = 0)]
1014 Present,
1015}
1016
1017#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1023pub enum UpgradeGoAhead {
1024 #[codec(index = 0)]
1033 Abort,
1034 #[codec(index = 1)]
1038 GoAhead,
1039}
1040
1041pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"POL1";
1043
1044#[derive(Decode, Encode, Clone, PartialEq, Eq)]
1046pub enum ConsensusLog {
1047 #[codec(index = 1)]
1049 ParaUpgradeCode(Id, ValidationCodeHash),
1050 #[codec(index = 2)]
1052 ParaScheduleUpgradeCode(Id, ValidationCodeHash, BlockNumber),
1053 #[codec(index = 3)]
1056 ForceApprove(BlockNumber),
1057 #[codec(index = 4)]
1066 Revert(BlockNumber),
1067}
1068
1069impl ConsensusLog {
1070 pub fn from_digest_item(
1072 digest_item: &sp_runtime::DigestItem,
1073 ) -> Result<Option<Self>, codec::Error> {
1074 match digest_item {
1075 sp_runtime::DigestItem::Consensus(id, encoded) if id == &POLKADOT_ENGINE_ID => {
1076 Ok(Some(Self::decode(&mut &encoded[..])?))
1077 },
1078 _ => Ok(None),
1079 }
1080 }
1081}
1082
1083impl From<ConsensusLog> for sp_runtime::DigestItem {
1084 fn from(c: ConsensusLog) -> sp_runtime::DigestItem {
1085 Self::Consensus(POLKADOT_ENGINE_ID, c.encode())
1086 }
1087}
1088
1089#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1093pub enum DisputeStatement {
1094 #[codec(index = 0)]
1096 Valid(ValidDisputeStatementKind),
1097 #[codec(index = 1)]
1099 Invalid(InvalidDisputeStatementKind),
1100}
1101
1102impl DisputeStatement {
1103 pub fn payload_data(
1108 &self,
1109 candidate_hash: CandidateHash,
1110 session: SessionIndex,
1111 ) -> Result<Vec<u8>, ()> {
1112 match self {
1113 DisputeStatement::Valid(ValidDisputeStatementKind::Explicit) => {
1114 Ok(ExplicitDisputeStatement { valid: true, candidate_hash, session }
1115 .signing_payload())
1116 },
1117 DisputeStatement::Valid(ValidDisputeStatementKind::BackingSeconded(
1118 inclusion_parent,
1119 )) => Ok(CompactStatement::Seconded(candidate_hash).signing_payload(&SigningContext {
1120 session_index: session,
1121 parent_hash: *inclusion_parent,
1122 })),
1123 DisputeStatement::Valid(ValidDisputeStatementKind::BackingValid(inclusion_parent)) => {
1124 Ok(CompactStatement::Valid(candidate_hash).signing_payload(&SigningContext {
1125 session_index: session,
1126 parent_hash: *inclusion_parent,
1127 }))
1128 },
1129 DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalChecking) => {
1130 Ok(ApprovalVote(candidate_hash).signing_payload(session))
1131 },
1132 DisputeStatement::Valid(
1133 ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes),
1134 ) => {
1135 if candidate_hashes.contains(&candidate_hash) {
1136 Ok(ApprovalVoteMultipleCandidates(candidate_hashes).signing_payload(session))
1137 } else {
1138 Err(())
1139 }
1140 },
1141 DisputeStatement::Invalid(InvalidDisputeStatementKind::Explicit) => {
1142 Ok(ExplicitDisputeStatement { valid: false, candidate_hash, session }
1143 .signing_payload())
1144 },
1145 }
1146 }
1147
1148 pub fn check_signature(
1150 &self,
1151 validator_public: &ValidatorId,
1152 candidate_hash: CandidateHash,
1153 session: SessionIndex,
1154 validator_signature: &ValidatorSignature,
1155 ) -> Result<(), ()> {
1156 let payload = self.payload_data(candidate_hash, session)?;
1157
1158 if validator_signature.verify(&payload[..], &validator_public) {
1159 Ok(())
1160 } else {
1161 Err(())
1162 }
1163 }
1164
1165 pub fn indicates_validity(&self) -> bool {
1167 match *self {
1168 DisputeStatement::Valid(_) => true,
1169 DisputeStatement::Invalid(_) => false,
1170 }
1171 }
1172
1173 pub fn indicates_invalidity(&self) -> bool {
1175 match *self {
1176 DisputeStatement::Valid(_) => false,
1177 DisputeStatement::Invalid(_) => true,
1178 }
1179 }
1180
1181 pub fn is_backing(&self) -> bool {
1183 match self {
1184 Self::Valid(s) => s.is_backing(),
1185 Self::Invalid(_) => false,
1186 }
1187 }
1188}
1189
1190#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1192pub enum ValidDisputeStatementKind {
1193 #[codec(index = 0)]
1195 Explicit,
1196 #[codec(index = 1)]
1198 BackingSeconded(Hash),
1199 #[codec(index = 2)]
1201 BackingValid(Hash),
1202 #[codec(index = 3)]
1204 ApprovalChecking,
1205 #[codec(index = 4)]
1210 ApprovalCheckingMultipleCandidates(Vec<CandidateHash>),
1211}
1212
1213impl ValidDisputeStatementKind {
1214 pub fn is_backing(&self) -> bool {
1216 match self {
1217 ValidDisputeStatementKind::BackingSeconded(_) |
1218 ValidDisputeStatementKind::BackingValid(_) => true,
1219 ValidDisputeStatementKind::Explicit |
1220 ValidDisputeStatementKind::ApprovalChecking |
1221 ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_) => false,
1222 }
1223 }
1224}
1225
1226#[derive(Encode, Decode, DecodeWithMemTracking, Copy, Clone, PartialEq, Debug, TypeInfo)]
1228pub enum InvalidDisputeStatementKind {
1229 #[codec(index = 0)]
1231 Explicit,
1232}
1233
1234#[derive(Clone, PartialEq, Debug)]
1236pub struct ExplicitDisputeStatement {
1237 pub valid: bool,
1239 pub candidate_hash: CandidateHash,
1241 pub session: SessionIndex,
1243}
1244
1245impl ExplicitDisputeStatement {
1246 pub fn signing_payload(&self) -> Vec<u8> {
1248 const MAGIC: [u8; 4] = *b"DISP";
1249
1250 (MAGIC, self.valid, self.candidate_hash, self.session).encode()
1251 }
1252}
1253
1254#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1256pub struct DisputeStatementSet {
1257 pub candidate_hash: CandidateHash,
1259 pub session: SessionIndex,
1261 pub statements: Vec<(DisputeStatement, ValidatorIndex, ValidatorSignature)>,
1263}
1264
1265impl From<CheckedDisputeStatementSet> for DisputeStatementSet {
1266 fn from(other: CheckedDisputeStatementSet) -> Self {
1267 other.0
1268 }
1269}
1270
1271impl AsRef<DisputeStatementSet> for DisputeStatementSet {
1272 fn as_ref(&self) -> &DisputeStatementSet {
1273 &self
1274 }
1275}
1276
1277pub type MultiDisputeStatementSet = Vec<DisputeStatementSet>;
1279
1280#[derive(Clone, PartialEq, Debug, Encode)]
1282pub struct CheckedDisputeStatementSet(DisputeStatementSet);
1283
1284impl AsRef<DisputeStatementSet> for CheckedDisputeStatementSet {
1285 fn as_ref(&self) -> &DisputeStatementSet {
1286 &self.0
1287 }
1288}
1289
1290impl core::cmp::PartialEq<DisputeStatementSet> for CheckedDisputeStatementSet {
1291 fn eq(&self, other: &DisputeStatementSet) -> bool {
1292 self.0.eq(other)
1293 }
1294}
1295
1296impl CheckedDisputeStatementSet {
1297 pub fn unchecked_from_unchecked(unchecked: DisputeStatementSet) -> Self {
1300 Self(unchecked)
1301 }
1302}
1303
1304pub type CheckedMultiDisputeStatementSet = Vec<CheckedDisputeStatementSet>;
1306
1307#[derive(Encode, Decode, Clone, Debug, PartialEq, TypeInfo)]
1309pub struct DisputeState<N = BlockNumber> {
1310 pub validators_for: BitVec<u8, bitvec::order::Lsb0>, pub validators_against: BitVec<u8, bitvec::order::Lsb0>, pub start: N,
1316 pub concluded_at: Option<N>,
1318}
1319
1320#[derive(Clone, Eq, PartialEq, Decode, DecodeWithMemTracking, Encode, Debug, TypeInfo)]
1323pub enum ValidityAttestation {
1324 #[codec(index = 1)]
1327 Implicit(ValidatorSignature),
1328 #[codec(index = 2)]
1331 Explicit(ValidatorSignature),
1332}
1333
1334impl ValidityAttestation {
1335 pub fn to_compact_statement(&self, candidate_hash: CandidateHash) -> CompactStatement {
1338 match *self {
1342 ValidityAttestation::Implicit(_) => CompactStatement::Seconded(candidate_hash),
1343 ValidityAttestation::Explicit(_) => CompactStatement::Valid(candidate_hash),
1344 }
1345 }
1346
1347 pub fn signature(&self) -> &ValidatorSignature {
1349 match *self {
1350 ValidityAttestation::Implicit(ref sig) => sig,
1351 ValidityAttestation::Explicit(ref sig) => sig,
1352 }
1353 }
1354
1355 pub fn signed_payload<H: Encode>(
1358 &self,
1359 candidate_hash: CandidateHash,
1360 signing_context: &SigningContext<H>,
1361 ) -> Vec<u8> {
1362 match *self {
1363 ValidityAttestation::Implicit(_) => {
1364 (CompactStatement::Seconded(candidate_hash), signing_context).encode()
1365 },
1366 ValidityAttestation::Explicit(_) => {
1367 (CompactStatement::Valid(candidate_hash), signing_context).encode()
1368 },
1369 }
1370 }
1371}
1372
1373#[derive(Clone, Eq, PartialEq, Default, Decode, Encode, Debug)]
1375pub struct SigningContext<H = Hash> {
1376 pub session_index: sp_staking::SessionIndex,
1378 pub parent_hash: H,
1380}
1381
1382const BACKING_STATEMENT_MAGIC: [u8; 4] = *b"BKNG";
1383
1384#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
1387#[cfg_attr(feature = "std", derive(Hash))]
1388pub enum CompactStatement {
1389 Seconded(CandidateHash),
1391 Valid(CandidateHash),
1393}
1394
1395impl CompactStatement {
1396 pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
1399 (self, context).encode()
1400 }
1401
1402 pub fn candidate_hash(&self) -> &CandidateHash {
1404 match *self {
1405 CompactStatement::Seconded(ref h) | CompactStatement::Valid(ref h) => h,
1406 }
1407 }
1408}
1409
1410#[derive(Encode, Decode, TypeInfo)]
1412enum CompactStatementInner {
1413 #[codec(index = 1)]
1414 Seconded(CandidateHash),
1415 #[codec(index = 2)]
1416 Valid(CandidateHash),
1417}
1418
1419impl From<CompactStatement> for CompactStatementInner {
1420 fn from(s: CompactStatement) -> Self {
1421 match s {
1422 CompactStatement::Seconded(h) => CompactStatementInner::Seconded(h),
1423 CompactStatement::Valid(h) => CompactStatementInner::Valid(h),
1424 }
1425 }
1426}
1427
1428impl codec::Encode for CompactStatement {
1429 fn size_hint(&self) -> usize {
1430 4 + 1 + 32
1432 }
1433
1434 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
1435 dest.write(&BACKING_STATEMENT_MAGIC);
1436 CompactStatementInner::from(self.clone()).encode_to(dest)
1437 }
1438}
1439
1440impl codec::Decode for CompactStatement {
1441 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
1442 let maybe_magic = <[u8; 4]>::decode(input)?;
1443 if maybe_magic != BACKING_STATEMENT_MAGIC {
1444 return Err(codec::Error::from("invalid magic string"));
1445 }
1446
1447 Ok(match CompactStatementInner::decode(input)? {
1448 CompactStatementInner::Seconded(h) => CompactStatement::Seconded(h),
1449 CompactStatementInner::Valid(h) => CompactStatement::Valid(h),
1450 })
1451 }
1452}
1453
1454#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1456#[cfg_attr(feature = "std", derive(PartialEq))]
1457pub struct IndexedVec<K, V>(Vec<V>, PhantomData<fn(K) -> K>);
1458
1459impl<K, V> Default for IndexedVec<K, V> {
1460 fn default() -> Self {
1461 Self(vec![], PhantomData)
1462 }
1463}
1464
1465impl<K, V> From<Vec<V>> for IndexedVec<K, V> {
1466 fn from(validators: Vec<V>) -> Self {
1467 Self(validators, PhantomData)
1468 }
1469}
1470
1471impl<K, V> FromIterator<V> for IndexedVec<K, V> {
1472 fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
1473 Self(Vec::from_iter(iter), PhantomData)
1474 }
1475}
1476
1477impl<K, V> IndexedVec<K, V>
1478where
1479 V: Clone,
1480{
1481 pub fn get(&self, index: K) -> Option<&V>
1483 where
1484 K: TypeIndex,
1485 {
1486 self.0.get(index.type_index())
1487 }
1488
1489 pub fn get_mut(&mut self, index: K) -> Option<&mut V>
1491 where
1492 K: TypeIndex,
1493 {
1494 self.0.get_mut(index.type_index())
1495 }
1496
1497 pub fn len(&self) -> usize {
1499 self.0.len()
1500 }
1501
1502 pub fn to_vec(&self) -> Vec<V> {
1504 self.0.clone()
1505 }
1506
1507 pub fn iter(&self) -> Iter<'_, V> {
1509 self.0.iter()
1510 }
1511
1512 pub fn iter_mut(&mut self) -> IterMut<'_, V> {
1514 self.0.iter_mut()
1515 }
1516
1517 pub fn into_iter(self) -> IntoIter<V> {
1519 self.0.into_iter()
1520 }
1521
1522 pub fn is_empty(&self) -> bool {
1524 self.0.is_empty()
1525 }
1526}
1527
1528pub const fn byzantine_threshold(n: usize) -> usize {
1532 n.saturating_sub(1) / 3
1533}
1534
1535pub const fn supermajority_threshold(n: usize) -> usize {
1538 n - byzantine_threshold(n)
1539}
1540
1541pub fn effective_minimum_backing_votes(
1543 group_len: usize,
1544 configured_minimum_backing_votes: u32,
1545) -> usize {
1546 core::cmp::min(group_len, configured_minimum_backing_votes as usize)
1547}
1548
1549#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1554#[cfg_attr(feature = "std", derive(PartialEq))]
1555pub struct SessionInfo {
1556 pub active_validator_indices: Vec<ValidatorIndex>,
1560 pub random_seed: [u8; 32],
1562 pub dispute_period: SessionIndex,
1564
1565 pub validators: IndexedVec<ValidatorIndex, ValidatorId>,
1574 pub discovery_keys: Vec<AuthorityDiscoveryId>,
1581 pub assignment_keys: Vec<AssignmentId>,
1592 pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
1596 pub n_cores: u32,
1598 pub zeroth_delay_tranche_width: u32,
1600 pub relay_vrf_modulo_samples: u32,
1602 pub n_delay_tranches: u32,
1604 pub no_show_slots: u32,
1607 pub needed_approvals: u32,
1609}
1610
1611#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1614pub struct PvfCheckStatement {
1615 pub accept: bool,
1617 pub subject: ValidationCodeHash,
1619 pub session_index: SessionIndex,
1621 pub validator_index: ValidatorIndex,
1623}
1624
1625impl PvfCheckStatement {
1626 pub fn signing_payload(&self) -> Vec<u8> {
1631 const MAGIC: [u8; 4] = *b"VCPC"; (MAGIC, self.accept, self.subject, self.session_index, self.validator_index).encode()
1633 }
1634}
1635
1636pub struct WellKnownKey<T> {
1640 pub key: Vec<u8>,
1642 _p: core::marker::PhantomData<T>,
1643}
1644
1645impl<T> From<Vec<u8>> for WellKnownKey<T> {
1646 fn from(key: Vec<u8>) -> Self {
1647 Self { key, _p: Default::default() }
1648 }
1649}
1650
1651impl<T> AsRef<[u8]> for WellKnownKey<T> {
1652 fn as_ref(&self) -> &[u8] {
1653 self.key.as_ref()
1654 }
1655}
1656
1657impl<T: Decode> WellKnownKey<T> {
1658 pub fn get(&self) -> Option<T> {
1660 sp_io::storage::get(&self.key)
1661 .and_then(|raw| codec::DecodeAll::decode_all(&mut raw.as_ref()).ok())
1662 }
1663}
1664
1665impl<T: Encode> WellKnownKey<T> {
1666 pub fn set(&self, value: T) {
1668 sp_io::storage::set(&self.key, &value.encode());
1669 }
1670}
1671
1672#[derive(
1674 Encode,
1675 Decode,
1676 DecodeWithMemTracking,
1677 TypeInfo,
1678 Clone,
1679 Copy,
1680 Debug,
1681 PartialEq,
1682 Eq,
1683 Serialize,
1684 Deserialize,
1685)]
1686pub enum PvfPrepKind {
1687 Precheck,
1689
1690 Prepare,
1692}
1693
1694#[derive(
1696 Encode,
1697 Decode,
1698 DecodeWithMemTracking,
1699 TypeInfo,
1700 Clone,
1701 Copy,
1702 Debug,
1703 PartialEq,
1704 Eq,
1705 Serialize,
1706 Deserialize,
1707)]
1708pub enum PvfExecKind {
1709 Backing,
1711 Approval,
1713}
1714
1715pub type NodeFeatures = BitVec<u8, bitvec::order::Lsb0>;
1717
1718pub mod node_features {
1720 use crate::NodeFeatures;
1721
1722 #[repr(u8)]
1725 #[derive(Clone, Copy)]
1726 pub enum FeatureIndex {
1727 EnableAssignmentsV2 = 0,
1730 ElasticScalingMVP = 1,
1734 AvailabilityChunkMapping = 2,
1740 CandidateReceiptV2 = 3,
1744 CandidateReceiptV3 = 4,
1746 FirstUnassigned = 5,
1750 }
1751
1752 impl FeatureIndex {
1753 pub fn is_set(self, node_features: &NodeFeatures) -> bool {
1755 node_features.get(self as usize).map(|v| *v).unwrap_or(false)
1756 }
1757 }
1758}
1759
1760#[derive(
1762 Debug,
1763 Copy,
1764 Clone,
1765 PartialEq,
1766 Encode,
1767 Decode,
1768 DecodeWithMemTracking,
1769 TypeInfo,
1770 serde::Serialize,
1771 serde::Deserialize,
1772)]
1773pub struct SchedulerParams<BlockNumber> {
1774 pub group_rotation_frequency: BlockNumber,
1778 pub paras_availability_period: BlockNumber,
1793 pub max_validators_per_core: Option<u32>,
1797 pub lookahead: u32,
1799 pub num_cores: u32,
1801 #[deprecated]
1804 pub max_availability_timeouts: u32,
1805 pub on_demand_queue_max_size: u32,
1807 pub on_demand_target_queue_utilization: Perbill,
1809 pub on_demand_fee_variability: Perbill,
1812 pub on_demand_base_fee: Balance,
1814 #[deprecated]
1817 pub ttl: BlockNumber,
1818}
1819
1820impl<BlockNumber: Default + From<u32>> Default for SchedulerParams<BlockNumber> {
1821 #[allow(deprecated)]
1822 fn default() -> Self {
1823 Self {
1824 group_rotation_frequency: 1u32.into(),
1825 paras_availability_period: 1u32.into(),
1826 max_validators_per_core: Default::default(),
1827 lookahead: 1,
1828 num_cores: Default::default(),
1829 max_availability_timeouts: Default::default(),
1830 on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE,
1831 on_demand_target_queue_utilization: Perbill::from_percent(25),
1832 on_demand_fee_variability: Perbill::from_percent(3),
1833 on_demand_base_fee: 10_000_000u128,
1834 ttl: 5u32.into(),
1835 }
1836 }
1837}
1838
1839#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, TypeInfo, Debug, PartialOrd, Ord, Hash)]
1841pub enum CandidateDescriptorVersion {
1842 V1,
1844 V2,
1852 V3,
1854 Unknown,
1858}
1859
1860#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1862pub enum CandidateDescriptorVersionCheckError {
1863 Inconsistency,
1866 V3NotEnabled,
1868}
1869
1870impl core::fmt::Display for CandidateDescriptorVersionCheckError {
1873 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1874 match self {
1875 Self::Inconsistency => {
1876 write!(f, "Descriptor version detection inconsistency (old vs new rules disagree)")
1877 },
1878 Self::V3NotEnabled => write!(f, "V3 candidate descriptor but V3 feature not enabled"),
1879 }
1880 }
1881}
1882
1883#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1885pub struct CandidateDescriptorV2<H = Hash> {
1886 pub(super) para_id: ParaId,
1888 relay_parent: H,
1890 pub(super) version: u8,
1895 pub(super) core_index: u16,
1897 session_index: SessionIndex,
1899 scheduling_session_offset: u8,
1904 reserved1: [u8; 24],
1906 persisted_validation_data_hash: Hash,
1910 pov_hash: Hash,
1912 erasure_root: Hash,
1914 scheduling_parent: H, reserved2: [u8; 32],
1918 para_head: Hash,
1920 validation_code_hash: ValidationCodeHash,
1922}
1923
1924impl<H: AsRef<[u8]>> CandidateDescriptorV2<H> {
1925 pub fn version(&self) -> CandidateDescriptorVersion {
1989 self.v3_version()
1990 }
1991
1992 pub fn version_old_rules(&self) -> CandidateDescriptorVersion {
2003 self.v2_version()
2004 }
2005
2006 pub fn check_version_consistency(&self) -> bool {
2013 self.version() == self.version_old_rules()
2014 }
2015
2016 pub fn check_version_acceptance(
2033 &self,
2034 v3_enabled: bool,
2035 ) -> Result<(), CandidateDescriptorVersionCheckError> {
2036 let version = self.version();
2037
2038 let is_expected_v3_disagreement = version == CandidateDescriptorVersion::V3 && v3_enabled;
2041 if !self.check_version_consistency() && !is_expected_v3_disagreement {
2042 return Err(CandidateDescriptorVersionCheckError::Inconsistency);
2043 }
2044
2045 if version == CandidateDescriptorVersion::V3 && !v3_enabled {
2047 return Err(CandidateDescriptorVersionCheckError::V3NotEnabled);
2048 }
2049
2050 Ok(())
2051 }
2052
2053 fn v2_version(&self) -> CandidateDescriptorVersion {
2054 let old_v1_detected = self.reserved2 != [0u8; 32] ||
2058 self.reserved1 != [0u8; 24] ||
2059 self.scheduling_session_offset != 0 ||
2060 self.scheduling_parent.as_ref() != &[0u8; 32];
2061
2062 if old_v1_detected {
2063 return CandidateDescriptorVersion::V1;
2064 }
2065
2066 match self.version {
2067 0 => CandidateDescriptorVersion::V2,
2068 _ => CandidateDescriptorVersion::Unknown,
2069 }
2070 }
2071}
2072
2073impl<H> CandidateDescriptorV2<H> {
2074 fn v3_version(&self) -> CandidateDescriptorVersion {
2075 let new_v1_detected = self.reserved1[0..16] != [0u8; 16];
2084
2085 if new_v1_detected {
2086 return CandidateDescriptorVersion::V1;
2087 }
2088 match self.version {
2089 0 => CandidateDescriptorVersion::V2,
2090 1 => CandidateDescriptorVersion::V3,
2091 _ => CandidateDescriptorVersion::Unknown,
2092 }
2093 }
2094}
2095
2096macro_rules! impl_getter {
2097 ($field:ident, $type:ident) => {
2098 pub fn $field(&self) -> $type {
2100 self.$field
2101 }
2102 };
2103}
2104
2105impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2106 impl_getter!(erasure_root, Hash);
2107 impl_getter!(para_head, Hash);
2108 impl_getter!(relay_parent, H);
2109 impl_getter!(para_id, ParaId);
2110 impl_getter!(persisted_validation_data_hash, Hash);
2111 impl_getter!(pov_hash, Hash);
2112 impl_getter!(validation_code_hash, ValidationCodeHash);
2113
2114 #[cfg(feature = "test")]
2115 fn rebuild_collator_field(&self) -> CollatorId {
2116 let mut collator_id = Vec::with_capacity(32);
2117 let core_index: [u8; 2] = self.core_index.to_ne_bytes();
2118 let session_index: [u8; 4] = self.session_index.to_ne_bytes();
2119
2120 collator_id.push(self.version);
2121 collator_id.extend_from_slice(core_index.as_slice());
2122 collator_id.extend_from_slice(session_index.as_slice());
2123 collator_id.push(self.scheduling_session_offset);
2124 collator_id.extend_from_slice(self.reserved1.as_slice());
2125
2126 CollatorId::from_slice(&collator_id.as_slice())
2127 .expect("Slice size is exactly 32 bytes; qed")
2128 }
2129
2130 #[cfg(feature = "test")]
2132 pub fn collator(&self) -> Option<CollatorId> {
2133 if self.version() == CandidateDescriptorVersion::V1 {
2134 Some(self.rebuild_collator_field())
2135 } else {
2136 None
2137 }
2138 }
2139
2140 #[cfg(feature = "test")]
2141 fn rebuild_signature_field(&self) -> CollatorSignature {
2142 let mut signature_bytes = Vec::with_capacity(64);
2143 signature_bytes.extend_from_slice(self.scheduling_parent.as_ref());
2144 signature_bytes.extend_from_slice(self.reserved2.as_slice());
2145
2146 CollatorSignature::from_slice(&signature_bytes)
2147 .expect("Slice size is exactly 64 bytes; qed")
2148 }
2149
2150 #[cfg(feature = "test")]
2151 #[doc(hidden)]
2152 pub fn rebuild_collator_field_for_tests(&self) -> CollatorId {
2153 self.rebuild_collator_field()
2154 }
2155
2156 #[cfg(feature = "test")]
2157 #[doc(hidden)]
2158 pub fn rebuild_signature_field_for_tests(&self) -> CollatorSignature {
2159 self.rebuild_signature_field()
2160 }
2161
2162 #[cfg(feature = "test")]
2164 pub fn signature(&self) -> Option<CollatorSignature> {
2165 if self.version() == CandidateDescriptorVersion::V1 {
2166 return Some(self.rebuild_signature_field());
2167 }
2168
2169 None
2170 }
2171
2172 pub fn core_index(&self) -> Option<CoreIndex> {
2174 if self.version() == CandidateDescriptorVersion::V1 {
2175 return None;
2176 }
2177
2178 Some(CoreIndex(self.core_index as u32))
2179 }
2180
2181 pub fn session_index(&self) -> Option<SessionIndex> {
2183 if self.version() == CandidateDescriptorVersion::V1 {
2184 return None;
2185 }
2186
2187 Some(self.session_index)
2188 }
2189
2190 pub fn scheduling_parent(&self) -> H {
2196 match self.version() {
2197 CandidateDescriptorVersion::V1 => self.relay_parent,
2198 CandidateDescriptorVersion::V2 => self.relay_parent,
2199 CandidateDescriptorVersion::V3 => self.scheduling_parent,
2200 CandidateDescriptorVersion::Unknown => self.relay_parent,
2201 }
2202 }
2203
2204 pub fn scheduling_session(&self) -> Option<SessionIndex> {
2211 match self.version() {
2212 CandidateDescriptorVersion::V1 => None,
2213 CandidateDescriptorVersion::V2 => Some(self.session_index),
2214 CandidateDescriptorVersion::V3 => {
2215 Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2216 },
2217 CandidateDescriptorVersion::Unknown => None,
2218 }
2219 }
2220
2221 pub fn version_for_candidate_validation(
2240 &self,
2241 v3_ever_seen: bool,
2242 ) -> CandidateDescriptorVersion {
2243 if v3_ever_seen {
2244 self.version()
2245 } else {
2246 self.version_old_rules()
2247 }
2248 }
2249
2250 pub fn scheduling_parent_for_candidate_validation(&self, v3_ever_seen: bool) -> H
2254 where
2255 H: Copy,
2256 {
2257 match self.version_for_candidate_validation(v3_ever_seen) {
2258 CandidateDescriptorVersion::V3 => self.scheduling_parent,
2259 _ => self.relay_parent,
2260 }
2261 }
2262
2263 pub fn scheduling_session_for_candidate_validation(
2267 &self,
2268 v3_ever_seen: bool,
2269 ) -> Option<SessionIndex> {
2270 match self.version_for_candidate_validation(v3_ever_seen) {
2271 CandidateDescriptorVersion::V1 => None,
2272 CandidateDescriptorVersion::V2 => Some(self.session_index),
2273 CandidateDescriptorVersion::V3 => {
2274 Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2275 },
2276 CandidateDescriptorVersion::Unknown => None,
2277 }
2278 }
2279
2280 pub fn session_index_for_candidate_validation(
2284 &self,
2285 v3_ever_seen: bool,
2286 ) -> Option<SessionIndex> {
2287 match self.version_for_candidate_validation(v3_ever_seen) {
2288 CandidateDescriptorVersion::V1 | CandidateDescriptorVersion::Unknown => None,
2289 CandidateDescriptorVersion::V2 | CandidateDescriptorVersion::V3 => {
2290 Some(self.session_index)
2291 },
2292 }
2293 }
2294}
2295
2296impl<H> core::fmt::Debug for CandidateDescriptorV2<H>
2297where
2298 H: core::fmt::Debug,
2299{
2300 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2301 match self.v3_version() {
2304 CandidateDescriptorVersion::V1 => f
2305 .debug_struct("CandidateDescriptorV1")
2306 .field("para_id", &self.para_id)
2307 .field("relay_parent", &self.relay_parent)
2308 .field("persisted_validation_hash", &self.persisted_validation_data_hash)
2309 .field("pov_hash", &self.pov_hash)
2310 .field("erasure_root", &self.erasure_root)
2311 .field("para_head", &self.para_head)
2312 .field("validation_code_hash", &self.validation_code_hash)
2313 .finish(),
2314 CandidateDescriptorVersion::V2 => f
2315 .debug_struct("CandidateDescriptorV2")
2316 .field("para_id", &self.para_id)
2317 .field("relay_parent", &self.relay_parent)
2318 .field("core_index", &self.core_index)
2319 .field("session_index", &self.session_index)
2320 .field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2321 .field("pov_hash", &self.pov_hash)
2322 .field("erasure_root", &self.erasure_root)
2323 .field("para_head", &self.para_head)
2324 .field("validation_code_hash", &self.validation_code_hash)
2325 .finish(),
2326 CandidateDescriptorVersion::V3 => f
2327 .debug_struct("CandidateDescriptorV3")
2328 .field("para_id", &self.para_id)
2329 .field("relay_parent", &self.relay_parent)
2330 .field("core_index", &self.core_index)
2331 .field("session_index", &self.session_index)
2332 .field("scheduling_session_offset", &self.scheduling_session_offset)
2333 .field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2334 .field("pov_hash", &self.pov_hash)
2335 .field("erasure_root", &self.erasure_root)
2336 .field("scheduling_parent", &self.scheduling_parent)
2337 .field("para_head", &self.para_head)
2338 .field("validation_code_hash", &self.validation_code_hash)
2339 .finish(),
2340 CandidateDescriptorVersion::Unknown => {
2341 write!(f, "CandidateDescriptorV2(unknown version={})", self.version)
2342 },
2343 }
2344 }
2345}
2346
2347impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2348 pub fn new(
2350 para_id: Id,
2351 relay_parent: H,
2352 core_index: CoreIndex,
2353 session_index: SessionIndex,
2354 persisted_validation_data_hash: Hash,
2355 pov_hash: Hash,
2356 erasure_root: Hash,
2357 para_head: Hash,
2358 validation_code_hash: ValidationCodeHash,
2359 ) -> Self
2360 where
2361 H: Default,
2362 {
2363 Self {
2364 para_id,
2365 relay_parent,
2366 version: 0,
2367 core_index: core_index.0 as u16,
2368 session_index,
2369 scheduling_session_offset: 0,
2370 reserved1: [0; 24],
2371 persisted_validation_data_hash,
2372 pov_hash,
2373 erasure_root,
2374 scheduling_parent: H::default(),
2375 reserved2: [0; 32],
2376 para_head,
2377 validation_code_hash,
2378 }
2379 }
2380
2381 pub fn new_v3(
2387 para_id: Id,
2388 relay_parent: H,
2389 core_index: CoreIndex,
2390 session_index: SessionIndex,
2391 scheduling_session_index: SessionIndex,
2392 persisted_validation_data_hash: Hash,
2393 pov_hash: Hash,
2394 erasure_root: Hash,
2395 para_head: Hash,
2396 validation_code_hash: ValidationCodeHash,
2397 scheduling_parent: H,
2398 ) -> Self {
2399 Self {
2400 para_id,
2401 relay_parent,
2402 version: 1,
2403 core_index: core_index.0 as u16,
2404 session_index,
2405 scheduling_session_offset: scheduling_session_index
2406 .saturating_sub(session_index)
2407 .try_into()
2408 .expect("scheduling session offset should fit in u8"),
2409 reserved1: [0; 24],
2410 persisted_validation_data_hash,
2411 pov_hash,
2412 erasure_root,
2413 scheduling_parent,
2414 reserved2: [0; 32],
2415 para_head,
2416 validation_code_hash,
2417 }
2418 }
2419
2420 pub fn new_v1(
2423 para_id: Id,
2424 relay_parent: H,
2425 persisted_validation_data_hash: Hash,
2426 pov_hash: Hash,
2427 erasure_root: Hash,
2428 para_head: Hash,
2429 validation_code_hash: ValidationCodeHash,
2430 ) -> Self
2431 where
2432 H: Default,
2433 {
2434 Self {
2435 para_id,
2436 relay_parent,
2437 version: 0,
2438 core_index: 0,
2439 session_index: 0,
2440 scheduling_session_offset: 0,
2441 reserved1: [1u8; 24],
2442 persisted_validation_data_hash,
2443 pov_hash,
2444 erasure_root,
2445 scheduling_parent: H::default(),
2446 reserved2: [1u8; 32],
2447 para_head,
2448 validation_code_hash,
2449 }
2450 }
2451
2452 #[cfg(feature = "test")]
2453 #[doc(hidden)]
2454 pub fn new_from_raw(
2455 para_id: Id,
2456 relay_parent: H,
2457 version: u8,
2458 core_index: u16,
2459 session_index: SessionIndex,
2460 scheduling_session_offset: u8,
2461 reserved1: [u8; 24],
2462 persisted_validation_data_hash: Hash,
2463 pov_hash: Hash,
2464 erasure_root: Hash,
2465 scheduling_parent: H,
2466 reserved2: [u8; 32],
2467 para_head: Hash,
2468 validation_code_hash: ValidationCodeHash,
2469 ) -> Self {
2470 Self {
2471 para_id,
2472 relay_parent,
2473 version,
2474 core_index,
2475 session_index,
2476 scheduling_session_offset,
2477 reserved1,
2478 persisted_validation_data_hash,
2479 pov_hash,
2480 erasure_root,
2481 scheduling_parent,
2482 reserved2,
2483 para_head,
2484 validation_code_hash,
2485 }
2486 }
2487}
2488
2489#[cfg(feature = "test")]
2491pub trait MutateDescriptorV2<H> {
2492 fn set_relay_parent(&mut self, relay_parent: H);
2494 fn set_para_id(&mut self, para_id: Id);
2496 fn set_pov_hash(&mut self, pov_hash: Hash);
2498 fn set_version(&mut self, version: u8);
2500 fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash);
2502 fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash);
2504 fn set_erasure_root(&mut self, erasure_root: Hash);
2506 fn set_para_head(&mut self, para_head: Hash);
2508 fn set_core_index(&mut self, core_index: CoreIndex);
2510 fn set_session_index(&mut self, session_index: SessionIndex);
2512 fn set_reserved2(&mut self, reserved2: [u8; 32]);
2514 fn set_scheduling_parent(&mut self, scheduling_parent: H);
2516 fn set_scheduling_session_offset(&mut self, offset: u8);
2518}
2519
2520#[cfg(feature = "test")]
2521impl<H> MutateDescriptorV2<H> for CandidateDescriptorV2<H> {
2522 fn set_para_id(&mut self, para_id: Id) {
2523 self.para_id = para_id;
2524 }
2525
2526 fn set_relay_parent(&mut self, relay_parent: H) {
2527 self.relay_parent = relay_parent;
2528 }
2529
2530 fn set_pov_hash(&mut self, pov_hash: Hash) {
2531 self.pov_hash = pov_hash;
2532 }
2533
2534 fn set_version(&mut self, version: u8) {
2535 self.version = version;
2536 }
2537
2538 fn set_core_index(&mut self, core_index: CoreIndex) {
2539 self.core_index = core_index.0 as u16;
2540 }
2541
2542 fn set_session_index(&mut self, session_index: SessionIndex) {
2543 self.session_index = session_index;
2544 }
2545
2546 fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) {
2547 self.persisted_validation_data_hash = persisted_validation_data_hash;
2548 }
2549
2550 fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) {
2551 self.validation_code_hash = validation_code_hash;
2552 }
2553
2554 fn set_erasure_root(&mut self, erasure_root: Hash) {
2555 self.erasure_root = erasure_root;
2556 }
2557
2558 fn set_para_head(&mut self, para_head: Hash) {
2559 self.para_head = para_head;
2560 }
2561
2562 fn set_reserved2(&mut self, reserved2: [u8; 32]) {
2563 self.reserved2 = reserved2;
2564 }
2565
2566 fn set_scheduling_parent(&mut self, scheduling_parent: H) {
2567 self.scheduling_parent = scheduling_parent;
2568 }
2569
2570 fn set_scheduling_session_offset(&mut self, offset: u8) {
2571 self.scheduling_session_offset = offset;
2572 }
2573}
2574
2575#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2577pub struct CandidateReceiptV2<H = Hash> {
2578 pub descriptor: CandidateDescriptorV2<H>,
2580 pub commitments_hash: Hash,
2582}
2583
2584#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2586pub struct CommittedCandidateReceiptV2<H = Hash> {
2587 pub descriptor: CandidateDescriptorV2<H>,
2589 pub commitments: CandidateCommitments,
2591}
2592
2593#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
2595#[cfg_attr(feature = "std", derive(PartialEq))]
2596pub enum CandidateEvent<H = Hash> {
2597 #[codec(index = 0)]
2600 CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2601 #[codec(index = 1)]
2605 CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2606 #[codec(index = 2)]
2609 CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
2610}
2611
2612impl<H> CandidateReceiptV2<H> {
2613 pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
2615 &self.descriptor
2616 }
2617
2618 pub fn hash(&self) -> CandidateHash
2620 where
2621 H: Encode,
2622 {
2623 CandidateHash(BlakeTwo256::hash_of(self))
2624 }
2625}
2626
2627impl<H: Clone> CommittedCandidateReceiptV2<H> {
2628 pub fn to_plain(&self) -> CandidateReceiptV2<H> {
2630 CandidateReceiptV2 {
2631 descriptor: self.descriptor.clone(),
2632 commitments_hash: self.commitments.hash(),
2633 }
2634 }
2635
2636 pub fn hash(&self) -> CandidateHash
2641 where
2642 H: Encode,
2643 {
2644 self.to_plain().hash()
2645 }
2646
2647 pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
2649 where
2650 H: PartialEq,
2651 {
2652 receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
2653 }
2654}
2655
2656impl PartialOrd for CommittedCandidateReceiptV2 {
2657 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
2658 Some(self.cmp(other))
2659 }
2660}
2661
2662impl Ord for CommittedCandidateReceiptV2 {
2663 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
2664 self.descriptor
2665 .para_id
2666 .cmp(&other.descriptor.para_id)
2667 .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
2668 }
2669}
2670
2671#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2674pub struct CoreSelector(pub u8);
2675
2676impl From<u8> for CoreSelector {
2677 fn from(value: u8) -> Self {
2678 Self(value)
2679 }
2680}
2681
2682#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2684pub struct ClaimQueueOffset(pub u8);
2685
2686impl From<u8> for ClaimQueueOffset {
2687 fn from(value: u8) -> Self {
2688 Self(value)
2689 }
2690}
2691
2692#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2694pub enum UMPSignal {
2695 SelectCore(CoreSelector, ClaimQueueOffset),
2699 ApprovedPeer(ApprovedPeerId),
2701}
2702
2703pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0;
2706
2707pub type ApprovedPeerId = BoundedVec<u8, ConstU32<64>>;
2711
2712#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Default)]
2713pub struct CandidateUMPSignals {
2715 pub(super) select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2716 pub(super) approved_peer: Option<ApprovedPeerId>,
2717}
2718
2719impl CandidateUMPSignals {
2720 pub fn core_selector(&self) -> Option<(CoreSelector, ClaimQueueOffset)> {
2722 self.select_core
2723 }
2724
2725 pub fn approved_peer(&self) -> Option<&ApprovedPeerId> {
2727 self.approved_peer.as_ref()
2728 }
2729
2730 pub fn is_empty(&self) -> bool {
2732 self.select_core.is_none() && self.approved_peer.is_none()
2733 }
2734
2735 fn try_decode_signal(
2736 &mut self,
2737 buffer: &mut impl codec::Input,
2738 ) -> Result<(), CommittedCandidateReceiptError> {
2739 match UMPSignal::decode(buffer)
2740 .map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)?
2741 {
2742 UMPSignal::ApprovedPeer(approved_peer_id) if self.approved_peer.is_none() => {
2743 self.approved_peer = Some(approved_peer_id);
2744 },
2745 UMPSignal::SelectCore(core_selector, cq_offset) if self.select_core.is_none() => {
2746 self.select_core = Some((core_selector, cq_offset));
2747 },
2748 _ => {
2749 return Err(CommittedCandidateReceiptError::DuplicateUMPSignal);
2751 },
2752 };
2753
2754 Ok(())
2755 }
2756
2757 #[cfg(feature = "test")]
2758 #[doc(hidden)]
2759 pub fn dummy(
2760 select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2761 approved_peer: Option<ApprovedPeerId>,
2762 ) -> Self {
2763 Self { select_core, approved_peer }
2764 }
2765}
2766
2767pub const UMP_SEPARATOR: Vec<u8> = vec![];
2769
2770pub fn skip_ump_signals<'a>(
2772 upward_messages: impl Iterator<Item = &'a Vec<u8>>,
2773) -> impl Iterator<Item = &'a Vec<u8>> {
2774 upward_messages.take_while(|message| *message != &UMP_SEPARATOR)
2775}
2776
2777impl CandidateCommitments {
2778 pub fn ump_signals(&self) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2781 let mut res = CandidateUMPSignals::default();
2782
2783 let mut signals_iter =
2784 self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR);
2785
2786 if signals_iter.next().is_none() {
2787 return Ok(res);
2789 }
2790
2791 let Some(first_signal) = signals_iter.next() else { return Ok(res) };
2793 res.try_decode_signal(&mut first_signal.as_slice())?;
2794
2795 let Some(second_signal) = signals_iter.next() else { return Ok(res) };
2797 res.try_decode_signal(&mut second_signal.as_slice())?;
2798
2799 if signals_iter.next().is_some() {
2801 return Err(CommittedCandidateReceiptError::TooManyUMPSignals);
2802 }
2803
2804 Ok(res)
2805 }
2806}
2807
2808#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2810#[cfg_attr(feature = "std", derive(thiserror::Error))]
2811pub enum CommittedCandidateReceiptError {
2812 #[cfg_attr(feature = "std", error("The specified core index is invalid"))]
2814 InvalidCoreIndex,
2815 #[cfg_attr(
2817 feature = "std",
2818 error("The core index in commitments ({commitments:?}) doesn't match the one in descriptor ({descriptor:?})")
2819 )]
2820 CoreIndexMismatch {
2821 descriptor: CoreIndex,
2823 commitments: CoreIndex,
2825 },
2826 #[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))]
2828 InvalidSelectedCore,
2829 #[cfg_attr(feature = "std", error("Could not decode UMP signal"))]
2830 UmpSignalDecode,
2832 #[cfg_attr(
2834 feature = "std",
2835 error("The parachain is not assigned to any core at specified claim queue offset")
2836 )]
2837 NoAssignment,
2838 #[cfg_attr(feature = "std", error("Unknown internal version"))]
2840 UnknownVersion(u8),
2841 #[cfg_attr(feature = "std", error("Too many UMP signals"))]
2843 TooManyUMPSignals,
2844 #[cfg_attr(feature = "std", error("Duplicate UMP signal"))]
2846 DuplicateUMPSignal,
2847 #[cfg_attr(feature = "std", error("Version 1 receipt does not support ump signals"))]
2850 UMPSignalWithV1Descriptor,
2851 #[cfg_attr(feature = "std", error("Version 3 receipt requires ump signals"))]
2864 NoUMPSignalWithV3Descriptor,
2865}
2866
2867impl<H: Copy + AsRef<[u8]>> CommittedCandidateReceiptV2<H> {
2868 pub fn parse_ump_signals(
2881 &self,
2882 cores_per_para: &TransposedClaimQueue,
2883 ) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2884 let signals = self.commitments.ump_signals()?;
2885
2886 match self.descriptor.version() {
2887 CandidateDescriptorVersion::V1 => {
2888 if !signals.is_empty() {
2891 return Err(CommittedCandidateReceiptError::UMPSignalWithV1Descriptor);
2892 } else {
2893 return Ok(CandidateUMPSignals::default());
2895 }
2896 },
2897 CandidateDescriptorVersion::V2 => {},
2898 CandidateDescriptorVersion::Unknown => {
2899 return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version))
2900 },
2901 _ if signals.is_empty() => {
2902 return Err(CommittedCandidateReceiptError::NoUMPSignalWithV3Descriptor);
2904 },
2905 _ => {},
2906 }
2907
2908 let (maybe_core_index_selector, cq_offset) = signals
2910 .core_selector()
2911 .map(|(selector, offset)| (Some(selector), offset))
2912 .unwrap_or_else(|| (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)));
2913
2914 self.check_core_index(cores_per_para, maybe_core_index_selector, cq_offset)?;
2915
2916 Ok(signals)
2919 }
2920
2921 fn check_core_index(
2925 &self,
2926 cores_per_para: &TransposedClaimQueue,
2927 maybe_core_index_selector: Option<CoreSelector>,
2928 cq_offset: ClaimQueueOffset,
2929 ) -> Result<(), CommittedCandidateReceiptError> {
2930 let assigned_cores = cores_per_para
2931 .get(&self.descriptor.para_id())
2932 .ok_or(CommittedCandidateReceiptError::NoAssignment)?
2933 .get(&cq_offset.0)
2934 .ok_or(CommittedCandidateReceiptError::NoAssignment)?;
2935
2936 if assigned_cores.is_empty() {
2937 return Err(CommittedCandidateReceiptError::NoAssignment);
2938 }
2939
2940 let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
2941
2942 let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector {
2943 core_index_selector
2945 } else if assigned_cores.len() > 1 {
2946 if !assigned_cores.contains(&descriptor_core_index) {
2948 return Err(CommittedCandidateReceiptError::InvalidCoreIndex);
2950 } else {
2951 return Ok(());
2954 }
2955 } else {
2956 CoreSelector(0)
2958 };
2959
2960 let core_index = assigned_cores
2961 .iter()
2962 .nth(core_index_selector.0 as usize % assigned_cores.len())
2963 .ok_or(CommittedCandidateReceiptError::InvalidSelectedCore)
2964 .copied()?;
2965
2966 if core_index != descriptor_core_index {
2967 return Err(CommittedCandidateReceiptError::CoreIndexMismatch {
2968 descriptor: descriptor_core_index,
2969 commitments: core_index,
2970 });
2971 }
2972
2973 Ok(())
2974 }
2975}
2976
2977#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug, TypeInfo)]
2979pub struct BackedCandidate<H = Hash> {
2980 candidate: CommittedCandidateReceiptV2<H>,
2982 validity_votes: Vec<ValidityAttestation>,
2984 validator_indices: BitVec<u8, bitvec::order::Lsb0>,
2988}
2989
2990#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
2992pub struct InherentData<HDR: HeaderT = Header> {
2993 pub bitfields: UncheckedSignedAvailabilityBitfields,
2995 pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
2997 pub disputes: MultiDisputeStatementSet,
2999 pub parent_header: HDR,
3001}
3002
3003impl<H> BackedCandidate<H> {
3004 pub fn new(
3006 candidate: CommittedCandidateReceiptV2<H>,
3007 validity_votes: Vec<ValidityAttestation>,
3008 validator_indices: BitVec<u8, bitvec::order::Lsb0>,
3009 core_index: CoreIndex,
3010 ) -> Self {
3011 let mut instance = Self { candidate, validity_votes, validator_indices };
3012 instance.inject_core_index(core_index);
3013 instance
3014 }
3015
3016 pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
3018 &self.candidate
3019 }
3020
3021 #[cfg(feature = "test")]
3024 pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2<H> {
3025 &mut self.candidate
3026 }
3027 pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
3029 &self.candidate.descriptor
3030 }
3031
3032 #[cfg(feature = "test")]
3034 pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
3035 &mut self.candidate.descriptor
3036 }
3037
3038 pub fn validity_votes(&self) -> &[ValidityAttestation] {
3040 &self.validity_votes
3041 }
3042
3043 pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
3045 &mut self.validity_votes
3046 }
3047
3048 pub fn hash(&self) -> CandidateHash
3050 where
3051 H: Clone + Encode,
3052 {
3053 self.candidate.to_plain().hash()
3054 }
3055
3056 pub fn receipt(&self) -> CandidateReceiptV2<H>
3058 where
3059 H: Clone,
3060 {
3061 self.candidate.to_plain()
3062 }
3063
3064 #[cfg(feature = "test")]
3066 pub fn raw_validator_indices(&self) -> BitVec<u8, bitvec::order::Lsb0> {
3067 self.validator_indices.clone()
3068 }
3069
3070 pub fn validator_indices_and_core_index(
3072 &self,
3073 ) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
3074 let core_idx_offset = self.validator_indices.len().saturating_sub(8);
3076 if core_idx_offset > 0 {
3077 let (validator_indices_slice, core_idx_slice) =
3078 self.validator_indices.split_at(core_idx_offset);
3079 return (validator_indices_slice, Some(CoreIndex(core_idx_slice.load::<u8>() as u32)));
3080 }
3081
3082 (&self.validator_indices, None)
3083 }
3084
3085 fn inject_core_index(&mut self, core_index: CoreIndex) {
3087 let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
3088 BitVec::from_vec(vec![core_index.0 as u8]);
3089 self.validator_indices.extend(core_index_to_inject);
3090 }
3091
3092 pub fn set_validator_indices_and_core_index(
3094 &mut self,
3095 new_indices: BitVec<u8, bitvec::order::Lsb0>,
3096 maybe_core_index: Option<CoreIndex>,
3097 ) {
3098 self.validator_indices = new_indices;
3099
3100 if let Some(core_index) = maybe_core_index {
3101 self.inject_core_index(core_index);
3102 }
3103 }
3104}
3105
3106#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
3108#[cfg_attr(feature = "std", derive(PartialEq))]
3109pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
3110 pub session: SessionIndex,
3112 pub backing_validators_per_candidate:
3115 Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
3116 pub disputes: MultiDisputeStatementSet,
3120}
3121
3122#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3124#[cfg_attr(feature = "std", derive(PartialEq))]
3125pub struct OccupiedCore<H = Hash, N = BlockNumber> {
3126 pub next_up_on_available: Option<ScheduledCore>,
3130 pub occupied_since: N,
3132 pub time_out_at: N,
3134 pub next_up_on_time_out: Option<ScheduledCore>,
3138 pub availability: BitVec<u8, bitvec::order::Lsb0>,
3142 pub group_responsible: GroupIndex,
3144 pub candidate_hash: CandidateHash,
3146 pub candidate_descriptor: CandidateDescriptorV2<H>,
3148}
3149
3150impl<H, N> OccupiedCore<H, N> {
3151 pub fn para_id(&self) -> Id {
3153 self.candidate_descriptor.para_id
3154 }
3155}
3156
3157#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3159#[cfg_attr(feature = "std", derive(PartialEq))]
3160pub enum CoreState<H = Hash, N = BlockNumber> {
3161 #[codec(index = 0)]
3163 Occupied(OccupiedCore<H, N>),
3164 #[codec(index = 1)]
3170 Scheduled(ScheduledCore),
3171 #[codec(index = 2)]
3175 Free,
3176}
3177
3178impl<N> CoreState<N> {
3179 #[deprecated(
3184 note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead."
3185 )]
3186 pub fn para_id(&self) -> Option<Id> {
3187 match self {
3188 Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id),
3189 Self::Scheduled(core) => Some(core.para_id),
3190 Self::Free => None,
3191 }
3192 }
3193
3194 pub fn is_occupied(&self) -> bool {
3196 matches!(self, Self::Occupied(_))
3197 }
3198}
3199
3200pub type TransposedClaimQueue = BTreeMap<ParaId, BTreeMap<u8, BTreeSet<CoreIndex>>>;
3202
3203pub fn transpose_claim_queue(
3206 claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
3207) -> TransposedClaimQueue {
3208 let mut per_para_claim_queue = BTreeMap::new();
3209
3210 for (core, paras) in claim_queue {
3211 for (depth, para) in paras.into_iter().enumerate() {
3213 let depths: &mut BTreeMap<u8, BTreeSet<CoreIndex>> =
3214 per_para_claim_queue.entry(para).or_insert_with(|| Default::default());
3215
3216 depths.entry(depth as u8).or_default().insert(core);
3217 }
3218 }
3219
3220 per_para_claim_queue
3221}
3222
3223#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
3226pub enum DisputeOffenceKind {
3227 #[codec(index = 0)]
3230 ForInvalidBacked,
3231 #[codec(index = 1)]
3234 AgainstValid,
3235 #[codec(index = 2)]
3238 ForInvalidApproved,
3239}
3240
3241impl From<super::v9::slashing::SlashingOffenceKind> for DisputeOffenceKind {
3245 fn from(value: super::v9::slashing::SlashingOffenceKind) -> Self {
3246 match value {
3247 super::v9::slashing::SlashingOffenceKind::ForInvalid => Self::ForInvalidBacked,
3248 super::v9::slashing::SlashingOffenceKind::AgainstValid => Self::AgainstValid,
3249 }
3250 }
3251}
3252
3253impl TryFrom<DisputeOffenceKind> for super::v9::slashing::SlashingOffenceKind {
3255 type Error = ();
3256
3257 fn try_from(value: DisputeOffenceKind) -> Result<Self, Self::Error> {
3258 match value {
3259 DisputeOffenceKind::ForInvalidBacked => Ok(Self::ForInvalid),
3260 DisputeOffenceKind::AgainstValid => Ok(Self::AgainstValid),
3261 DisputeOffenceKind::ForInvalidApproved => Err(()),
3262 }
3263 }
3264}
3265
3266#[cfg(test)]
3267pub mod tests {
3269 use super::*;
3270
3271 #[test]
3272 fn group_rotation_info_calculations() {
3273 let info =
3274 GroupRotationInfo { session_start_block: 10u32, now: 15, group_rotation_frequency: 5 };
3275
3276 assert_eq!(info.next_rotation_at(), 20);
3277 assert_eq!(info.last_rotation_at(), 15);
3278 }
3279
3280 #[test]
3281 fn group_for_core_is_core_for_group() {
3282 for cores in 1..=256 {
3283 for rotations in 0..(cores * 2) {
3284 let info = GroupRotationInfo {
3285 session_start_block: 0u32,
3286 now: rotations,
3287 group_rotation_frequency: 1,
3288 };
3289
3290 for core in 0..cores {
3291 let group = info.group_for_core(CoreIndex(core), cores as usize);
3292 assert_eq!(info.core_for_group(group, cores as usize).0, core);
3293 }
3294 }
3295 }
3296 }
3297
3298 #[test]
3299 fn test_byzantine_threshold() {
3300 assert_eq!(byzantine_threshold(0), 0);
3301 assert_eq!(byzantine_threshold(1), 0);
3302 assert_eq!(byzantine_threshold(2), 0);
3303 assert_eq!(byzantine_threshold(3), 0);
3304 assert_eq!(byzantine_threshold(4), 1);
3305 assert_eq!(byzantine_threshold(5), 1);
3306 assert_eq!(byzantine_threshold(6), 1);
3307 assert_eq!(byzantine_threshold(7), 2);
3308 }
3309
3310 #[test]
3311 fn test_supermajority_threshold() {
3312 assert_eq!(supermajority_threshold(0), 0);
3313 assert_eq!(supermajority_threshold(1), 1);
3314 assert_eq!(supermajority_threshold(2), 2);
3315 assert_eq!(supermajority_threshold(3), 3);
3316 assert_eq!(supermajority_threshold(4), 3);
3317 assert_eq!(supermajority_threshold(5), 4);
3318 assert_eq!(supermajority_threshold(6), 5);
3319 assert_eq!(supermajority_threshold(7), 5);
3320 }
3321
3322 #[test]
3323 fn balance_bigger_than_usize() {
3324 let zero_b: Balance = 0;
3325 let zero_u: usize = 0;
3326
3327 assert!(zero_b.leading_zeros() >= zero_u.leading_zeros());
3328 }
3329
3330 fn make_v2_descriptor() -> CandidateDescriptorV2 {
3331 CandidateDescriptorV2::new(
3332 Id::from(1u32),
3333 Hash::repeat_byte(1),
3334 CoreIndex(0),
3335 1,
3336 Hash::repeat_byte(2),
3337 Hash::repeat_byte(3),
3338 Hash::repeat_byte(4),
3339 Hash::repeat_byte(5),
3340 ValidationCodeHash::from(Hash::repeat_byte(6)),
3341 )
3342 }
3343
3344 fn make_v3_descriptor() -> CandidateDescriptorV2 {
3345 CandidateDescriptorV2::new_v3(
3346 Id::from(1u32),
3347 Hash::repeat_byte(1),
3348 CoreIndex(0),
3349 1, 1, Hash::repeat_byte(2),
3352 Hash::repeat_byte(3),
3353 Hash::repeat_byte(4),
3354 Hash::repeat_byte(5),
3355 ValidationCodeHash::from(Hash::repeat_byte(6)),
3356 Hash::repeat_byte(7), )
3358 }
3359
3360 #[test]
3361 fn check_version_acceptance_v1_consistent() {
3362 let mut desc = make_v2_descriptor();
3365 desc.reserved1[0] = 0xFF;
3368
3369 assert_eq!(desc.version(), CandidateDescriptorVersion::V1);
3370 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3371 assert!(desc.check_version_consistency());
3372
3373 assert!(desc.check_version_acceptance(false).is_ok());
3374 assert!(desc.check_version_acceptance(true).is_ok());
3375 }
3376
3377 #[test]
3378 fn check_version_acceptance_v2_consistent() {
3379 let desc = make_v2_descriptor();
3381
3382 assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3383 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V2);
3384 assert!(desc.check_version_consistency());
3385
3386 assert!(desc.check_version_acceptance(false).is_ok());
3387 assert!(desc.check_version_acceptance(true).is_ok());
3388 }
3389
3390 #[test]
3391 fn check_version_acceptance_v3_when_enabled() {
3392 let desc = make_v3_descriptor();
3394
3395 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3396 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3397 assert!(!desc.check_version_consistency());
3398
3399 assert!(desc.check_version_acceptance(true).is_ok());
3400 }
3401
3402 #[test]
3403 fn check_version_acceptance_v3_when_disabled() {
3404 let desc = make_v3_descriptor();
3408
3409 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3410 assert_eq!(
3411 desc.check_version_acceptance(false),
3412 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3413 );
3414 }
3415
3416 #[test]
3417 fn check_version_acceptance_ambiguous_rejected() {
3418 let mut desc = make_v2_descriptor();
3421 desc.reserved1[16] = 0xFF; assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3424 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3425 assert!(!desc.check_version_consistency());
3426
3427 assert_eq!(
3429 desc.check_version_acceptance(false),
3430 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3431 );
3432 assert_eq!(
3433 desc.check_version_acceptance(true),
3434 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3435 );
3436 }
3437
3438 #[test]
3439 fn check_version_consistency_v3_expected_disagreement() {
3440 let desc = make_v3_descriptor();
3443
3444 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3445 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3446 assert!(!desc.check_version_consistency());
3447 assert!(desc.check_version_acceptance(true).is_ok());
3449 }
3450
3451 #[test]
3452 fn v3_feature_activation_changes_descriptor_interpretation() {
3453 let desc = make_v3_descriptor();
3454
3455 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3457 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3458
3459 assert_eq!(desc.version_for_candidate_validation(false), CandidateDescriptorVersion::V1,);
3461 assert_eq!(
3462 desc.scheduling_parent_for_candidate_validation(false),
3463 Hash::repeat_byte(1), );
3465 assert_eq!(
3466 desc.scheduling_session_for_candidate_validation(false),
3467 None,
3468 "V1 has no embedded session — must be fetched from runtime",
3469 );
3470
3471 assert_eq!(desc.version_for_candidate_validation(true), CandidateDescriptorVersion::V3,);
3473 assert_eq!(
3474 desc.scheduling_parent_for_candidate_validation(true),
3475 Hash::repeat_byte(7), );
3477 assert_eq!(
3478 desc.scheduling_session_for_candidate_validation(true),
3479 Some(1), );
3481 }
3482
3483 #[test]
3484 fn check_version_acceptance_ambiguous_scheduling_parent_nonzero() {
3485 let mut desc = make_v2_descriptor();
3489 desc.scheduling_parent = Hash::repeat_byte(0xAB);
3490
3491 assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3492 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3493 assert!(!desc.check_version_consistency());
3494
3495 assert_eq!(
3496 desc.check_version_acceptance(false),
3497 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3498 );
3499 assert_eq!(
3500 desc.check_version_acceptance(true),
3501 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3502 );
3503 }
3504}