1#![deny(missing_docs)]
24
25use std::{env::var, pin::Pin, sync::LazyLock};
26
27use bounded_vec::BoundedVec;
28use codec::{Decode, Encode, Error as CodecError, Input};
29use futures::Future;
30use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
31
32use polkadot_primitives::{
33 BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, ChunkIndex, CollatorPair,
34 CommittedCandidateReceiptError, CommittedCandidateReceiptV2 as CommittedCandidateReceipt,
35 CompactStatement, CoreIndex, EncodeAs, Hash, HashT, HeadData, Id as ParaId,
36 PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode,
37 ValidationCodeHash, MAX_CODE_SIZE, MAX_POV_SIZE,
38};
39pub use sp_consensus_babe::{
40 AllowedSlots as BabeAllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch,
41 Randomness as BabeRandomness,
42};
43
44pub use polkadot_parachain_primitives::primitives::{
45 BlockData, HorizontalMessages, UpwardMessages,
46};
47
48pub mod approval;
49
50pub mod disputes;
52pub use disputes::{
53 dispute_is_inactive, CandidateVotes, DisputeMessage, DisputeMessageCheckError, DisputeStatus,
54 InvalidDisputeVote, SignedDisputeStatement, Timestamp, UncheckedDisputeMessage,
55 ValidDisputeVote, ACTIVE_DURATION_SECS,
56};
57
58pub const NODE_VERSION: &'static str = "1.22.3";
64
65const MERKLE_NODE_MAX_SIZE: usize = 512 + 100;
69const MERKLE_PROOF_MAX_DEPTH: usize = 8;
71
72#[deprecated(
74 note = "`VALIDATION_CODE_BOMB_LIMIT` will be removed. Use `validation_code_bomb_limit`
75 runtime API to retrieve the value from the runtime"
76)]
77pub const VALIDATION_CODE_BOMB_LIMIT: usize = (MAX_CODE_SIZE * 4u32) as usize;
78
79pub const POV_BOMB_LIMIT: usize = (MAX_POV_SIZE * 4u32) as usize;
81
82pub static DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION: LazyLock<BlockNumber> =
108 LazyLock::new(|| {
109 if var("ZOMBIE_DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION").is_ok() {
110 1
111 } else {
112 10
113 }
114 });
115
116pub const MAX_FINALITY_LAG: u32 = 500;
120
121#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
128pub struct SessionWindowSize(SessionIndex);
129
130#[macro_export]
131macro_rules! new_session_window_size {
133 (0) => {
134 compile_error!("Must be non zero");
135 };
136 (0_u32) => {
137 compile_error!("Must be non zero");
138 };
139 (0 as u32) => {
140 compile_error!("Must be non zero");
141 };
142 (0 as _) => {
143 compile_error!("Must be non zero");
144 };
145 ($l:literal) => {
146 SessionWindowSize::unchecked_new($l as _)
147 };
148}
149
150pub const DISPUTE_WINDOW: SessionWindowSize = new_session_window_size!(6);
155
156impl SessionWindowSize {
157 pub fn get(self) -> SessionIndex {
159 self.0
160 }
161
162 #[doc(hidden)]
167 pub const fn unchecked_new(size: SessionIndex) -> Self {
168 Self(size)
169 }
170}
171
172pub type BlockWeight = u32;
174
175#[derive(Clone, PartialEq, Eq, Encode, Decode)]
182pub enum Statement {
183 #[codec(index = 1)]
185 Seconded(CommittedCandidateReceipt),
186 #[codec(index = 2)]
188 Valid(CandidateHash),
189}
190
191impl std::fmt::Debug for Statement {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 match self {
194 Statement::Seconded(seconded) => write!(f, "Seconded: {:?}", seconded.descriptor),
195 Statement::Valid(hash) => write!(f, "Valid: {:?}", hash),
196 }
197 }
198}
199
200impl Statement {
201 pub fn candidate_hash(&self) -> CandidateHash {
206 match *self {
207 Statement::Valid(ref h) => *h,
208 Statement::Seconded(ref c) => c.hash(),
209 }
210 }
211
212 pub fn to_compact(&self) -> CompactStatement {
215 match *self {
216 Statement::Seconded(ref c) => CompactStatement::Seconded(c.hash()),
217 Statement::Valid(hash) => CompactStatement::Valid(hash),
218 }
219 }
220
221 pub fn supply_pvd(self, pvd: PersistedValidationData) -> StatementWithPVD {
223 match self {
224 Statement::Seconded(c) => StatementWithPVD::Seconded(c, pvd),
225 Statement::Valid(hash) => StatementWithPVD::Valid(hash),
226 }
227 }
228}
229
230impl From<&'_ Statement> for CompactStatement {
231 fn from(stmt: &Statement) -> Self {
232 stmt.to_compact()
233 }
234}
235
236impl EncodeAs<CompactStatement> for Statement {
237 fn encode_as(&self) -> Vec<u8> {
238 self.to_compact().encode()
239 }
240}
241
242#[derive(Clone, PartialEq, Eq)]
245pub enum StatementWithPVD {
246 Seconded(CommittedCandidateReceipt, PersistedValidationData),
248 Valid(CandidateHash),
250}
251
252impl std::fmt::Debug for StatementWithPVD {
253 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 match self {
255 StatementWithPVD::Seconded(seconded, _) => {
256 write!(f, "Seconded: {:?}", seconded.descriptor)
257 },
258 StatementWithPVD::Valid(hash) => write!(f, "Valid: {:?}", hash),
259 }
260 }
261}
262
263impl StatementWithPVD {
264 pub fn candidate_hash(&self) -> CandidateHash {
269 match *self {
270 StatementWithPVD::Valid(ref h) => *h,
271 StatementWithPVD::Seconded(ref c, _) => c.hash(),
272 }
273 }
274
275 pub fn to_compact(&self) -> CompactStatement {
278 match *self {
279 StatementWithPVD::Seconded(ref c, _) => CompactStatement::Seconded(c.hash()),
280 StatementWithPVD::Valid(hash) => CompactStatement::Valid(hash),
281 }
282 }
283
284 pub fn drop_pvd(self) -> Statement {
286 match self {
287 StatementWithPVD::Seconded(c, _) => Statement::Seconded(c),
288 StatementWithPVD::Valid(c_h) => Statement::Valid(c_h),
289 }
290 }
291
292 pub fn drop_pvd_from_signed(signed: SignedFullStatementWithPVD) -> SignedFullStatement {
295 signed
296 .convert_to_superpayload_with(|s| s.drop_pvd())
297 .expect("persisted_validation_data doesn't affect encode_as; qed")
298 }
299
300 pub fn signed_to_compact(signed: SignedFullStatementWithPVD) -> Signed<CompactStatement> {
303 signed
304 .convert_to_superpayload_with(|s| s.to_compact())
305 .expect("doesn't affect encode_as; qed")
306 }
307}
308
309impl From<&'_ StatementWithPVD> for CompactStatement {
310 fn from(stmt: &StatementWithPVD) -> Self {
311 stmt.to_compact()
312 }
313}
314
315impl EncodeAs<CompactStatement> for StatementWithPVD {
316 fn encode_as(&self) -> Vec<u8> {
317 self.to_compact().encode()
318 }
319}
320
321pub type SignedFullStatement = Signed<Statement, CompactStatement>;
328
329pub type UncheckedSignedFullStatement = UncheckedSigned<Statement, CompactStatement>;
331
332pub type SignedFullStatementWithPVD = Signed<StatementWithPVD, CompactStatement>;
338
339#[derive(Debug)]
341pub enum InvalidCandidate {
342 ExecutionError(String),
344 InvalidOutputs,
346 Timeout,
348 ParamsTooLarge(u64),
350 CodeTooLarge(u64),
352 PoVDecompressionFailure,
354 BadReturn,
356 BadParent,
358 PoVHashMismatch,
360 BadSignature,
362 ParaHeadHashMismatch,
364 CodeHashMismatch,
366 CommitmentsHashMismatch,
368 InvalidSchedulingSession,
370 InvalidRelayParentSession,
372 InvalidUMPSignals(CommittedCandidateReceiptError),
374}
375
376#[derive(Debug)]
378pub enum ValidationResult {
379 Valid(CandidateCommitments, PersistedValidationData),
382 Invalid(InvalidCandidate),
384}
385
386#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug)]
388pub struct PoV {
389 pub block_data: BlockData,
391}
392
393impl PoV {
394 pub fn hash(&self) -> Hash {
396 BlakeTwo256::hash_of(self)
397 }
398}
399
400#[derive(Clone, Encode, Decode)]
402#[cfg(not(target_os = "unknown"))]
403pub enum MaybeCompressedPoV {
404 Raw(PoV),
406 Compressed(PoV),
408}
409
410#[cfg(not(target_os = "unknown"))]
411impl std::fmt::Debug for MaybeCompressedPoV {
412 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
413 let (variant, size) = match self {
414 MaybeCompressedPoV::Raw(pov) => ("Raw", pov.block_data.0.len()),
415 MaybeCompressedPoV::Compressed(pov) => ("Compressed", pov.block_data.0.len()),
416 };
417
418 write!(f, "{} PoV ({} bytes)", variant, size)
419 }
420}
421
422#[cfg(not(target_os = "unknown"))]
423impl MaybeCompressedPoV {
424 pub fn into_compressed(self) -> PoV {
428 match self {
429 Self::Raw(raw) => maybe_compress_pov(raw),
430 Self::Compressed(compressed) => compressed,
431 }
432 }
433}
434
435#[derive(Debug, Clone, Encode, Decode)]
442#[cfg(not(target_os = "unknown"))]
443pub struct Collation<BlockNumber = polkadot_primitives::BlockNumber> {
444 pub upward_messages: UpwardMessages,
446 pub horizontal_messages: HorizontalMessages,
448 pub new_validation_code: Option<ValidationCode>,
450 pub head_data: HeadData,
452 pub proof_of_validity: MaybeCompressedPoV,
454 pub processed_downward_messages: u32,
456 pub hrmp_watermark: BlockNumber,
459}
460
461#[derive(Debug)]
463#[cfg(not(target_os = "unknown"))]
464pub struct CollationSecondedSignal {
465 pub scheduling_parent: Hash,
469 pub statement: SignedFullStatement,
473}
474
475#[cfg(not(target_os = "unknown"))]
477pub struct CollationResult {
478 pub collation: Collation,
480 pub result_sender: Option<futures::channel::oneshot::Sender<CollationSecondedSignal>>,
486}
487
488#[cfg(not(target_os = "unknown"))]
489impl CollationResult {
490 pub fn into_inner(
492 self,
493 ) -> (Collation, Option<futures::channel::oneshot::Sender<CollationSecondedSignal>>) {
494 (self.collation, self.result_sender)
495 }
496}
497
498#[cfg(not(target_os = "unknown"))]
506pub type CollatorFn = Box<
507 dyn Fn(
508 Hash,
509 &PersistedValidationData,
510 ) -> Pin<Box<dyn Future<Output = Option<CollationResult>> + Send>>
511 + Send
512 + Sync,
513>;
514
515#[cfg(not(target_os = "unknown"))]
517pub struct CollationGenerationConfig {
518 pub key: CollatorPair,
520 pub collator: Option<CollatorFn>,
525 pub para_id: ParaId,
527}
528
529#[cfg(not(target_os = "unknown"))]
530impl std::fmt::Debug for CollationGenerationConfig {
531 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
532 write!(f, "CollationGenerationConfig {{ ... }}")
533 }
534}
535
536#[derive(Debug)]
538pub struct SubmitCollationParams {
539 pub relay_parent: Hash,
541 pub collation: Collation,
543 pub validation_code_hash: ValidationCodeHash,
545 pub result_sender: Option<futures::channel::oneshot::Sender<CollationSecondedSignal>>,
551 pub core_index: CoreIndex,
553 pub scheduling_parent: Option<Hash>,
559 pub session_index: SessionIndex,
562 pub validation_data: PersistedValidationData,
565}
566
567#[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)]
569pub struct AvailableData {
570 pub pov: std::sync::Arc<PoV>,
572 pub validation_data: PersistedValidationData,
574}
575
576#[derive(PartialEq, Eq, Clone, Debug, Hash)]
578pub struct Proof(BoundedVec<BoundedVec<u8, 1, MERKLE_NODE_MAX_SIZE>, 1, MERKLE_PROOF_MAX_DEPTH>);
579
580impl Proof {
581 pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
583 self.0.iter().map(|v| v.as_slice())
584 }
585
586 pub fn dummy_proof() -> Proof {
590 Proof(BoundedVec::from_vec(vec![BoundedVec::from_vec(vec![0]).unwrap()]).unwrap())
591 }
592}
593
594#[derive(thiserror::Error, Debug)]
596pub enum MerkleProofError {
597 #[error("Merkle max proof depth exceeded {0} > {} .", MERKLE_PROOF_MAX_DEPTH)]
598 MerkleProofDepthExceeded(usize),
600
601 #[error("Merkle node max size exceeded {0} > {} .", MERKLE_NODE_MAX_SIZE)]
602 MerkleProofNodeSizeExceeded(usize),
604}
605
606impl TryFrom<Vec<Vec<u8>>> for Proof {
607 type Error = MerkleProofError;
608
609 fn try_from(input: Vec<Vec<u8>>) -> Result<Self, Self::Error> {
610 if input.len() > MERKLE_PROOF_MAX_DEPTH {
611 return Err(Self::Error::MerkleProofDepthExceeded(input.len()));
612 }
613 let mut out = Vec::new();
614 for element in input.into_iter() {
615 let length = element.len();
616 let data: BoundedVec<u8, 1, MERKLE_NODE_MAX_SIZE> = BoundedVec::from_vec(element)
617 .map_err(|_| Self::Error::MerkleProofNodeSizeExceeded(length))?;
618 out.push(data);
619 }
620 Ok(Proof(BoundedVec::from_vec(out).expect("Buffer size is deterined above. qed")))
621 }
622}
623
624impl Decode for Proof {
625 fn decode<I: Input>(value: &mut I) -> Result<Self, CodecError> {
626 let temp: Vec<Vec<u8>> = Decode::decode(value)?;
627 let mut out = Vec::new();
628 for element in temp.into_iter() {
629 let bounded_temp: Result<BoundedVec<u8, 1, MERKLE_NODE_MAX_SIZE>, CodecError> =
630 BoundedVec::from_vec(element)
631 .map_err(|_| "Inner node exceeds maximum node size.".into());
632 out.push(bounded_temp?);
633 }
634 BoundedVec::from_vec(out)
635 .map(Self)
636 .map_err(|_| "Merkle proof depth exceeds maximum trie depth".into())
637 }
638}
639
640impl Encode for Proof {
641 fn size_hint(&self) -> usize {
642 MERKLE_NODE_MAX_SIZE * MERKLE_PROOF_MAX_DEPTH
643 }
644
645 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
646 let temp = self.0.iter().map(|v| v.as_vec()).collect::<Vec<_>>();
647 temp.using_encoded(f)
648 }
649}
650
651impl Serialize for Proof {
652 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
653 where
654 S: Serializer,
655 {
656 serializer.serialize_bytes(&self.encode())
657 }
658}
659
660impl<'de> Deserialize<'de> for Proof {
661 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
662 where
663 D: Deserializer<'de>,
664 {
665 let s = Vec::<u8>::deserialize(deserializer)?;
667 let mut slice = s.as_slice();
668 Decode::decode(&mut slice).map_err(de::Error::custom)
669 }
670}
671
672#[derive(PartialEq, Eq, Clone, Encode, Decode, Serialize, Deserialize, Debug, Hash)]
674pub struct ErasureChunk {
675 pub chunk: Vec<u8>,
677 pub index: ChunkIndex,
679 pub proof: Proof,
681}
682
683impl ErasureChunk {
684 pub fn proof(&self) -> &Proof {
686 &self.proof
687 }
688}
689
690#[cfg(not(target_os = "unknown"))]
692pub fn maybe_compress_pov(pov: PoV) -> PoV {
693 let PoV { block_data: BlockData(raw) } = pov;
694 let raw = sp_maybe_compressed_blob::compress_weakly(&raw, POV_BOMB_LIMIT).unwrap_or(raw);
695
696 let pov = PoV { block_data: BlockData(raw) };
697 pov
698}