referrerpolicy=no-referrer-when-downgrade

cumulus_primitives_core/
scheduling.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5//! V3 scheduling types for low-latency parachain block production.
6//!
7//! V3 candidates separate the relay parent (execution context) from the scheduling
8//! parent (a recent relay chain tip used for core assignment). This enables building
9//! on older relay parents while still being scheduled based on recent relay state.
10//!
11//! # Resubmission
12//!
13//! When a candidate fails to get backed in time, a different collator can resubmit
14//! it with a new `scheduling_parent` (fresh relay tip) without re-executing the blocks.
15//! The `relay_parent` stays the same since the execution context hasn't changed.
16//!
17//! For resubmission, `signed_scheduling_info` must be provided. The resubmitting
18//! collator signs the core selection, proving they are the eligible author for the
19//! slot derived from the `internal_scheduling_parent`.
20
21use alloc::vec::Vec;
22use codec::{Decode, Encode};
23use polkadot_primitives::{ApprovedPeerId, CoreSelector, Header as RelayChainHeader};
24use sp_runtime::traits::{BlakeTwo256, Hash as HashT};
25
26/// Payload signed by a collator for resubmission.
27///
28/// This binds the core selection and reputation-credit peer to a specific internal
29/// scheduling parent, preventing replay attacks across different scheduling contexts.
30#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)]
31pub struct SchedulingInfoPayload {
32	/// Which core to use (indexes into the parachain's assigned cores).
33	pub core_selector: CoreSelector,
34	/// The claim queue offset.
35	pub claim_queue_offset: u8,
36	/// Peer ID to receive reputation credit for successful collation delivery.
37	pub peer_id: ApprovedPeerId,
38	/// The internal scheduling parent whom's slot decides the
39	/// eligible block author that must sign the payload.
40	pub internal_scheduling_parent: polkadot_primitives::Hash,
41}
42
43/// Signed scheduling information for candidate resubmission.
44///
45/// When a collator resubmits a candidate (with a newer `scheduling_parent` but same
46/// `relay_parent`), they must sign the core selection to prove eligibility for the
47/// slot at `internal_scheduling_parent`.
48///
49/// The `claim_queue_offset` is derived from the runtime's `relay_parent_offset`
50/// configuration and is not part of this struct - it cannot be overridden by the
51/// collator.
52#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)]
53pub struct SignedSchedulingInfo {
54	/// The scheduling information.
55	pub payload: SchedulingInfoPayload,
56	/// Signature by the eligible collator over the SCALE-encoded
57	/// `SchedulingInfoPayload`.
58	///
59	/// Stored as a fixed 64-byte blob so the verifier can decode it as either an sr25519
60	/// or ed25519 signature. Both schemes produce 64-byte signatures.
61	pub signature: [u8; 64],
62}
63
64impl SchedulingInfoPayload {
65	/// Create a new scheduling info payload.
66	pub fn new(
67		core_selector: CoreSelector,
68		claim_queue_offset: u8,
69		peer_id: ApprovedPeerId,
70		internal_scheduling_parent: polkadot_primitives::Hash,
71	) -> Self {
72		Self { core_selector, claim_queue_offset, peer_id, internal_scheduling_parent }
73	}
74}
75
76/// V3 scheduling proof included in the POV.
77///
78/// Provides the ancestry from scheduling_parent back to the internal scheduling
79/// parent. The PVF validates this against the relay_parent and scheduling_parent
80/// from the candidate descriptor extension.
81#[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)]
82pub struct SchedulingProof {
83	/// Relay chain headers proving ancestry from scheduling_parent backward.
84	///
85	/// Forms a chain where each header's parent_hash equals the next header's hash.
86	/// The first header's hash must equal the candidate's scheduling_parent.
87	/// The last header's parent_hash is the internal scheduling parent.
88	/// Length is defined by the parachain runtime config (RelayParentOffset).
89	pub header_chain: Vec<RelayChainHeader>,
90	/// The relay chain header at `internal_scheduling_parent`. Its hash must equal the
91	/// `internal_scheduling_parent` derived from `header_chain` (the parent of the chain's
92	/// last header, or `scheduling_parent` if the chain is empty).
93	pub internal_scheduling_parent_header: RelayChainHeader,
94	/// Signed scheduling info for core selection override.
95	///
96	/// - `None` with `relay_parent == internal_scheduling_parent`: Initial submission. Core
97	///   selection comes from the parachain block's UMP signals.
98	///
99	/// - `Some` with `relay_parent == internal_scheduling_parent`: Initial submission with
100	///   explicit core selection. This is optional but legal. Collators should refuse to
101	///   acknowledge blocks with invalid scheduling info, so providing a signature is not required
102	///   for initial submissions.
103	///
104	/// - `Some` with `relay_parent != internal_scheduling_parent`: Resubmission (required). The
105	///   resubmitting collator signs the core selection, overriding the block's UMP signals.
106	///   Signature is verified against the eligible author for the slot at
107	///   `internal_scheduling_parent`.
108	pub signed_scheduling_info: Option<SignedSchedulingInfo>,
109}
110
111impl SchedulingProof {
112	/// Derive the scheduling parent hash.
113	///
114	/// Returns the hash of the first/newest header in `header_chain` if non-empty, otherwise
115	/// falls back to `internal_scheduling_parent_header.hash()` (the ISP coincides with the
116	/// scheduling parent when the parachain runs with `relay_parent_offset = 0`).
117	pub fn scheduling_parent(&self) -> polkadot_primitives::Hash {
118		self.header_chain
119			.first()
120			.map(BlakeTwo256::hash_of)
121			.unwrap_or_else(|| self.internal_scheduling_parent_header.hash())
122	}
123}
124
125/// Verifier for V3 scheduling.
126///
127/// Reports whether V3 scheduling is enabled for the parachain (via
128/// [`Self::V3_SCHEDULING_ENABLED`]) and, when it is, verifies the [`SignedSchedulingInfo`]
129/// attached to a candidate (via [`Self::verify`]).
130pub trait VerifySchedulingSignature {
131	/// Whether V3 scheduling validation is enabled.
132	const V3_SCHEDULING_ENABLED: bool;
133
134	/// Verifies `signed_info` against `internal_scheduling_parent_header`.
135	fn verify(
136		signed_info: &SignedSchedulingInfo,
137		internal_scheduling_parent_header: &RelayChainHeader,
138	) -> bool;
139}
140
141/// Default no-op wiring: V3 scheduling disabled, scheduling info accepted unconditionally.
142///
143/// Replacing it with a real verifier should also turn V3 on.
144impl VerifySchedulingSignature for () {
145	const V3_SCHEDULING_ENABLED: bool = false;
146
147	fn verify(
148		_signed_info: &SignedSchedulingInfo,
149		_internal_scheduling_parent_header: &RelayChainHeader,
150	) -> bool {
151		true
152	}
153}