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 = 5 * 1024 * 1024;
453
454pub const MAX_HEAD_DATA_SIZE: u32 = 1 * 1024 * 1024;
461
462pub const MAX_POV_SIZE: u32 = 10 * 1024 * 1024;
470
471pub const ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE: u32 = 10_000;
475
476pub const ON_DEMAND_MAX_QUEUE_MAX_SIZE: u32 = 10_000;
480
481pub const LEGACY_MIN_BACKING_VOTES: u32 = 2;
484
485pub const DEFAULT_SCHEDULING_LOOKAHEAD: u32 = 3;
487
488mod assignment_app {
491 use sp_application_crypto::{app_crypto, sr25519};
492 app_crypto!(sr25519, super::ASSIGNMENT_KEY_TYPE_ID);
493}
494
495pub type AssignmentId = assignment_app::Public;
498
499sp_application_crypto::with_pair! {
500 pub type AssignmentPair = assignment_app::Pair;
503}
504
505pub type CandidateIndex = u32;
507
508#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
530#[cfg_attr(feature = "std", derive(Default))]
531pub struct PersistedValidationData<H = Hash, N = BlockNumber> {
532 pub parent_head: HeadData,
534 pub relay_parent_number: N,
536 pub relay_parent_storage_root: H,
538 pub max_pov_size: u32,
540}
541
542impl<H: Encode, N: Encode> PersistedValidationData<H, N> {
543 pub fn hash(&self) -> Hash {
545 BlakeTwo256::hash_of(self)
546 }
547}
548
549#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
551#[cfg_attr(feature = "std", derive(Default, Hash))]
552pub struct CandidateCommitments<N = BlockNumber> {
553 pub upward_messages: UpwardMessages,
555 pub horizontal_messages: HorizontalMessages,
557 pub new_validation_code: Option<ValidationCode>,
559 pub head_data: HeadData,
561 pub processed_downward_messages: u32,
563 pub hrmp_watermark: N,
566}
567
568impl CandidateCommitments {
569 pub fn hash(&self) -> Hash {
571 BlakeTwo256::hash_of(self)
572 }
573}
574
575#[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
587pub type SignedStatement = Signed<CompactStatement>;
589pub type UncheckedSignedStatement = UncheckedSigned<CompactStatement>;
591
592pub type SignedAvailabilityBitfield = Signed<AvailabilityBitfield>;
594pub type UncheckedSignedAvailabilityBitfield = UncheckedSigned<AvailabilityBitfield>;
596
597pub type SignedAvailabilityBitfields = Vec<SignedAvailabilityBitfield>;
599pub type UncheckedSignedAvailabilityBitfields = Vec<UncheckedSignedAvailabilityBitfield>;
602
603pub 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#[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#[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#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
740pub struct ParathreadClaim(pub Id, pub Option<CollatorId>);
741
742#[derive(Clone, Encode, Decode, TypeInfo, PartialEq, Debug)]
744pub struct ParathreadEntry {
745 pub claim: ParathreadClaim,
747 pub retries: u32,
749}
750
751#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
753#[cfg_attr(feature = "std", derive(PartialEq))]
754pub struct GroupRotationInfo<N = BlockNumber> {
755 pub session_start_block: N,
757 pub group_rotation_frequency: N,
759 pub now: N,
761}
762
763impl GroupRotationInfo {
764 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 let idx = (core_index.0 as usize + rotations as usize) % cores;
783 GroupIndex(idx as u32)
784 }
785
786 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 let idx = (group_index.0 as usize + cores - rotations as usize) % cores;
809 CoreIndex(idx as u32)
810 }
811
812 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 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 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#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
841#[cfg_attr(feature = "std", derive(PartialEq))]
842pub struct ScheduledCore {
843 pub para_id: Id,
845 pub collator: Option<CollatorId>,
849}
850
851#[derive(Clone, Copy, Encode, Decode, TypeInfo, Debug)]
853#[cfg_attr(feature = "std", derive(PartialEq, Eq, Hash))]
854pub enum OccupiedCoreAssumption {
855 #[codec(index = 0)]
857 Included,
858 #[codec(index = 1)]
860 TimedOut,
861 #[codec(index = 2)]
863 Free,
864}
865
866#[derive(Clone, Debug)]
868pub struct ApprovalVote(pub CandidateHash);
869
870impl ApprovalVote {
871 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#[derive(Clone, Debug)]
881pub struct ApprovalVoteMultipleCandidates<'a>(pub &'a [CandidateHash]);
882
883impl<'a> ApprovalVoteMultipleCandidates<'a> {
884 pub fn signing_payload(&self, session_index: SessionIndex) -> Vec<u8> {
886 const MAGIC: [u8; 4] = *b"APPR";
887 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#[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 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#[repr(u8)]
928pub enum ValidityError {
929 InvalidEthereumSignature = 0,
931 SignerHasNoClaim = 1,
933 NoPermission = 2,
935 InvalidStatement = 3,
937}
938
939impl From<ValidityError> for u8 {
940 fn from(err: ValidityError) -> Self {
941 err as u8
942 }
943}
944
945#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
948#[cfg_attr(feature = "std", derive(PartialEq))]
949pub struct AbridgedHostConfiguration {
950 pub max_code_size: u32,
952 pub max_head_data_size: u32,
954 pub max_upward_queue_count: u32,
956 pub max_upward_queue_size: u32,
960 pub max_upward_message_size: u32,
964 pub max_upward_message_num_per_candidate: u32,
968 pub hrmp_max_message_num_per_candidate: u32,
972 pub validation_upgrade_cooldown: BlockNumber,
974 pub validation_upgrade_delay: BlockNumber,
976 pub async_backing_params: AsyncBackingParams,
978}
979
980#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
983#[cfg_attr(feature = "std", derive(Default, PartialEq))]
984pub struct AbridgedHrmpChannel {
985 pub max_capacity: u32,
987 pub max_total_size: u32,
989 pub max_message_size: u32,
991 pub msg_count: u32,
994 pub total_size: u32,
997 pub mqc_head: Option<Hash>,
1005}
1006
1007#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1009pub enum UpgradeRestriction {
1010 #[codec(index = 0)]
1013 Present,
1014}
1015
1016#[derive(Copy, Clone, Encode, Decode, PartialEq, Debug, TypeInfo)]
1022pub enum UpgradeGoAhead {
1023 #[codec(index = 0)]
1032 Abort,
1033 #[codec(index = 1)]
1037 GoAhead,
1038}
1039
1040pub const POLKADOT_ENGINE_ID: sp_runtime::ConsensusEngineId = *b"POL1";
1042
1043#[derive(Decode, Encode, Clone, PartialEq, Eq)]
1045pub enum ConsensusLog {
1046 #[codec(index = 1)]
1048 ParaUpgradeCode(Id, ValidationCodeHash),
1049 #[codec(index = 2)]
1051 ParaScheduleUpgradeCode(Id, ValidationCodeHash, BlockNumber),
1052 #[codec(index = 3)]
1055 ForceApprove(BlockNumber),
1056 #[codec(index = 4)]
1065 Revert(BlockNumber),
1066}
1067
1068impl ConsensusLog {
1069 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#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1092pub enum DisputeStatement {
1093 #[codec(index = 0)]
1095 Valid(ValidDisputeStatementKind),
1096 #[codec(index = 1)]
1098 Invalid(InvalidDisputeStatementKind),
1099}
1100
1101impl DisputeStatement {
1102 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 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 pub fn indicates_validity(&self) -> bool {
1166 match *self {
1167 DisputeStatement::Valid(_) => true,
1168 DisputeStatement::Invalid(_) => false,
1169 }
1170 }
1171
1172 pub fn indicates_invalidity(&self) -> bool {
1174 match *self {
1175 DisputeStatement::Valid(_) => false,
1176 DisputeStatement::Invalid(_) => true,
1177 }
1178 }
1179
1180 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#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1191pub enum ValidDisputeStatementKind {
1192 #[codec(index = 0)]
1194 Explicit,
1195 #[codec(index = 1)]
1197 BackingSeconded(Hash),
1198 #[codec(index = 2)]
1200 BackingValid(Hash),
1201 #[codec(index = 3)]
1203 ApprovalChecking,
1204 #[codec(index = 4)]
1209 ApprovalCheckingMultipleCandidates(Vec<CandidateHash>),
1210}
1211
1212impl ValidDisputeStatementKind {
1213 pub fn is_backing(&self) -> bool {
1215 match self {
1216 ValidDisputeStatementKind::BackingSeconded(_) |
1217 ValidDisputeStatementKind::BackingValid(_) => true,
1218 ValidDisputeStatementKind::Explicit |
1219 ValidDisputeStatementKind::ApprovalChecking |
1220 ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(_) => false,
1221 }
1222 }
1223}
1224
1225#[derive(Encode, Decode, DecodeWithMemTracking, Copy, Clone, PartialEq, Debug, TypeInfo)]
1227pub enum InvalidDisputeStatementKind {
1228 #[codec(index = 0)]
1230 Explicit,
1231}
1232
1233#[derive(Clone, PartialEq, Debug)]
1235pub struct ExplicitDisputeStatement {
1236 pub valid: bool,
1238 pub candidate_hash: CandidateHash,
1240 pub session: SessionIndex,
1242}
1243
1244impl ExplicitDisputeStatement {
1245 pub fn signing_payload(&self) -> Vec<u8> {
1247 const MAGIC: [u8; 4] = *b"DISP";
1248
1249 (MAGIC, self.valid, self.candidate_hash, self.session).encode()
1250 }
1251}
1252
1253#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1255pub struct DisputeStatementSet {
1256 pub candidate_hash: CandidateHash,
1258 pub session: SessionIndex,
1260 pub statements: Vec<(DisputeStatement, ValidatorIndex, ValidatorSignature)>,
1262}
1263
1264impl From<CheckedDisputeStatementSet> for DisputeStatementSet {
1265 fn from(other: CheckedDisputeStatementSet) -> Self {
1266 other.0
1267 }
1268}
1269
1270impl AsRef<DisputeStatementSet> for DisputeStatementSet {
1271 fn as_ref(&self) -> &DisputeStatementSet {
1272 &self
1273 }
1274}
1275
1276pub type MultiDisputeStatementSet = Vec<DisputeStatementSet>;
1278
1279#[derive(Clone, PartialEq, Debug, Encode)]
1281pub struct CheckedDisputeStatementSet(DisputeStatementSet);
1282
1283impl AsRef<DisputeStatementSet> for CheckedDisputeStatementSet {
1284 fn as_ref(&self) -> &DisputeStatementSet {
1285 &self.0
1286 }
1287}
1288
1289impl core::cmp::PartialEq<DisputeStatementSet> for CheckedDisputeStatementSet {
1290 fn eq(&self, other: &DisputeStatementSet) -> bool {
1291 self.0.eq(other)
1292 }
1293}
1294
1295impl CheckedDisputeStatementSet {
1296 pub fn unchecked_from_unchecked(unchecked: DisputeStatementSet) -> Self {
1299 Self(unchecked)
1300 }
1301}
1302
1303pub type CheckedMultiDisputeStatementSet = Vec<CheckedDisputeStatementSet>;
1305
1306#[derive(Encode, Decode, Clone, Debug, PartialEq, TypeInfo)]
1308pub struct DisputeState<N = BlockNumber> {
1309 pub validators_for: BitVec<u8, bitvec::order::Lsb0>, pub validators_against: BitVec<u8, bitvec::order::Lsb0>, pub start: N,
1315 pub concluded_at: Option<N>,
1317}
1318
1319#[derive(Clone, Eq, PartialEq, Decode, DecodeWithMemTracking, Encode, Debug, TypeInfo)]
1322pub enum ValidityAttestation {
1323 #[codec(index = 1)]
1326 Implicit(ValidatorSignature),
1327 #[codec(index = 2)]
1330 Explicit(ValidatorSignature),
1331}
1332
1333impl ValidityAttestation {
1334 pub fn to_compact_statement(&self, candidate_hash: CandidateHash) -> CompactStatement {
1337 match *self {
1341 ValidityAttestation::Implicit(_) => CompactStatement::Seconded(candidate_hash),
1342 ValidityAttestation::Explicit(_) => CompactStatement::Valid(candidate_hash),
1343 }
1344 }
1345
1346 pub fn signature(&self) -> &ValidatorSignature {
1348 match *self {
1349 ValidityAttestation::Implicit(ref sig) => sig,
1350 ValidityAttestation::Explicit(ref sig) => sig,
1351 }
1352 }
1353
1354 pub fn signed_payload<H: Encode>(
1357 &self,
1358 candidate_hash: CandidateHash,
1359 signing_context: &SigningContext<H>,
1360 ) -> Vec<u8> {
1361 match *self {
1362 ValidityAttestation::Implicit(_) => {
1363 (CompactStatement::Seconded(candidate_hash), signing_context).encode()
1364 },
1365 ValidityAttestation::Explicit(_) => {
1366 (CompactStatement::Valid(candidate_hash), signing_context).encode()
1367 },
1368 }
1369 }
1370}
1371
1372#[derive(Clone, Eq, PartialEq, Default, Decode, Encode, Debug)]
1374pub struct SigningContext<H = Hash> {
1375 pub session_index: sp_staking::SessionIndex,
1377 pub parent_hash: H,
1379}
1380
1381const BACKING_STATEMENT_MAGIC: [u8; 4] = *b"BKNG";
1382
1383#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
1386#[cfg_attr(feature = "std", derive(Hash))]
1387pub enum CompactStatement {
1388 Seconded(CandidateHash),
1390 Valid(CandidateHash),
1392}
1393
1394impl CompactStatement {
1395 pub fn signing_payload(&self, context: &SigningContext) -> Vec<u8> {
1398 (self, context).encode()
1399 }
1400
1401 pub fn candidate_hash(&self) -> &CandidateHash {
1403 match *self {
1404 CompactStatement::Seconded(ref h) | CompactStatement::Valid(ref h) => h,
1405 }
1406 }
1407}
1408
1409#[derive(Encode, Decode, TypeInfo)]
1411enum CompactStatementInner {
1412 #[codec(index = 1)]
1413 Seconded(CandidateHash),
1414 #[codec(index = 2)]
1415 Valid(CandidateHash),
1416}
1417
1418impl From<CompactStatement> for CompactStatementInner {
1419 fn from(s: CompactStatement) -> Self {
1420 match s {
1421 CompactStatement::Seconded(h) => CompactStatementInner::Seconded(h),
1422 CompactStatement::Valid(h) => CompactStatementInner::Valid(h),
1423 }
1424 }
1425}
1426
1427impl codec::Encode for CompactStatement {
1428 fn size_hint(&self) -> usize {
1429 4 + 1 + 32
1431 }
1432
1433 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
1434 dest.write(&BACKING_STATEMENT_MAGIC);
1435 CompactStatementInner::from(self.clone()).encode_to(dest)
1436 }
1437}
1438
1439impl codec::Decode for CompactStatement {
1440 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
1441 let maybe_magic = <[u8; 4]>::decode(input)?;
1442 if maybe_magic != BACKING_STATEMENT_MAGIC {
1443 return Err(codec::Error::from("invalid magic string"));
1444 }
1445
1446 Ok(match CompactStatementInner::decode(input)? {
1447 CompactStatementInner::Seconded(h) => CompactStatement::Seconded(h),
1448 CompactStatementInner::Valid(h) => CompactStatement::Valid(h),
1449 })
1450 }
1451}
1452
1453#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1455#[cfg_attr(feature = "std", derive(PartialEq))]
1456pub struct IndexedVec<K, V>(Vec<V>, PhantomData<fn(K) -> K>);
1457
1458impl<K, V> Default for IndexedVec<K, V> {
1459 fn default() -> Self {
1460 Self(vec![], PhantomData)
1461 }
1462}
1463
1464impl<K, V> From<Vec<V>> for IndexedVec<K, V> {
1465 fn from(validators: Vec<V>) -> Self {
1466 Self(validators, PhantomData)
1467 }
1468}
1469
1470impl<K, V> FromIterator<V> for IndexedVec<K, V> {
1471 fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
1472 Self(Vec::from_iter(iter), PhantomData)
1473 }
1474}
1475
1476impl<K, V> IndexedVec<K, V>
1477where
1478 V: Clone,
1479{
1480 pub fn get(&self, index: K) -> Option<&V>
1482 where
1483 K: TypeIndex,
1484 {
1485 self.0.get(index.type_index())
1486 }
1487
1488 pub fn get_mut(&mut self, index: K) -> Option<&mut V>
1490 where
1491 K: TypeIndex,
1492 {
1493 self.0.get_mut(index.type_index())
1494 }
1495
1496 pub fn len(&self) -> usize {
1498 self.0.len()
1499 }
1500
1501 pub fn to_vec(&self) -> Vec<V> {
1503 self.0.clone()
1504 }
1505
1506 pub fn iter(&self) -> Iter<'_, V> {
1508 self.0.iter()
1509 }
1510
1511 pub fn iter_mut(&mut self) -> IterMut<'_, V> {
1513 self.0.iter_mut()
1514 }
1515
1516 pub fn into_iter(self) -> IntoIter<V> {
1518 self.0.into_iter()
1519 }
1520
1521 pub fn is_empty(&self) -> bool {
1523 self.0.is_empty()
1524 }
1525}
1526
1527pub const fn byzantine_threshold(n: usize) -> usize {
1531 n.saturating_sub(1) / 3
1532}
1533
1534pub const fn supermajority_threshold(n: usize) -> usize {
1537 n - byzantine_threshold(n)
1538}
1539
1540pub fn effective_minimum_backing_votes(
1542 group_len: usize,
1543 configured_minimum_backing_votes: u32,
1544) -> usize {
1545 core::cmp::min(group_len, configured_minimum_backing_votes as usize)
1546}
1547
1548#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
1553#[cfg_attr(feature = "std", derive(PartialEq))]
1554pub struct SessionInfo {
1555 pub active_validator_indices: Vec<ValidatorIndex>,
1559 pub random_seed: [u8; 32],
1561 pub dispute_period: SessionIndex,
1563
1564 pub validators: IndexedVec<ValidatorIndex, ValidatorId>,
1573 pub discovery_keys: Vec<AuthorityDiscoveryId>,
1580 pub assignment_keys: Vec<AssignmentId>,
1591 pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
1595 pub n_cores: u32,
1597 pub zeroth_delay_tranche_width: u32,
1599 pub relay_vrf_modulo_samples: u32,
1601 pub n_delay_tranches: u32,
1603 pub no_show_slots: u32,
1606 pub needed_approvals: u32,
1608}
1609
1610#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
1613pub struct PvfCheckStatement {
1614 pub accept: bool,
1616 pub subject: ValidationCodeHash,
1618 pub session_index: SessionIndex,
1620 pub validator_index: ValidatorIndex,
1622}
1623
1624impl PvfCheckStatement {
1625 pub fn signing_payload(&self) -> Vec<u8> {
1630 const MAGIC: [u8; 4] = *b"VCPC"; (MAGIC, self.accept, self.subject, self.session_index, self.validator_index).encode()
1632 }
1633}
1634
1635pub struct WellKnownKey<T> {
1639 pub key: Vec<u8>,
1641 _p: core::marker::PhantomData<T>,
1642}
1643
1644impl<T> From<Vec<u8>> for WellKnownKey<T> {
1645 fn from(key: Vec<u8>) -> Self {
1646 Self { key, _p: Default::default() }
1647 }
1648}
1649
1650impl<T> AsRef<[u8]> for WellKnownKey<T> {
1651 fn as_ref(&self) -> &[u8] {
1652 self.key.as_ref()
1653 }
1654}
1655
1656impl<T: Decode> WellKnownKey<T> {
1657 pub fn get(&self) -> Option<T> {
1659 sp_io::storage::get(&self.key)
1660 .and_then(|raw| codec::DecodeAll::decode_all(&mut raw.as_ref()).ok())
1661 }
1662}
1663
1664impl<T: Encode> WellKnownKey<T> {
1665 pub fn set(&self, value: T) {
1667 sp_io::storage::set(&self.key, &value.encode());
1668 }
1669}
1670
1671#[derive(
1673 Encode,
1674 Decode,
1675 DecodeWithMemTracking,
1676 TypeInfo,
1677 Clone,
1678 Copy,
1679 Debug,
1680 PartialEq,
1681 Eq,
1682 Serialize,
1683 Deserialize,
1684)]
1685pub enum PvfPrepKind {
1686 Precheck,
1688
1689 Prepare,
1691}
1692
1693#[derive(
1695 Encode,
1696 Decode,
1697 DecodeWithMemTracking,
1698 TypeInfo,
1699 Clone,
1700 Copy,
1701 Debug,
1702 PartialEq,
1703 Eq,
1704 Serialize,
1705 Deserialize,
1706)]
1707pub enum PvfExecKind {
1708 Backing,
1710 Approval,
1712}
1713
1714pub type NodeFeatures = BitVec<u8, bitvec::order::Lsb0>;
1716
1717pub mod node_features {
1719 use crate::NodeFeatures;
1720
1721 #[repr(u8)]
1724 #[derive(Clone, Copy)]
1725 pub enum FeatureIndex {
1726 EnableAssignmentsV2 = 0,
1729 ElasticScalingMVP = 1,
1733 AvailabilityChunkMapping = 2,
1739 CandidateReceiptV2 = 3,
1743 CandidateReceiptV3 = 4,
1745 FirstUnassigned = 5,
1749 }
1750
1751 impl FeatureIndex {
1752 pub fn is_set(self, node_features: &NodeFeatures) -> bool {
1754 node_features.get(self as usize).map(|v| *v).unwrap_or(false)
1755 }
1756 }
1757}
1758
1759#[derive(
1761 Debug,
1762 Copy,
1763 Clone,
1764 PartialEq,
1765 Encode,
1766 Decode,
1767 DecodeWithMemTracking,
1768 TypeInfo,
1769 serde::Serialize,
1770 serde::Deserialize,
1771)]
1772pub struct SchedulerParams<BlockNumber> {
1773 pub group_rotation_frequency: BlockNumber,
1777 pub paras_availability_period: BlockNumber,
1792 pub max_validators_per_core: Option<u32>,
1796 pub lookahead: u32,
1798 pub num_cores: u32,
1800 #[deprecated]
1803 pub max_availability_timeouts: u32,
1804 pub on_demand_queue_max_size: u32,
1806 pub on_demand_target_queue_utilization: Perbill,
1808 pub on_demand_fee_variability: Perbill,
1811 pub on_demand_base_fee: Balance,
1813 #[deprecated]
1816 pub ttl: BlockNumber,
1817}
1818
1819impl<BlockNumber: Default + From<u32>> Default for SchedulerParams<BlockNumber> {
1820 #[allow(deprecated)]
1821 fn default() -> Self {
1822 Self {
1823 group_rotation_frequency: 1u32.into(),
1824 paras_availability_period: 1u32.into(),
1825 max_validators_per_core: Default::default(),
1826 lookahead: 1,
1827 num_cores: Default::default(),
1828 max_availability_timeouts: Default::default(),
1829 on_demand_queue_max_size: ON_DEMAND_DEFAULT_QUEUE_MAX_SIZE,
1830 on_demand_target_queue_utilization: Perbill::from_percent(25),
1831 on_demand_fee_variability: Perbill::from_percent(3),
1832 on_demand_base_fee: 10_000_000u128,
1833 ttl: 5u32.into(),
1834 }
1835 }
1836}
1837
1838#[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, TypeInfo, Debug, PartialOrd, Ord, Hash)]
1840pub enum CandidateDescriptorVersion {
1841 V1,
1843 V2,
1851 V3,
1853 Unknown,
1857}
1858
1859#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1861pub enum CandidateDescriptorVersionCheckError {
1862 Inconsistency,
1865 V3NotEnabled,
1867}
1868
1869impl core::fmt::Display for CandidateDescriptorVersionCheckError {
1872 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1873 match self {
1874 Self::Inconsistency => {
1875 write!(f, "Descriptor version detection inconsistency (old vs new rules disagree)")
1876 },
1877 Self::V3NotEnabled => write!(f, "V3 candidate descriptor but V3 feature not enabled"),
1878 }
1879 }
1880}
1881
1882#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
1884pub struct CandidateDescriptorV2<H = Hash> {
1885 pub(super) para_id: ParaId,
1887 relay_parent: H,
1889 pub(super) version: u8,
1894 pub(super) core_index: u16,
1896 session_index: SessionIndex,
1898 scheduling_session_offset: u8,
1903 reserved1: [u8; 24],
1905 persisted_validation_data_hash: Hash,
1909 pov_hash: Hash,
1911 erasure_root: Hash,
1913 scheduling_parent: H, reserved2: [u8; 32],
1917 para_head: Hash,
1919 validation_code_hash: ValidationCodeHash,
1921}
1922
1923impl<H: AsRef<[u8]>> CandidateDescriptorV2<H> {
1924 pub fn version(&self) -> CandidateDescriptorVersion {
1988 self.v3_version()
1989 }
1990
1991 pub fn version_old_rules(&self) -> CandidateDescriptorVersion {
2002 self.v2_version()
2003 }
2004
2005 pub fn check_version_consistency(&self) -> bool {
2012 self.version() == self.version_old_rules()
2013 }
2014
2015 pub fn check_version_acceptance(
2032 &self,
2033 v3_enabled: bool,
2034 ) -> Result<(), CandidateDescriptorVersionCheckError> {
2035 let version = self.version();
2036
2037 let is_expected_v3_disagreement = version == CandidateDescriptorVersion::V3 && v3_enabled;
2040 if !self.check_version_consistency() && !is_expected_v3_disagreement {
2041 return Err(CandidateDescriptorVersionCheckError::Inconsistency);
2042 }
2043
2044 if version == CandidateDescriptorVersion::V3 && !v3_enabled {
2046 return Err(CandidateDescriptorVersionCheckError::V3NotEnabled);
2047 }
2048
2049 Ok(())
2050 }
2051
2052 fn v2_version(&self) -> CandidateDescriptorVersion {
2053 let old_v1_detected = self.reserved2 != [0u8; 32] ||
2057 self.reserved1 != [0u8; 24] ||
2058 self.scheduling_session_offset != 0 ||
2059 self.scheduling_parent.as_ref() != &[0u8; 32];
2060
2061 if old_v1_detected {
2062 return CandidateDescriptorVersion::V1;
2063 }
2064
2065 match self.version {
2066 0 => CandidateDescriptorVersion::V2,
2067 _ => CandidateDescriptorVersion::Unknown,
2068 }
2069 }
2070}
2071
2072impl<H> CandidateDescriptorV2<H> {
2073 fn v3_version(&self) -> CandidateDescriptorVersion {
2074 let new_v1_detected = self.reserved1[0..16] != [0u8; 16];
2083
2084 if new_v1_detected {
2085 return CandidateDescriptorVersion::V1;
2086 }
2087 match self.version {
2088 0 => CandidateDescriptorVersion::V2,
2089 1 => CandidateDescriptorVersion::V3,
2090 _ => CandidateDescriptorVersion::Unknown,
2091 }
2092 }
2093}
2094
2095macro_rules! impl_getter {
2096 ($field:ident, $type:ident) => {
2097 pub fn $field(&self) -> $type {
2099 self.$field
2100 }
2101 };
2102}
2103
2104impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2105 impl_getter!(erasure_root, Hash);
2106 impl_getter!(para_head, Hash);
2107 impl_getter!(relay_parent, H);
2108 impl_getter!(para_id, ParaId);
2109 impl_getter!(persisted_validation_data_hash, Hash);
2110 impl_getter!(pov_hash, Hash);
2111 impl_getter!(validation_code_hash, ValidationCodeHash);
2112
2113 #[cfg(feature = "test")]
2114 fn rebuild_collator_field(&self) -> CollatorId {
2115 let mut collator_id = Vec::with_capacity(32);
2116 let core_index: [u8; 2] = self.core_index.to_ne_bytes();
2117 let session_index: [u8; 4] = self.session_index.to_ne_bytes();
2118
2119 collator_id.push(self.version);
2120 collator_id.extend_from_slice(core_index.as_slice());
2121 collator_id.extend_from_slice(session_index.as_slice());
2122 collator_id.push(self.scheduling_session_offset);
2123 collator_id.extend_from_slice(self.reserved1.as_slice());
2124
2125 CollatorId::from_slice(&collator_id.as_slice())
2126 .expect("Slice size is exactly 32 bytes; qed")
2127 }
2128
2129 #[cfg(feature = "test")]
2131 pub fn collator(&self) -> Option<CollatorId> {
2132 if self.version() == CandidateDescriptorVersion::V1 {
2133 Some(self.rebuild_collator_field())
2134 } else {
2135 None
2136 }
2137 }
2138
2139 #[cfg(feature = "test")]
2140 fn rebuild_signature_field(&self) -> CollatorSignature {
2141 let mut signature_bytes = Vec::with_capacity(64);
2142 signature_bytes.extend_from_slice(self.scheduling_parent.as_ref());
2143 signature_bytes.extend_from_slice(self.reserved2.as_slice());
2144
2145 CollatorSignature::from_slice(&signature_bytes)
2146 .expect("Slice size is exactly 64 bytes; qed")
2147 }
2148
2149 #[cfg(feature = "test")]
2150 #[doc(hidden)]
2151 pub fn rebuild_collator_field_for_tests(&self) -> CollatorId {
2152 self.rebuild_collator_field()
2153 }
2154
2155 #[cfg(feature = "test")]
2156 #[doc(hidden)]
2157 pub fn rebuild_signature_field_for_tests(&self) -> CollatorSignature {
2158 self.rebuild_signature_field()
2159 }
2160
2161 #[cfg(feature = "test")]
2163 pub fn signature(&self) -> Option<CollatorSignature> {
2164 if self.version() == CandidateDescriptorVersion::V1 {
2165 return Some(self.rebuild_signature_field());
2166 }
2167
2168 None
2169 }
2170
2171 pub fn core_index(&self) -> Option<CoreIndex> {
2173 if self.version() == CandidateDescriptorVersion::V1 {
2174 return None;
2175 }
2176
2177 Some(CoreIndex(self.core_index as u32))
2178 }
2179
2180 pub fn session_index(&self) -> Option<SessionIndex> {
2182 if self.version() == CandidateDescriptorVersion::V1 {
2183 return None;
2184 }
2185
2186 Some(self.session_index)
2187 }
2188
2189 pub fn scheduling_parent(&self) -> H {
2195 match self.version() {
2196 CandidateDescriptorVersion::V1 => self.relay_parent,
2197 CandidateDescriptorVersion::V2 => self.relay_parent,
2198 CandidateDescriptorVersion::V3 => self.scheduling_parent,
2199 CandidateDescriptorVersion::Unknown => self.relay_parent,
2200 }
2201 }
2202
2203 pub fn scheduling_session(&self) -> Option<SessionIndex> {
2210 match self.version() {
2211 CandidateDescriptorVersion::V1 => None,
2212 CandidateDescriptorVersion::V2 => Some(self.session_index),
2213 CandidateDescriptorVersion::V3 => {
2214 Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2215 },
2216 CandidateDescriptorVersion::Unknown => None,
2217 }
2218 }
2219
2220 pub fn version_for_candidate_validation(
2239 &self,
2240 v3_ever_seen: bool,
2241 ) -> CandidateDescriptorVersion {
2242 if v3_ever_seen {
2243 self.version()
2244 } else {
2245 self.version_old_rules()
2246 }
2247 }
2248
2249 pub fn scheduling_parent_for_candidate_validation(&self, v3_ever_seen: bool) -> H
2253 where
2254 H: Copy,
2255 {
2256 match self.version_for_candidate_validation(v3_ever_seen) {
2257 CandidateDescriptorVersion::V3 => self.scheduling_parent,
2258 _ => self.relay_parent,
2259 }
2260 }
2261
2262 pub fn scheduling_session_for_candidate_validation(
2266 &self,
2267 v3_ever_seen: bool,
2268 ) -> Option<SessionIndex> {
2269 match self.version_for_candidate_validation(v3_ever_seen) {
2270 CandidateDescriptorVersion::V1 => None,
2271 CandidateDescriptorVersion::V2 => Some(self.session_index),
2272 CandidateDescriptorVersion::V3 => {
2273 Some(self.session_index.saturating_add(self.scheduling_session_offset as _))
2274 },
2275 CandidateDescriptorVersion::Unknown => None,
2276 }
2277 }
2278
2279 pub fn session_index_for_candidate_validation(
2283 &self,
2284 v3_ever_seen: bool,
2285 ) -> Option<SessionIndex> {
2286 match self.version_for_candidate_validation(v3_ever_seen) {
2287 CandidateDescriptorVersion::V1 | CandidateDescriptorVersion::Unknown => None,
2288 CandidateDescriptorVersion::V2 | CandidateDescriptorVersion::V3 => {
2289 Some(self.session_index)
2290 },
2291 }
2292 }
2293}
2294
2295impl<H> core::fmt::Debug for CandidateDescriptorV2<H>
2296where
2297 H: core::fmt::Debug,
2298{
2299 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2300 match self.v3_version() {
2303 CandidateDescriptorVersion::V1 => f
2304 .debug_struct("CandidateDescriptorV1")
2305 .field("para_id", &self.para_id)
2306 .field("relay_parent", &self.relay_parent)
2307 .field("persisted_validation_hash", &self.persisted_validation_data_hash)
2308 .field("pov_hash", &self.pov_hash)
2309 .field("erasure_root", &self.erasure_root)
2310 .field("para_head", &self.para_head)
2311 .field("validation_code_hash", &self.validation_code_hash)
2312 .finish(),
2313 CandidateDescriptorVersion::V2 => f
2314 .debug_struct("CandidateDescriptorV2")
2315 .field("para_id", &self.para_id)
2316 .field("relay_parent", &self.relay_parent)
2317 .field("core_index", &self.core_index)
2318 .field("session_index", &self.session_index)
2319 .field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2320 .field("pov_hash", &self.pov_hash)
2321 .field("erasure_root", &self.erasure_root)
2322 .field("para_head", &self.para_head)
2323 .field("validation_code_hash", &self.validation_code_hash)
2324 .finish(),
2325 CandidateDescriptorVersion::V3 => f
2326 .debug_struct("CandidateDescriptorV3")
2327 .field("para_id", &self.para_id)
2328 .field("relay_parent", &self.relay_parent)
2329 .field("core_index", &self.core_index)
2330 .field("session_index", &self.session_index)
2331 .field("scheduling_session_offset", &self.scheduling_session_offset)
2332 .field("persisted_validation_data_hash", &self.persisted_validation_data_hash)
2333 .field("pov_hash", &self.pov_hash)
2334 .field("erasure_root", &self.erasure_root)
2335 .field("scheduling_parent", &self.scheduling_parent)
2336 .field("para_head", &self.para_head)
2337 .field("validation_code_hash", &self.validation_code_hash)
2338 .finish(),
2339 CandidateDescriptorVersion::Unknown => {
2340 write!(f, "CandidateDescriptorV2(unknown version={})", self.version)
2341 },
2342 }
2343 }
2344}
2345
2346impl<H: Copy + AsRef<[u8]>> CandidateDescriptorV2<H> {
2347 pub fn new(
2349 para_id: Id,
2350 relay_parent: H,
2351 core_index: CoreIndex,
2352 session_index: SessionIndex,
2353 persisted_validation_data_hash: Hash,
2354 pov_hash: Hash,
2355 erasure_root: Hash,
2356 para_head: Hash,
2357 validation_code_hash: ValidationCodeHash,
2358 ) -> Self
2359 where
2360 H: Default,
2361 {
2362 Self {
2363 para_id,
2364 relay_parent,
2365 version: 0,
2366 core_index: core_index.0 as u16,
2367 session_index,
2368 scheduling_session_offset: 0,
2369 reserved1: [0; 24],
2370 persisted_validation_data_hash,
2371 pov_hash,
2372 erasure_root,
2373 scheduling_parent: H::default(),
2374 reserved2: [0; 32],
2375 para_head,
2376 validation_code_hash,
2377 }
2378 }
2379
2380 pub fn new_v3(
2386 para_id: Id,
2387 relay_parent: H,
2388 core_index: CoreIndex,
2389 session_index: SessionIndex,
2390 scheduling_session_index: SessionIndex,
2391 persisted_validation_data_hash: Hash,
2392 pov_hash: Hash,
2393 erasure_root: Hash,
2394 para_head: Hash,
2395 validation_code_hash: ValidationCodeHash,
2396 scheduling_parent: H,
2397 ) -> Self {
2398 Self {
2399 para_id,
2400 relay_parent,
2401 version: 1,
2402 core_index: core_index.0 as u16,
2403 session_index,
2404 scheduling_session_offset: scheduling_session_index
2405 .saturating_sub(session_index)
2406 .try_into()
2407 .expect("scheduling session offset should fit in u8"),
2408 reserved1: [0; 24],
2409 persisted_validation_data_hash,
2410 pov_hash,
2411 erasure_root,
2412 scheduling_parent,
2413 reserved2: [0; 32],
2414 para_head,
2415 validation_code_hash,
2416 }
2417 }
2418
2419 pub fn new_v1(
2422 para_id: Id,
2423 relay_parent: H,
2424 persisted_validation_data_hash: Hash,
2425 pov_hash: Hash,
2426 erasure_root: Hash,
2427 para_head: Hash,
2428 validation_code_hash: ValidationCodeHash,
2429 ) -> Self
2430 where
2431 H: Default,
2432 {
2433 Self {
2434 para_id,
2435 relay_parent,
2436 version: 0,
2437 core_index: 0,
2438 session_index: 0,
2439 scheduling_session_offset: 0,
2440 reserved1: [1u8; 24],
2441 persisted_validation_data_hash,
2442 pov_hash,
2443 erasure_root,
2444 scheduling_parent: H::default(),
2445 reserved2: [1u8; 32],
2446 para_head,
2447 validation_code_hash,
2448 }
2449 }
2450
2451 #[cfg(feature = "test")]
2452 #[doc(hidden)]
2453 pub fn new_from_raw(
2454 para_id: Id,
2455 relay_parent: H,
2456 version: u8,
2457 core_index: u16,
2458 session_index: SessionIndex,
2459 scheduling_session_offset: u8,
2460 reserved1: [u8; 24],
2461 persisted_validation_data_hash: Hash,
2462 pov_hash: Hash,
2463 erasure_root: Hash,
2464 scheduling_parent: H,
2465 reserved2: [u8; 32],
2466 para_head: Hash,
2467 validation_code_hash: ValidationCodeHash,
2468 ) -> Self {
2469 Self {
2470 para_id,
2471 relay_parent,
2472 version,
2473 core_index,
2474 session_index,
2475 scheduling_session_offset,
2476 reserved1,
2477 persisted_validation_data_hash,
2478 pov_hash,
2479 erasure_root,
2480 scheduling_parent,
2481 reserved2,
2482 para_head,
2483 validation_code_hash,
2484 }
2485 }
2486}
2487
2488#[cfg(feature = "test")]
2490pub trait MutateDescriptorV2<H> {
2491 fn set_relay_parent(&mut self, relay_parent: H);
2493 fn set_para_id(&mut self, para_id: Id);
2495 fn set_pov_hash(&mut self, pov_hash: Hash);
2497 fn set_version(&mut self, version: u8);
2499 fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash);
2501 fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash);
2503 fn set_erasure_root(&mut self, erasure_root: Hash);
2505 fn set_para_head(&mut self, para_head: Hash);
2507 fn set_core_index(&mut self, core_index: CoreIndex);
2509 fn set_session_index(&mut self, session_index: SessionIndex);
2511 fn set_reserved2(&mut self, reserved2: [u8; 32]);
2513 fn set_scheduling_parent(&mut self, scheduling_parent: H);
2515 fn set_scheduling_session_offset(&mut self, offset: u8);
2517}
2518
2519#[cfg(feature = "test")]
2520impl<H> MutateDescriptorV2<H> for CandidateDescriptorV2<H> {
2521 fn set_para_id(&mut self, para_id: Id) {
2522 self.para_id = para_id;
2523 }
2524
2525 fn set_relay_parent(&mut self, relay_parent: H) {
2526 self.relay_parent = relay_parent;
2527 }
2528
2529 fn set_pov_hash(&mut self, pov_hash: Hash) {
2530 self.pov_hash = pov_hash;
2531 }
2532
2533 fn set_version(&mut self, version: u8) {
2534 self.version = version;
2535 }
2536
2537 fn set_core_index(&mut self, core_index: CoreIndex) {
2538 self.core_index = core_index.0 as u16;
2539 }
2540
2541 fn set_session_index(&mut self, session_index: SessionIndex) {
2542 self.session_index = session_index;
2543 }
2544
2545 fn set_persisted_validation_data_hash(&mut self, persisted_validation_data_hash: Hash) {
2546 self.persisted_validation_data_hash = persisted_validation_data_hash;
2547 }
2548
2549 fn set_validation_code_hash(&mut self, validation_code_hash: ValidationCodeHash) {
2550 self.validation_code_hash = validation_code_hash;
2551 }
2552
2553 fn set_erasure_root(&mut self, erasure_root: Hash) {
2554 self.erasure_root = erasure_root;
2555 }
2556
2557 fn set_para_head(&mut self, para_head: Hash) {
2558 self.para_head = para_head;
2559 }
2560
2561 fn set_reserved2(&mut self, reserved2: [u8; 32]) {
2562 self.reserved2 = reserved2;
2563 }
2564
2565 fn set_scheduling_parent(&mut self, scheduling_parent: H) {
2566 self.scheduling_parent = scheduling_parent;
2567 }
2568
2569 fn set_scheduling_session_offset(&mut self, offset: u8) {
2570 self.scheduling_session_offset = offset;
2571 }
2572}
2573
2574#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2576pub struct CandidateReceiptV2<H = Hash> {
2577 pub descriptor: CandidateDescriptorV2<H>,
2579 pub commitments_hash: Hash,
2581}
2582
2583#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2585pub struct CommittedCandidateReceiptV2<H = Hash> {
2586 pub descriptor: CandidateDescriptorV2<H>,
2588 pub commitments: CandidateCommitments,
2590}
2591
2592#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
2594#[cfg_attr(feature = "std", derive(PartialEq))]
2595pub enum CandidateEvent<H = Hash> {
2596 #[codec(index = 0)]
2599 CandidateBacked(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2600 #[codec(index = 1)]
2604 CandidateIncluded(CandidateReceiptV2<H>, HeadData, CoreIndex, GroupIndex),
2605 #[codec(index = 2)]
2608 CandidateTimedOut(CandidateReceiptV2<H>, HeadData, CoreIndex),
2609}
2610
2611impl<H> CandidateReceiptV2<H> {
2612 pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
2614 &self.descriptor
2615 }
2616
2617 pub fn hash(&self) -> CandidateHash
2619 where
2620 H: Encode,
2621 {
2622 CandidateHash(BlakeTwo256::hash_of(self))
2623 }
2624}
2625
2626impl<H: Clone> CommittedCandidateReceiptV2<H> {
2627 pub fn to_plain(&self) -> CandidateReceiptV2<H> {
2629 CandidateReceiptV2 {
2630 descriptor: self.descriptor.clone(),
2631 commitments_hash: self.commitments.hash(),
2632 }
2633 }
2634
2635 pub fn hash(&self) -> CandidateHash
2640 where
2641 H: Encode,
2642 {
2643 self.to_plain().hash()
2644 }
2645
2646 pub fn corresponds_to(&self, receipt: &CandidateReceiptV2<H>) -> bool
2648 where
2649 H: PartialEq,
2650 {
2651 receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
2652 }
2653}
2654
2655impl PartialOrd for CommittedCandidateReceiptV2 {
2656 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
2657 Some(self.cmp(other))
2658 }
2659}
2660
2661impl Ord for CommittedCandidateReceiptV2 {
2662 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
2663 self.descriptor
2664 .para_id
2665 .cmp(&other.descriptor.para_id)
2666 .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
2667 }
2668}
2669
2670#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2673pub struct CoreSelector(pub u8);
2674
2675impl From<u8> for CoreSelector {
2676 fn from(value: u8) -> Self {
2677 Self(value)
2678 }
2679}
2680
2681#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Copy)]
2683pub struct ClaimQueueOffset(pub u8);
2684
2685impl From<u8> for ClaimQueueOffset {
2686 fn from(value: u8) -> Self {
2687 Self(value)
2688 }
2689}
2690
2691#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2693pub enum UMPSignal {
2694 SelectCore(CoreSelector, ClaimQueueOffset),
2698 ApprovedPeer(ApprovedPeerId),
2700}
2701
2702pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0;
2705
2706pub type ApprovedPeerId = BoundedVec<u8, ConstU32<64>>;
2710
2711#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug, Default)]
2712pub struct CandidateUMPSignals {
2714 pub(super) select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2715 pub(super) approved_peer: Option<ApprovedPeerId>,
2716}
2717
2718impl CandidateUMPSignals {
2719 pub fn core_selector(&self) -> Option<(CoreSelector, ClaimQueueOffset)> {
2721 self.select_core
2722 }
2723
2724 pub fn approved_peer(&self) -> Option<&ApprovedPeerId> {
2726 self.approved_peer.as_ref()
2727 }
2728
2729 pub fn is_empty(&self) -> bool {
2731 self.select_core.is_none() && self.approved_peer.is_none()
2732 }
2733
2734 fn try_decode_signal(
2735 &mut self,
2736 buffer: &mut impl codec::Input,
2737 ) -> Result<(), CommittedCandidateReceiptError> {
2738 match UMPSignal::decode(buffer)
2739 .map_err(|_| CommittedCandidateReceiptError::UmpSignalDecode)?
2740 {
2741 UMPSignal::ApprovedPeer(approved_peer_id) if self.approved_peer.is_none() => {
2742 self.approved_peer = Some(approved_peer_id);
2743 },
2744 UMPSignal::SelectCore(core_selector, cq_offset) if self.select_core.is_none() => {
2745 self.select_core = Some((core_selector, cq_offset));
2746 },
2747 _ => {
2748 return Err(CommittedCandidateReceiptError::DuplicateUMPSignal);
2750 },
2751 };
2752
2753 Ok(())
2754 }
2755
2756 #[cfg(feature = "test")]
2757 #[doc(hidden)]
2758 pub fn dummy(
2759 select_core: Option<(CoreSelector, ClaimQueueOffset)>,
2760 approved_peer: Option<ApprovedPeerId>,
2761 ) -> Self {
2762 Self { select_core, approved_peer }
2763 }
2764}
2765
2766pub const UMP_SEPARATOR: Vec<u8> = vec![];
2768
2769pub fn skip_ump_signals<'a>(
2771 upward_messages: impl Iterator<Item = &'a Vec<u8>>,
2772) -> impl Iterator<Item = &'a Vec<u8>> {
2773 upward_messages.take_while(|message| *message != &UMP_SEPARATOR)
2774}
2775
2776impl CandidateCommitments {
2777 pub fn ump_signals(&self) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2780 let mut res = CandidateUMPSignals::default();
2781
2782 let mut signals_iter =
2783 self.upward_messages.iter().skip_while(|message| *message != &UMP_SEPARATOR);
2784
2785 if signals_iter.next().is_none() {
2786 return Ok(res);
2788 }
2789
2790 let Some(first_signal) = signals_iter.next() else { return Ok(res) };
2792 res.try_decode_signal(&mut first_signal.as_slice())?;
2793
2794 let Some(second_signal) = signals_iter.next() else { return Ok(res) };
2796 res.try_decode_signal(&mut second_signal.as_slice())?;
2797
2798 if signals_iter.next().is_some() {
2800 return Err(CommittedCandidateReceiptError::TooManyUMPSignals);
2801 }
2802
2803 Ok(res)
2804 }
2805}
2806
2807#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, Debug)]
2809#[cfg_attr(feature = "std", derive(thiserror::Error))]
2810pub enum CommittedCandidateReceiptError {
2811 #[cfg_attr(feature = "std", error("The specified core index is invalid"))]
2813 InvalidCoreIndex,
2814 #[cfg_attr(
2816 feature = "std",
2817 error("The core index in commitments ({commitments:?}) doesn't match the one in descriptor ({descriptor:?})")
2818 )]
2819 CoreIndexMismatch {
2820 descriptor: CoreIndex,
2822 commitments: CoreIndex,
2824 },
2825 #[cfg_attr(feature = "std", error("The core selector or claim queue offset is invalid"))]
2827 InvalidSelectedCore,
2828 #[cfg_attr(feature = "std", error("Could not decode UMP signal"))]
2829 UmpSignalDecode,
2831 #[cfg_attr(
2833 feature = "std",
2834 error("The parachain is not assigned to any core at specified claim queue offset")
2835 )]
2836 NoAssignment,
2837 #[cfg_attr(feature = "std", error("Unknown internal version"))]
2839 UnknownVersion(u8),
2840 #[cfg_attr(feature = "std", error("Too many UMP signals"))]
2842 TooManyUMPSignals,
2843 #[cfg_attr(feature = "std", error("Duplicate UMP signal"))]
2845 DuplicateUMPSignal,
2846 #[cfg_attr(feature = "std", error("Version 1 receipt does not support ump signals"))]
2849 UMPSignalWithV1Descriptor,
2850 #[cfg_attr(feature = "std", error("Version 3 receipt requires ump signals"))]
2863 NoUMPSignalWithV3Descriptor,
2864}
2865
2866impl<H: Copy + AsRef<[u8]>> CommittedCandidateReceiptV2<H> {
2867 pub fn parse_ump_signals(
2880 &self,
2881 cores_per_para: &TransposedClaimQueue,
2882 ) -> Result<CandidateUMPSignals, CommittedCandidateReceiptError> {
2883 let signals = self.commitments.ump_signals()?;
2884
2885 match self.descriptor.version() {
2886 CandidateDescriptorVersion::V1 => {
2887 if !signals.is_empty() {
2890 return Err(CommittedCandidateReceiptError::UMPSignalWithV1Descriptor);
2891 } else {
2892 return Ok(CandidateUMPSignals::default());
2894 }
2895 },
2896 CandidateDescriptorVersion::V2 => {},
2897 CandidateDescriptorVersion::Unknown => {
2898 return Err(CommittedCandidateReceiptError::UnknownVersion(self.descriptor.version))
2899 },
2900 _ if signals.is_empty() => {
2901 return Err(CommittedCandidateReceiptError::NoUMPSignalWithV3Descriptor);
2903 },
2904 _ => {},
2905 }
2906
2907 let (maybe_core_index_selector, cq_offset) = signals
2909 .core_selector()
2910 .map(|(selector, offset)| (Some(selector), offset))
2911 .unwrap_or_else(|| (None, ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)));
2912
2913 self.check_core_index(cores_per_para, maybe_core_index_selector, cq_offset)?;
2914
2915 Ok(signals)
2918 }
2919
2920 fn check_core_index(
2924 &self,
2925 cores_per_para: &TransposedClaimQueue,
2926 maybe_core_index_selector: Option<CoreSelector>,
2927 cq_offset: ClaimQueueOffset,
2928 ) -> Result<(), CommittedCandidateReceiptError> {
2929 let assigned_cores = cores_per_para
2930 .get(&self.descriptor.para_id())
2931 .ok_or(CommittedCandidateReceiptError::NoAssignment)?
2932 .get(&cq_offset.0)
2933 .ok_or(CommittedCandidateReceiptError::NoAssignment)?;
2934
2935 if assigned_cores.is_empty() {
2936 return Err(CommittedCandidateReceiptError::NoAssignment);
2937 }
2938
2939 let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32);
2940
2941 let core_index_selector = if let Some(core_index_selector) = maybe_core_index_selector {
2942 core_index_selector
2944 } else if assigned_cores.len() > 1 {
2945 if !assigned_cores.contains(&descriptor_core_index) {
2947 return Err(CommittedCandidateReceiptError::InvalidCoreIndex);
2949 } else {
2950 return Ok(());
2953 }
2954 } else {
2955 CoreSelector(0)
2957 };
2958
2959 let core_index = assigned_cores
2960 .iter()
2961 .nth(core_index_selector.0 as usize % assigned_cores.len())
2962 .ok_or(CommittedCandidateReceiptError::InvalidSelectedCore)
2963 .copied()?;
2964
2965 if core_index != descriptor_core_index {
2966 return Err(CommittedCandidateReceiptError::CoreIndexMismatch {
2967 descriptor: descriptor_core_index,
2968 commitments: core_index,
2969 });
2970 }
2971
2972 Ok(())
2973 }
2974}
2975
2976#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, Debug, TypeInfo)]
2978pub struct BackedCandidate<H = Hash> {
2979 candidate: CommittedCandidateReceiptV2<H>,
2981 validity_votes: Vec<ValidityAttestation>,
2983 validator_indices: BitVec<u8, bitvec::order::Lsb0>,
2987}
2988
2989#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Debug, TypeInfo)]
2991pub struct InherentData<HDR: HeaderT = Header> {
2992 pub bitfields: UncheckedSignedAvailabilityBitfields,
2994 pub backed_candidates: Vec<BackedCandidate<HDR::Hash>>,
2996 pub disputes: MultiDisputeStatementSet,
2998 pub parent_header: HDR,
3000}
3001
3002impl<H> BackedCandidate<H> {
3003 pub fn new(
3005 candidate: CommittedCandidateReceiptV2<H>,
3006 validity_votes: Vec<ValidityAttestation>,
3007 validator_indices: BitVec<u8, bitvec::order::Lsb0>,
3008 core_index: CoreIndex,
3009 ) -> Self {
3010 let mut instance = Self { candidate, validity_votes, validator_indices };
3011 instance.inject_core_index(core_index);
3012 instance
3013 }
3014
3015 pub fn candidate(&self) -> &CommittedCandidateReceiptV2<H> {
3017 &self.candidate
3018 }
3019
3020 #[cfg(feature = "test")]
3023 pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2<H> {
3024 &mut self.candidate
3025 }
3026 pub fn descriptor(&self) -> &CandidateDescriptorV2<H> {
3028 &self.candidate.descriptor
3029 }
3030
3031 #[cfg(feature = "test")]
3033 pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2<H> {
3034 &mut self.candidate.descriptor
3035 }
3036
3037 pub fn validity_votes(&self) -> &[ValidityAttestation] {
3039 &self.validity_votes
3040 }
3041
3042 pub fn validity_votes_mut(&mut self) -> &mut Vec<ValidityAttestation> {
3044 &mut self.validity_votes
3045 }
3046
3047 pub fn hash(&self) -> CandidateHash
3049 where
3050 H: Clone + Encode,
3051 {
3052 self.candidate.to_plain().hash()
3053 }
3054
3055 pub fn receipt(&self) -> CandidateReceiptV2<H>
3057 where
3058 H: Clone,
3059 {
3060 self.candidate.to_plain()
3061 }
3062
3063 #[cfg(feature = "test")]
3065 pub fn raw_validator_indices(&self) -> BitVec<u8, bitvec::order::Lsb0> {
3066 self.validator_indices.clone()
3067 }
3068
3069 pub fn validator_indices_and_core_index(
3071 &self,
3072 ) -> (&BitSlice<u8, bitvec::order::Lsb0>, Option<CoreIndex>) {
3073 let core_idx_offset = self.validator_indices.len().saturating_sub(8);
3075 if core_idx_offset > 0 {
3076 let (validator_indices_slice, core_idx_slice) =
3077 self.validator_indices.split_at(core_idx_offset);
3078 return (validator_indices_slice, Some(CoreIndex(core_idx_slice.load::<u8>() as u32)));
3079 }
3080
3081 (&self.validator_indices, None)
3082 }
3083
3084 fn inject_core_index(&mut self, core_index: CoreIndex) {
3086 let core_index_to_inject: BitVec<u8, bitvec::order::Lsb0> =
3087 BitVec::from_vec(vec![core_index.0 as u8]);
3088 self.validator_indices.extend(core_index_to_inject);
3089 }
3090
3091 pub fn set_validator_indices_and_core_index(
3093 &mut self,
3094 new_indices: BitVec<u8, bitvec::order::Lsb0>,
3095 maybe_core_index: Option<CoreIndex>,
3096 ) {
3097 self.validator_indices = new_indices;
3098
3099 if let Some(core_index) = maybe_core_index {
3100 self.inject_core_index(core_index);
3101 }
3102 }
3103}
3104
3105#[derive(Clone, Encode, Decode, Debug, TypeInfo)]
3107#[cfg_attr(feature = "std", derive(PartialEq))]
3108pub struct ScrapedOnChainVotes<H: Encode + Decode = Hash> {
3109 pub session: SessionIndex,
3111 pub backing_validators_per_candidate:
3114 Vec<(CandidateReceiptV2<H>, Vec<(ValidatorIndex, ValidityAttestation)>)>,
3115 pub disputes: MultiDisputeStatementSet,
3119}
3120
3121#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3123#[cfg_attr(feature = "std", derive(PartialEq))]
3124pub struct OccupiedCore<H = Hash, N = BlockNumber> {
3125 pub next_up_on_available: Option<ScheduledCore>,
3129 pub occupied_since: N,
3131 pub time_out_at: N,
3133 pub next_up_on_time_out: Option<ScheduledCore>,
3137 pub availability: BitVec<u8, bitvec::order::Lsb0>,
3141 pub group_responsible: GroupIndex,
3143 pub candidate_hash: CandidateHash,
3145 pub candidate_descriptor: CandidateDescriptorV2<H>,
3147}
3148
3149impl<H, N> OccupiedCore<H, N> {
3150 pub fn para_id(&self) -> Id {
3152 self.candidate_descriptor.para_id
3153 }
3154}
3155
3156#[derive(Clone, Encode, Decode, TypeInfo, Debug)]
3158#[cfg_attr(feature = "std", derive(PartialEq))]
3159pub enum CoreState<H = Hash, N = BlockNumber> {
3160 #[codec(index = 0)]
3162 Occupied(OccupiedCore<H, N>),
3163 #[codec(index = 1)]
3169 Scheduled(ScheduledCore),
3170 #[codec(index = 2)]
3174 Free,
3175}
3176
3177impl<N> CoreState<N> {
3178 #[deprecated(
3183 note = "`para_id` will be removed. Use `ClaimQueue` to query the scheduled `para_id` instead."
3184 )]
3185 pub fn para_id(&self) -> Option<Id> {
3186 match self {
3187 Self::Occupied(ref core) => core.next_up_on_available.as_ref().map(|n| n.para_id),
3188 Self::Scheduled(core) => Some(core.para_id),
3189 Self::Free => None,
3190 }
3191 }
3192
3193 pub fn is_occupied(&self) -> bool {
3195 matches!(self, Self::Occupied(_))
3196 }
3197}
3198
3199pub type TransposedClaimQueue = BTreeMap<ParaId, BTreeMap<u8, BTreeSet<CoreIndex>>>;
3201
3202pub fn transpose_claim_queue(
3205 claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>,
3206) -> TransposedClaimQueue {
3207 let mut per_para_claim_queue = BTreeMap::new();
3208
3209 for (core, paras) in claim_queue {
3210 for (depth, para) in paras.into_iter().enumerate() {
3212 let depths: &mut BTreeMap<u8, BTreeSet<CoreIndex>> =
3213 per_para_claim_queue.entry(para).or_insert_with(|| Default::default());
3214
3215 depths.entry(depth as u8).or_default().insert(core);
3216 }
3217 }
3218
3219 per_para_claim_queue
3220}
3221
3222#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
3225pub enum DisputeOffenceKind {
3226 #[codec(index = 0)]
3229 ForInvalidBacked,
3230 #[codec(index = 1)]
3233 AgainstValid,
3234 #[codec(index = 2)]
3237 ForInvalidApproved,
3238}
3239
3240impl From<super::v9::slashing::SlashingOffenceKind> for DisputeOffenceKind {
3244 fn from(value: super::v9::slashing::SlashingOffenceKind) -> Self {
3245 match value {
3246 super::v9::slashing::SlashingOffenceKind::ForInvalid => Self::ForInvalidBacked,
3247 super::v9::slashing::SlashingOffenceKind::AgainstValid => Self::AgainstValid,
3248 }
3249 }
3250}
3251
3252impl TryFrom<DisputeOffenceKind> for super::v9::slashing::SlashingOffenceKind {
3254 type Error = ();
3255
3256 fn try_from(value: DisputeOffenceKind) -> Result<Self, Self::Error> {
3257 match value {
3258 DisputeOffenceKind::ForInvalidBacked => Ok(Self::ForInvalid),
3259 DisputeOffenceKind::AgainstValid => Ok(Self::AgainstValid),
3260 DisputeOffenceKind::ForInvalidApproved => Err(()),
3261 }
3262 }
3263}
3264
3265#[cfg(test)]
3266pub mod tests {
3268 use super::*;
3269
3270 #[test]
3271 fn group_rotation_info_calculations() {
3272 let info =
3273 GroupRotationInfo { session_start_block: 10u32, now: 15, group_rotation_frequency: 5 };
3274
3275 assert_eq!(info.next_rotation_at(), 20);
3276 assert_eq!(info.last_rotation_at(), 15);
3277 }
3278
3279 #[test]
3280 fn group_for_core_is_core_for_group() {
3281 for cores in 1..=256 {
3282 for rotations in 0..(cores * 2) {
3283 let info = GroupRotationInfo {
3284 session_start_block: 0u32,
3285 now: rotations,
3286 group_rotation_frequency: 1,
3287 };
3288
3289 for core in 0..cores {
3290 let group = info.group_for_core(CoreIndex(core), cores as usize);
3291 assert_eq!(info.core_for_group(group, cores as usize).0, core);
3292 }
3293 }
3294 }
3295 }
3296
3297 #[test]
3298 fn test_byzantine_threshold() {
3299 assert_eq!(byzantine_threshold(0), 0);
3300 assert_eq!(byzantine_threshold(1), 0);
3301 assert_eq!(byzantine_threshold(2), 0);
3302 assert_eq!(byzantine_threshold(3), 0);
3303 assert_eq!(byzantine_threshold(4), 1);
3304 assert_eq!(byzantine_threshold(5), 1);
3305 assert_eq!(byzantine_threshold(6), 1);
3306 assert_eq!(byzantine_threshold(7), 2);
3307 }
3308
3309 #[test]
3310 fn test_supermajority_threshold() {
3311 assert_eq!(supermajority_threshold(0), 0);
3312 assert_eq!(supermajority_threshold(1), 1);
3313 assert_eq!(supermajority_threshold(2), 2);
3314 assert_eq!(supermajority_threshold(3), 3);
3315 assert_eq!(supermajority_threshold(4), 3);
3316 assert_eq!(supermajority_threshold(5), 4);
3317 assert_eq!(supermajority_threshold(6), 5);
3318 assert_eq!(supermajority_threshold(7), 5);
3319 }
3320
3321 #[test]
3322 fn balance_bigger_than_usize() {
3323 let zero_b: Balance = 0;
3324 let zero_u: usize = 0;
3325
3326 assert!(zero_b.leading_zeros() >= zero_u.leading_zeros());
3327 }
3328
3329 fn make_v2_descriptor() -> CandidateDescriptorV2 {
3330 CandidateDescriptorV2::new(
3331 Id::from(1u32),
3332 Hash::repeat_byte(1),
3333 CoreIndex(0),
3334 1,
3335 Hash::repeat_byte(2),
3336 Hash::repeat_byte(3),
3337 Hash::repeat_byte(4),
3338 Hash::repeat_byte(5),
3339 ValidationCodeHash::from(Hash::repeat_byte(6)),
3340 )
3341 }
3342
3343 fn make_v3_descriptor() -> CandidateDescriptorV2 {
3344 CandidateDescriptorV2::new_v3(
3345 Id::from(1u32),
3346 Hash::repeat_byte(1),
3347 CoreIndex(0),
3348 1, 1, Hash::repeat_byte(2),
3351 Hash::repeat_byte(3),
3352 Hash::repeat_byte(4),
3353 Hash::repeat_byte(5),
3354 ValidationCodeHash::from(Hash::repeat_byte(6)),
3355 Hash::repeat_byte(7), )
3357 }
3358
3359 #[test]
3360 fn check_version_acceptance_v1_consistent() {
3361 let mut desc = make_v2_descriptor();
3364 desc.reserved1[0] = 0xFF;
3367
3368 assert_eq!(desc.version(), CandidateDescriptorVersion::V1);
3369 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3370 assert!(desc.check_version_consistency());
3371
3372 assert!(desc.check_version_acceptance(false).is_ok());
3373 assert!(desc.check_version_acceptance(true).is_ok());
3374 }
3375
3376 #[test]
3377 fn check_version_acceptance_v2_consistent() {
3378 let desc = make_v2_descriptor();
3380
3381 assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3382 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V2);
3383 assert!(desc.check_version_consistency());
3384
3385 assert!(desc.check_version_acceptance(false).is_ok());
3386 assert!(desc.check_version_acceptance(true).is_ok());
3387 }
3388
3389 #[test]
3390 fn check_version_acceptance_v3_when_enabled() {
3391 let desc = make_v3_descriptor();
3393
3394 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3395 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3396 assert!(!desc.check_version_consistency());
3397
3398 assert!(desc.check_version_acceptance(true).is_ok());
3399 }
3400
3401 #[test]
3402 fn check_version_acceptance_v3_when_disabled() {
3403 let desc = make_v3_descriptor();
3407
3408 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3409 assert_eq!(
3410 desc.check_version_acceptance(false),
3411 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3412 );
3413 }
3414
3415 #[test]
3416 fn check_version_acceptance_ambiguous_rejected() {
3417 let mut desc = make_v2_descriptor();
3420 desc.reserved1[16] = 0xFF; assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3423 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3424 assert!(!desc.check_version_consistency());
3425
3426 assert_eq!(
3428 desc.check_version_acceptance(false),
3429 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3430 );
3431 assert_eq!(
3432 desc.check_version_acceptance(true),
3433 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3434 );
3435 }
3436
3437 #[test]
3438 fn check_version_consistency_v3_expected_disagreement() {
3439 let desc = make_v3_descriptor();
3442
3443 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3444 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3445 assert!(!desc.check_version_consistency());
3446 assert!(desc.check_version_acceptance(true).is_ok());
3448 }
3449
3450 #[test]
3451 fn v3_feature_activation_changes_descriptor_interpretation() {
3452 let desc = make_v3_descriptor();
3453
3454 assert_eq!(desc.version(), CandidateDescriptorVersion::V3);
3456 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3457
3458 assert_eq!(desc.version_for_candidate_validation(false), CandidateDescriptorVersion::V1,);
3460 assert_eq!(
3461 desc.scheduling_parent_for_candidate_validation(false),
3462 Hash::repeat_byte(1), );
3464 assert_eq!(
3465 desc.scheduling_session_for_candidate_validation(false),
3466 None,
3467 "V1 has no embedded session — must be fetched from runtime",
3468 );
3469
3470 assert_eq!(desc.version_for_candidate_validation(true), CandidateDescriptorVersion::V3,);
3472 assert_eq!(
3473 desc.scheduling_parent_for_candidate_validation(true),
3474 Hash::repeat_byte(7), );
3476 assert_eq!(
3477 desc.scheduling_session_for_candidate_validation(true),
3478 Some(1), );
3480 }
3481
3482 #[test]
3483 fn check_version_acceptance_ambiguous_scheduling_parent_nonzero() {
3484 let mut desc = make_v2_descriptor();
3488 desc.scheduling_parent = Hash::repeat_byte(0xAB);
3489
3490 assert_eq!(desc.version(), CandidateDescriptorVersion::V2);
3491 assert_eq!(desc.version_old_rules(), CandidateDescriptorVersion::V1);
3492 assert!(!desc.check_version_consistency());
3493
3494 assert_eq!(
3495 desc.check_version_acceptance(false),
3496 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3497 );
3498 assert_eq!(
3499 desc.check_version_acceptance(true),
3500 Err(CandidateDescriptorVersionCheckError::Inconsistency)
3501 );
3502 }
3503}