From ce7591473e7dca96dd0afe6c99351e3a7c300ff1 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Fri, 22 May 2026 13:38:54 +0000 Subject: [PATCH 01/11] collation-generation: added `SubmitSegment` primitive Signed-off-by: Iulian Barbu --- polkadot/node/collation-generation/src/lib.rs | 6 +++ polkadot/node/primitives/src/lib.rs | 41 +++++++++++++++++++ polkadot/node/subsystem-types/src/messages.rs | 8 +++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index b523babed1848..8ef86392433a8 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -203,6 +203,12 @@ impl CollationGenerationSubsystem { false }, + Ok(FromOrchestra::Communication { + msg: CollationGenerationMessage::SubmitSegment(_params), + }) => { + // TODO: handle segment submission. + false + }, Ok(FromOrchestra::Signal(OverseerSignal::BlockFinalized(..))) => false, Err(err) => { gum::error!( diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 075b9013021f3..a0b8824b0ec58 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -553,6 +553,47 @@ pub struct SubmitCollationParams { pub validation_data: PersistedValidationData, } +/// A single collation in a segment submitted via `CollationGenerationMessage::SubmitSegment`. +#[derive(Debug)] +pub struct SegmentCollation { + /// The relay-parent the collation is built against. + pub relay_parent: Hash, + /// The collation itself (PoV and commitments). + pub collation: Collation, + /// The hash of the validation code the collation was created against. + pub validation_code_hash: ValidationCodeHash, + /// An optional result sender that should be informed about a successfully seconded collation. + /// + /// See [`SubmitCollationParams::result_sender`] for the same caveats. + pub result_sender: Option>, + /// The session index of the relay parent. Goes into the candidate descriptor. + /// Must be provided by the caller because the relay parent's state may be pruned. + pub session_index: SessionIndex, + /// The persisted validation data for this collation. The `parent_head` field must be set + /// to the correct parent head-data for the parablock being submitted. + pub validation_data: PersistedValidationData, +} + +/// Parameters for `CollationGenerationMessage::SubmitSegment`. +/// +/// Submits multiple collations that share a common scheduling parent and target core. Each +/// [`SegmentCollation`] in `collations` carries the fields that may differ between blocks of the +/// segment (relay parent, collation payload, validation data, etc.). +#[derive(Debug)] +pub struct SubmitSegmentParams { + /// The scheduling parent for V3 candidate descriptors, shared by all collations in the + /// segment. If set, every candidate descriptor will use this as the scheduling parent + /// (creating V3 descriptors). If `None`, each collation's `relay_parent` is used (V2 + /// descriptors). + /// + /// WARNING: Should only be set if the `CandidateReceiptV3` node feature is set. + pub scheduling_parent: Option, + /// The core index on which the resulting candidates should be backed. + pub core_index: CoreIndex, + /// The collations in this segment, in the order they should be submitted. + pub collations: Vec, +} + /// This is the data we keep available for each candidate included in the relay chain. #[derive(Clone, Encode, Decode, PartialEq, Eq, Debug)] pub struct AvailableData { diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index 7117f16999dc3..74821703b0799 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -39,7 +39,7 @@ use polkadot_node_primitives::{ AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, CollationSecondedSignal, DisputeMessage, DisputeStatus, ErasureChunk, PoV, SignedDisputeStatement, SignedFullStatement, SignedFullStatementWithPVD, SubmitCollationParams, - ValidationResult, + SubmitSegmentParams, ValidationResult, }; use polkadot_primitives::{ self, @@ -989,6 +989,12 @@ pub enum CollationGenerationMessage { /// /// If sent before `Initialize`, this will be ignored. SubmitCollation(SubmitCollationParams), + /// Submit a segment of collations that share a scheduling parent and target core. Each + /// collation is packaged into a signed [`CommittedCandidateReceipt`] and distributed to + /// validators, in the order they appear in [`SubmitSegmentParams::collations`]. + /// + /// If sent before `Initialize`, this will be ignored. + SubmitSegment(SubmitSegmentParams), } /// The result type of [`ApprovalVotingMessage::ImportAssignment`] request. From f9e9c1f8f951b0251025e0b57de62eb67bc26199 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Fri, 22 May 2026 16:48:44 +0000 Subject: [PATCH 02/11] cumulus: use CollatorSegmentMessage Signed-off-by: Iulian Barbu --- .../slot_based/block_builder_task.rs | 22 ++- .../collators/slot_based/collation_task.rs | 181 ++++++++++-------- .../aura/src/collators/slot_based/mod.rs | 39 ++-- 3 files changed, 135 insertions(+), 107 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 88d68611a4de5..26e88ac9b443d 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use super::CollatorMessage; +use super::{CollatorSegmentEntry, CollatorSegmentMessage}; use crate::{ collator::{self as collator_util, BuildBlockAndImportParams, Collator, SlotClaim}, collators::{ @@ -109,7 +109,7 @@ pub struct BuilderTaskParams< /// The generic collator service used to plug into this consensus engine. pub collator_service: CS, /// Channel to send built blocks to the collation task. - pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, + pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, /// Slot duration of the relay chain. pub relay_chain_slot_duration: Duration, /// Offset all time operations by this duration. @@ -561,7 +561,7 @@ struct BuildCollationParams< relay_client: &'a RelayClient, code_hash_provider: &'a CHP, slot_claim: &'a SlotClaim, - collator_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, + collator_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, collator: &'a mut Collator, allowed_pov_size: usize, core_info: CoreInfo, @@ -902,15 +902,17 @@ where "Sending out PoV" ); - if let Err(err) = collator_sender.unbounded_send(CollatorMessage { - relay_parent: relay_parent_hash, + if let Err(err) = collator_sender.unbounded_send(CollatorSegmentMessage { scheduling_proof, - parent_header: pov_parent_header.clone(), - blocks, - proof, - validation_code_hash, core_index, - validation_data, + entries: vec![CollatorSegmentEntry { + relay_parent: relay_parent_hash, + parent_header: pov_parent_header.clone(), + blocks, + proof, + validation_code_hash, + validation_data, + }], }) { tracing::error!(target: LOG_TARGET, ?err, "Unable to send block to collation task."); Err(()) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index 2ddff00c40421..0eeb3fe2646a6 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface; use cumulus_relay_chain_interface::RelayChainInterface; -use polkadot_node_primitives::{MaybeCompressedPoV, SubmitCollationParams}; +use polkadot_node_primitives::{MaybeCompressedPoV, SegmentCollation, SubmitSegmentParams}; use polkadot_node_subsystem::messages::CollationGenerationMessage; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; @@ -32,7 +32,7 @@ use crate::export_pov_to_path; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_runtime::traits::{Block as BlockT, Header}; -use super::CollatorMessage; +use super::CollatorSegmentMessage; const LOG_TARGET: &str = "aura::cumulus::collation_task"; @@ -49,7 +49,7 @@ pub struct Params { /// Collator service interface pub collator_service: CS, /// Receiver channel for communication with the block builder task. - pub collator_receiver: TracingUnboundedReceiver>, + pub collator_receiver: TracingUnboundedReceiver>, /// The handle from the special slot based block import. pub block_import_handle: super::SlotBasedBlockImportHandle, /// When set, the collator will export every produced `POV` to this folder. @@ -98,7 +98,7 @@ pub async fn run_collation_task( return; }; - handle_collation_message(message, &collator_service, &mut overseer_handle,relay_client.clone(),export_pov.clone()).await; + handle_segment_message(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; }, block_import_msg = block_import_handle.next().fuse() => { // TODO: Implement me. @@ -109,108 +109,119 @@ pub async fn run_collation_task( } } -/// Handle an incoming collation message from the block builder task. -/// This builds the collation from the [`CollatorMessage`] and submits it to -/// the collation-generation subsystem of the relay chain. -async fn handle_collation_message( - message: CollatorMessage, +/// Handle an incoming segment message from the block builder task. Each entry is built into a +/// [`SegmentCollation`] (one PoV / one candidate on the relay chain); the whole segment is +/// forwarded to the collation-generation subsystem as a single +/// [`CollationGenerationMessage::SubmitSegment`]. Entries that fail to build or whose session +/// lookup fails are skipped — they do not abort the whole segment. +async fn handle_segment_message( + message: CollatorSegmentMessage, collator_service: &impl CollatorServiceInterface, overseer_handle: &mut OverseerHandle, relay_client: RClient, export_pov: Option, ) { - let CollatorMessage { - scheduling_proof, - parent_header, - blocks, - proof, - validation_code_hash, - relay_parent, - core_index, - validation_data, - } = message; - - // Derive scheduling_parent from the proof (the ISP header's hash is used when the - // header chain is empty — that's the case with `relay_parent_offset = 0`). + let CollatorSegmentMessage { scheduling_proof, core_index, entries } = message; + + // Unlike the single-collation path, we cannot fall back to "the" relay_parent here — entries + // may have different relay parents. `None` means V2 descriptors for the whole segment. let scheduling_parent = scheduling_proof.as_ref().map(|p| p.scheduling_parent()); - let (collation, block_data) = match collator_service.build_multi_block_collation( - &parent_header, - blocks, - proof, - scheduling_proof, - ) { - Some(collation) => collation, - None => { - tracing::warn!(target: LOG_TARGET, ?core_index, "Unable to build collation."); - return; - }, - }; - block_data.log_size_info(); - - if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { - if let Some(pov_path) = export_pov { - if let Ok(Some(relay_parent_header)) = - relay_client.header(BlockId::Hash(relay_parent)).await - { - if let Some(header) = block_data.blocks().first().map(|b| b.header()) { - export_pov_to_path::( - pov_path.clone(), - pov.clone(), - header.hash(), - *header.number(), - parent_header.clone(), - relay_parent_header.state_root, - relay_parent_header.number, - validation_data.max_pov_size, - ); + let mut collations = Vec::with_capacity(entries.len()); + for entry in entries { + let relay_parent = entry.relay_parent; + let validation_code_hash = entry.validation_code_hash; + let validation_data = entry.validation_data; + let parent_header = entry.parent_header; + + let (collation, block_data) = match collator_service.build_multi_block_collation( + &parent_header, + entry.blocks, + entry.proof, + scheduling_proof.clone(), + ) { + Some(collation) => collation, + None => { + tracing::warn!(target: LOG_TARGET, ?core_index, ?relay_parent, "Unable to build collation for segment entry."); + continue; + }, + }; + + block_data.log_size_info(); + + if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { + if let Some(pov_path) = export_pov.clone() { + if let Ok(Some(relay_parent_header)) = + relay_client.header(BlockId::Hash(relay_parent)).await + { + if let Some(header) = block_data.blocks().first().map(|b| b.header()) { + export_pov_to_path::( + pov_path, + pov.clone(), + header.hash(), + *header.number(), + parent_header.clone(), + relay_parent_header.state_root, + relay_parent_header.number, + validation_data.max_pov_size, + ); + } + } else { + tracing::error!(target: LOG_TARGET, "Failed to get relay parent header from hash: {relay_parent:?}"); } - } else { - tracing::error!(target: LOG_TARGET, "Failed to get relay parent header from hash: {relay_parent:?}"); } + + tracing::info!( + target: LOG_TARGET, + block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), + "Compressed PoV size: {}kb", + pov.block_data.0.len() as f64 / 1024f64, + ); } - tracing::info!( + let session_index = match relay_client.session_index_for_child(relay_parent).await { + Ok(session_index) => session_index, + Err(err) => { + tracing::error!( + target: LOG_TARGET, + ?err, + ?relay_parent, + "Failed to fetch session index for segment entry.", + ); + continue; + }, + }; + + tracing::debug!( target: LOG_TARGET, + ?core_index, + ?relay_parent, block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), - "Compressed PoV size: {}kb", - pov.block_data.0.len() as f64 / 1024f64, + "Adding entry to segment for core.", ); - } - let session_index = match relay_client.session_index_for_child(relay_parent).await { - Ok(session_index) => session_index, - Err(err) => { - tracing::error!( - target: LOG_TARGET, - ?err, - ?relay_parent, - "Failed to fetch session index." - ); - return; - }, - }; + collations.push(SegmentCollation { + relay_parent, + collation, + validation_code_hash, + result_sender: None, + session_index, + validation_data, + }); + } - tracing::debug!( - target: LOG_TARGET, - ?core_index, - block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), - "Submitting collation for core.", - ); + if collations.is_empty() { + return; + } overseer_handle .send_msg( - CollationGenerationMessage::SubmitCollation(SubmitCollationParams { - relay_parent, - collation, - validation_code_hash, - core_index, - result_sender: None, + CollationGenerationMessage::SubmitSegment(SubmitSegmentParams { scheduling_parent, - session_index, - validation_data, + core_index, + collations, }), - "SubmitCollation", + "SubmitSegment", ) .await; } diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs index f309b6751eba4..7fefceaffc176 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs @@ -266,24 +266,39 @@ pub fn run { - /// The hash of the relay chain block that provides the context for the parachain block. - pub relay_parent: RelayHash, - /// V3 scheduling proof. None for V1/V2 candidates. +/// Represents a segment of collations targeted at a single core. The block builder produces one +/// message per assigned core per wake-up; each entry in `entries` becomes one `SegmentCollation` +/// (one PoV / candidate on the relay chain) and the whole vec is submitted as one +/// `CollationGenerationMessage::SubmitSegment`. +/// +/// At the moment the builder always emits length-1 segments — resubmission of the unincluded +/// segment will extend this to multi-entry segments later. +struct CollatorSegmentMessage { + /// Shared V3 scheduling proof. `scheduling_parent` for the whole segment is derived from + /// `scheduling_proof.scheduling_parent()`. `None` → V2 descriptors for every entry. pub scheduling_proof: Option, - /// The header of the parent block. + /// Target core for every entry in the segment. + pub core_index: CoreIndex, + /// Per-entry payloads, in submission order. + pub entries: Vec>, +} + +/// One entry of a [`CollatorSegmentMessage`]. Each entry produces one `SegmentCollation` +/// (one PoV / one candidate on the relay chain), and may still bundle multiple parablocks +/// inside its PoV via `build_multi_block_collation`. +struct CollatorSegmentEntry { + /// The hash of the relay chain block that provides the context for the parachain block(s). + pub relay_parent: RelayHash, + /// The header of the parent of the first block in this entry. pub parent_header: Block::Header, - /// The built blocks. + /// The built blocks bundled into this entry. pub blocks: Vec, - /// The storage proof that was collected while building all the blocks. + /// The storage proof collected while building all of `blocks`. pub proof: StorageProof, /// The validation code hash at the parent block. pub validation_code_hash: ValidationCodeHash, - /// Core index that this block should be submitted on - pub core_index: CoreIndex, - /// The persisted validation data for this collation. + /// The persisted validation data for this entry. pub validation_data: PersistedValidationData, } From d2683d8d1ba5e62b1411259e998e66c8656198dd Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Mon, 25 May 2026 08:10:36 +0000 Subject: [PATCH 03/11] collation-generatin: accept only 1-length segments Signed-off-by: Iulian Barbu --- polkadot/node/collation-generation/src/lib.rs | 57 ++++++++++++++++++- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index 8ef86392433a8..ebaa6a63bda31 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -90,7 +90,7 @@ use error::{Error, Result}; use futures::{channel::oneshot, future::FutureExt, select}; use polkadot_node_primitives::{ AvailableData, Collation, CollationGenerationConfig, CollationSecondedSignal, PoV, - SubmitCollationParams, + SegmentCollation, SubmitCollationParams, SubmitSegmentParams, }; use polkadot_node_subsystem::{ messages::{CollationGenerationMessage, CollatorProtocolMessage, RuntimeApiMessage}, @@ -204,9 +204,12 @@ impl CollationGenerationSubsystem { false }, Ok(FromOrchestra::Communication { - msg: CollationGenerationMessage::SubmitSegment(_params), + msg: CollationGenerationMessage::SubmitSegment(params), }) => { - // TODO: handle segment submission. + if let Err(err) = self.handle_submit_segment(params, ctx).await { + gum::error!(target: LOG_TARGET, ?err, "Failed to submit segment"); + } + false }, Ok(FromOrchestra::Signal(OverseerSignal::BlockFinalized(..))) => false, @@ -282,6 +285,54 @@ impl CollationGenerationSubsystem { Ok(()) } + // TODO: once the V4 collator-protocol lands, distribute the receipts for the whole segment as + // a single unit instead of one collation at a time, and drop the length-1 restriction. + async fn handle_submit_segment( + &mut self, + params: SubmitSegmentParams, + ctx: &mut Context, + ) -> Result<()> { + let SubmitSegmentParams { scheduling_parent, core_index, mut collations } = params; + + if collations.is_empty() { + gum::warn!(target: LOG_TARGET, "received empty segment submission"); + return Ok(()); + } + + if collations.len() > 1 { + gum::warn!( + target: LOG_TARGET, + len = collations.len(), + "multi-collation segment submission not yet supported; ignoring", + ); + return Ok(()); + } + + let SegmentCollation { + relay_parent, + collation, + validation_code_hash, + result_sender, + session_index, + validation_data, + } = collations.pop().expect("len == 1; qed"); + + self.handle_submit_collation( + SubmitCollationParams { + relay_parent, + collation, + validation_code_hash, + result_sender, + core_index, + scheduling_parent, + session_index, + validation_data, + }, + ctx, + ) + .await + } + async fn handle_new_activation( &mut self, maybe_activated: Option, From 4a299faa779f7b299e18c2994d41ffaca4883ea9 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Tue, 2 Jun 2026 09:35:28 +0000 Subject: [PATCH 04/11] cumulus: segment collator message usage cleanup Signed-off-by: Iulian Barbu --- .../slot_based/block_builder_task.rs | 59 ++++++--- .../collators/slot_based/collation_task.rs | 123 +++++++++++++++++- .../aura/src/collators/slot_based/mod.rs | 43 ++++-- 3 files changed, 192 insertions(+), 33 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 26e88ac9b443d..45888598985a6 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use super::{CollatorSegmentEntry, CollatorSegmentMessage}; +use super::{CollatorMessage, CollatorSegmentEntry, CollatorSegmentMessage}; use crate::{ collator::{self as collator_util, BuildBlockAndImportParams, Collator, SlotClaim}, collators::{ @@ -108,8 +108,10 @@ pub struct BuilderTaskParams< pub proposer: Proposer, /// The generic collator service used to plug into this consensus engine. pub collator_service: CS, - /// Channel to send built blocks to the collation task. - pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, + /// Channel for V2 single-bundle collations. + pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, + /// Channel for V3 segment collations. + pub segment_sender: sc_utils::mpsc::TracingUnboundedSender>, /// Slot duration of the relay chain. pub relay_chain_slot_duration: Duration, /// Offset all time operations by this duration. @@ -185,6 +187,7 @@ where proposer, collator_service, collator_sender, + segment_sender, code_hash_provider, relay_chain_slot_duration, para_backend, @@ -502,6 +505,7 @@ where code_hash_provider: &code_hash_provider, slot_claim: &slot_claim, collator_sender: &collator_sender, + segment_sender: &segment_sender, collator: &mut collator, allowed_pov_size, core_info: cores.core_info(), @@ -561,7 +565,8 @@ struct BuildCollationParams< relay_client: &'a RelayClient, code_hash_provider: &'a CHP, slot_claim: &'a SlotClaim, - collator_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, + collator_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, + segment_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, collator: &'a mut Collator, allowed_pov_size: usize, core_info: CoreInfo, @@ -606,6 +611,7 @@ async fn build_collation_for_core< code_hash_provider, slot_claim, collator_sender, + segment_sender, collator, allowed_pov_size, core_info, @@ -902,19 +908,38 @@ where "Sending out PoV" ); - if let Err(err) = collator_sender.unbounded_send(CollatorSegmentMessage { - scheduling_proof, - core_index, - entries: vec![CollatorSegmentEntry { - relay_parent: relay_parent_hash, - parent_header: pov_parent_header.clone(), - blocks, - proof, - validation_code_hash, - validation_data, - }], - }) { - tracing::error!(target: LOG_TARGET, ?err, "Unable to send block to collation task."); + let send_ok = if v3_enabled { + segment_sender + .unbounded_send(CollatorSegmentMessage { + scheduling_proof, + core_index, + entries: vec![CollatorSegmentEntry { + relay_parent: relay_parent_hash, + parent_header: pov_parent_header.clone(), + blocks, + proof, + validation_code_hash, + validation_data, + }], + }) + .is_ok() + } else { + collator_sender + .unbounded_send(CollatorMessage { + scheduling_proof, + core_index, + relay_parent: relay_parent_hash, + parent_header: pov_parent_header.clone(), + blocks, + proof, + validation_code_hash, + validation_data, + }) + .is_ok() + }; + + if !send_ok { + tracing::error!(target: LOG_TARGET, "Unable to send block to collation task."); Err(()) } else { // Now let's sleep for the rest of the core. diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index 0eeb3fe2646a6..e320dc6a709f5 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -20,7 +20,9 @@ use std::path::PathBuf; use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface; use cumulus_relay_chain_interface::RelayChainInterface; -use polkadot_node_primitives::{MaybeCompressedPoV, SegmentCollation, SubmitSegmentParams}; +use polkadot_node_primitives::{ + MaybeCompressedPoV, SegmentCollation, SubmitCollationParams, SubmitSegmentParams, +}; use polkadot_node_subsystem::messages::CollationGenerationMessage; use polkadot_overseer::Handle as OverseerHandle; use polkadot_primitives::{CollatorPair, Id as ParaId}; @@ -32,7 +34,7 @@ use crate::export_pov_to_path; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_runtime::traits::{Block as BlockT, Header}; -use super::CollatorSegmentMessage; +use super::{CollatorMessage, CollatorSegmentMessage}; const LOG_TARGET: &str = "aura::cumulus::collation_task"; @@ -48,8 +50,10 @@ pub struct Params { pub reinitialize: bool, /// Collator service interface pub collator_service: CS, - /// Receiver channel for communication with the block builder task. - pub collator_receiver: TracingUnboundedReceiver>, + /// Receiver channel for V2 single-bundle collations from the block builder task. + pub collator_receiver: TracingUnboundedReceiver>, + /// Receiver channel for V3 segment collations from the block builder task. + pub segment_receiver: TracingUnboundedReceiver>, /// The handle from the special slot based block import. pub block_import_handle: super::SlotBasedBlockImportHandle, /// When set, the collator will export every produced `POV` to this folder. @@ -70,6 +74,7 @@ pub async fn run_collation_task( reinitialize, collator_service, mut collator_receiver, + mut segment_receiver, mut block_import_handle, export_pov, }: Params, @@ -98,6 +103,13 @@ pub async fn run_collation_task( return; }; + handle_collation_message(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; + }, + segment_message = segment_receiver.next() => { + let Some(message) = segment_message else { + return; + }; + handle_segment_message(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; }, block_import_msg = block_import_handle.next().fuse() => { @@ -109,6 +121,109 @@ pub async fn run_collation_task( } } +/// Handle an incoming single-bundle V2 [`CollatorMessage`] and forward it to the collation +/// generation subsystem as a [`CollationGenerationMessage::SubmitCollation`]. +async fn handle_collation_message( + message: CollatorMessage, + collator_service: &impl CollatorServiceInterface, + overseer_handle: &mut OverseerHandle, + relay_client: RClient, + export_pov: Option, +) { + let CollatorMessage { + scheduling_proof, + parent_header, + blocks, + proof, + validation_code_hash, + relay_parent, + core_index, + validation_data, + } = message; + + let scheduling_parent = scheduling_proof.as_ref().map(|p| p.scheduling_parent()); + let (collation, block_data) = match collator_service.build_multi_block_collation( + &parent_header, + blocks, + proof, + scheduling_proof, + ) { + Some(collation) => collation, + None => { + tracing::warn!(target: LOG_TARGET, ?core_index, "Unable to build collation."); + return; + }, + }; + + block_data.log_size_info(); + + if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { + if let Some(pov_path) = export_pov { + if let Ok(Some(relay_parent_header)) = + relay_client.header(BlockId::Hash(relay_parent)).await + { + if let Some(header) = block_data.blocks().first().map(|b| b.header()) { + export_pov_to_path::( + pov_path.clone(), + pov.clone(), + header.hash(), + *header.number(), + parent_header.clone(), + relay_parent_header.state_root, + relay_parent_header.number, + validation_data.max_pov_size, + ); + } + } else { + tracing::error!(target: LOG_TARGET, "Failed to get relay parent header from hash: {relay_parent:?}"); + } + } + + tracing::info!( + target: LOG_TARGET, + block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), + "Compressed PoV size: {}kb", + pov.block_data.0.len() as f64 / 1024f64, + ); + } + + let session_index = match relay_client.session_index_for_child(relay_parent).await { + Ok(session_index) => session_index, + Err(err) => { + tracing::error!( + target: LOG_TARGET, + ?err, + ?relay_parent, + "Failed to fetch session index." + ); + return; + }, + }; + + tracing::debug!( + target: LOG_TARGET, + ?core_index, + block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), + "Submitting collation for core.", + ); + + overseer_handle + .send_msg( + CollationGenerationMessage::SubmitCollation(SubmitCollationParams { + relay_parent, + collation, + validation_code_hash, + core_index, + result_sender: None, + scheduling_parent, + session_index, + validation_data, + }), + "SubmitCollation", + ) + .await; +} + /// Handle an incoming segment message from the block builder task. Each entry is built into a /// [`SegmentCollation`] (one PoV / one candidate on the relay chain); the whole segment is /// forwarded to the collation-generation subsystem as a single diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs index 7fefceaffc176..d18c487f6bf42 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs @@ -219,14 +219,16 @@ pub fn run { + /// The hash of the relay chain block that provides the context for the parachain block. + pub relay_parent: RelayHash, + /// V3 scheduling proof. `None` for V2 candidates emitted on this channel. + pub scheduling_proof: Option, + /// The header of the parent block. + pub parent_header: Block::Header, + /// The built blocks. + pub blocks: Vec, + /// The storage proof that was collected while building all the blocks. + pub proof: StorageProof, + /// The validation code hash at the parent block. + pub validation_code_hash: ValidationCodeHash, + /// Core index that this block should be submitted on. + pub core_index: CoreIndex, + /// The persisted validation data for this collation. + pub validation_data: PersistedValidationData, +} + +/// Segment message sent from the block builder for V3 candidates. Routed to +/// [`CollationGenerationMessage::SubmitSegment`] by the collation task. The builder always +/// emits length-1 segments. +// TODO: support multi-entry segments to enable unincluded-segment resubmission. struct CollatorSegmentMessage { /// Shared V3 scheduling proof. `scheduling_parent` for the whole segment is derived from /// `scheduling_proof.scheduling_parent()`. `None` → V2 descriptors for every entry. From 15bddbb684206a1c11e09dfa050c8c009c98abd6 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 08:42:46 +0000 Subject: [PATCH 05/11] cumulus: separate v2/v3 messages Signed-off-by: Iulian Barbu --- .../slot_based/block_builder_task.rs | 26 +-- .../collators/slot_based/collation_task.rs | 174 +++++++++--------- .../aura/src/collators/slot_based/mod.rs | 45 ++--- polkadot/node/collation-generation/src/lib.rs | 2 +- polkadot/node/primitives/src/lib.rs | 10 +- 5 files changed, 127 insertions(+), 130 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 45888598985a6..a6a6e7789c9cd 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use super::{CollatorMessage, CollatorSegmentEntry, CollatorSegmentMessage}; +use super::{CollatorMessage, CollatorSegmentMessage, SegmentKind}; use crate::{ collator::{self as collator_util, BuildBlockAndImportParams, Collator, SlotClaim}, collators::{ @@ -908,25 +908,29 @@ where "Sending out PoV" ); - let send_ok = if v3_enabled { + // `scheduling_proof` is `Some` iff `v3_enabled`; routes V3 bundles to `segment_sender`, + // V2 bundles (no proof) to `collator_sender`. + let send_ok = if let Some(scheduling_proof) = scheduling_proof { segment_sender .unbounded_send(CollatorSegmentMessage { scheduling_proof, core_index, - entries: vec![CollatorSegmentEntry { - relay_parent: relay_parent_hash, - parent_header: pov_parent_header.clone(), - blocks, - proof, - validation_code_hash, - validation_data, - }], + kind: SegmentKind::WithBundle { + bundle: CollatorMessage { + relay_parent: relay_parent_hash, + parent_header: pov_parent_header.clone(), + blocks, + proof, + validation_code_hash, + validation_data, + core_index, + }, + }, }) .is_ok() } else { collator_sender .unbounded_send(CollatorMessage { - scheduling_proof, core_index, relay_parent: relay_parent_hash, parent_header: pov_parent_header.clone(), diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index e320dc6a709f5..586640e711bdc 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -34,7 +34,7 @@ use crate::export_pov_to_path; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_runtime::traits::{Block as BlockT, Header}; -use super::{CollatorMessage, CollatorSegmentMessage}; +use super::{CollatorMessage, CollatorSegmentMessage, SegmentKind}; const LOG_TARGET: &str = "aura::cumulus::collation_task"; @@ -131,7 +131,6 @@ async fn handle_collation_message, ) { let CollatorMessage { - scheduling_proof, parent_header, blocks, proof, @@ -141,12 +140,11 @@ async fn handle_collation_message collation, None => { @@ -215,7 +213,7 @@ async fn handle_collation_message, ) { - let CollatorSegmentMessage { scheduling_proof, core_index, entries } = message; - - // Unlike the single-collation path, we cannot fall back to "the" relay_parent here — entries - // may have different relay parents. `None` means V2 descriptors for the whole segment. - let scheduling_parent = scheduling_proof.as_ref().map(|p| p.scheduling_parent()); - - let mut collations = Vec::with_capacity(entries.len()); - for entry in entries { - let relay_parent = entry.relay_parent; - let validation_code_hash = entry.validation_code_hash; - let validation_data = entry.validation_data; - let parent_header = entry.parent_header; - - let (collation, block_data) = match collator_service.build_multi_block_collation( - &parent_header, - entry.blocks, - entry.proof, - scheduling_proof.clone(), - ) { - Some(collation) => collation, - None => { - tracing::warn!(target: LOG_TARGET, ?core_index, ?relay_parent, "Unable to build collation for segment entry."); - continue; - }, - }; - - block_data.log_size_info(); - - if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { - if let Some(pov_path) = export_pov.clone() { - if let Ok(Some(relay_parent_header)) = - relay_client.header(BlockId::Hash(relay_parent)).await - { - if let Some(header) = block_data.blocks().first().map(|b| b.header()) { - export_pov_to_path::( - pov_path, - pov.clone(), - header.hash(), - *header.number(), - parent_header.clone(), - relay_parent_header.state_root, - relay_parent_header.number, - validation_data.max_pov_size, - ); + let CollatorSegmentMessage { scheduling_proof, core_index, kind } = message; + let scheduling_parent = scheduling_proof.scheduling_parent(); + + let mut collations = Vec::new(); + match kind { + SegmentKind::WithBundle { bundle } => { + let CollatorMessage { + relay_parent, + parent_header, + blocks, + proof, + validation_code_hash, + core_index: _bundle_core_index, + validation_data, + } = bundle; + + let (collation, block_data) = match collator_service.build_multi_block_collation( + &parent_header, + blocks, + proof, + Some(scheduling_proof.clone()), + ) { + Some(collation) => collation, + None => { + tracing::warn!(target: LOG_TARGET, ?core_index, ?relay_parent, "Unable to build collation for segment entry."); + return; + }, + }; + + block_data.log_size_info(); + + if let MaybeCompressedPoV::Compressed(ref pov) = collation.proof_of_validity { + if let Some(pov_path) = export_pov.clone() { + if let Ok(Some(relay_parent_header)) = + relay_client.header(BlockId::Hash(relay_parent)).await + { + if let Some(header) = block_data.blocks().first().map(|b| b.header()) { + export_pov_to_path::( + pov_path, + pov.clone(), + header.hash(), + *header.number(), + parent_header.clone(), + relay_parent_header.state_root, + relay_parent_header.number, + validation_data.max_pov_size, + ); + } + } else { + tracing::error!(target: LOG_TARGET, "Failed to get relay parent header from hash: {relay_parent:?}"); } - } else { - tracing::error!(target: LOG_TARGET, "Failed to get relay parent header from hash: {relay_parent:?}"); } + + tracing::info!( + target: LOG_TARGET, + block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), + "Compressed PoV size: {}kb", + pov.block_data.0.len() as f64 / 1024f64, + ); } - tracing::info!( + let session_index = match relay_client.session_index_for_child(relay_parent).await { + Ok(session_index) => session_index, + Err(err) => { + tracing::error!( + target: LOG_TARGET, + ?err, + ?relay_parent, + "Failed to fetch session index for segment entry.", + ); + return; + }, + }; + + tracing::debug!( target: LOG_TARGET, + ?core_index, + ?relay_parent, block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), - "Compressed PoV size: {}kb", - pov.block_data.0.len() as f64 / 1024f64, + "Adding entry to segment for core.", ); - } - let session_index = match relay_client.session_index_for_child(relay_parent).await { - Ok(session_index) => session_index, - Err(err) => { - tracing::error!( - target: LOG_TARGET, - ?err, - ?relay_parent, - "Failed to fetch session index for segment entry.", - ); - continue; - }, - }; - - tracing::debug!( - target: LOG_TARGET, - ?core_index, - ?relay_parent, - block_numbers = ?block_data.blocks().iter().map(|b| *b.header().number()).collect::>(), - "Adding entry to segment for core.", - ); - - collations.push(SegmentCollation { - relay_parent, - collation, - validation_code_hash, - result_sender: None, - session_index, - validation_data, - }); + collations.push(SegmentCollation { + relay_parent, + collation, + validation_code_hash, + result_sender: None, + session_index, + validation_data, + }); + }, + SegmentKind::ResubmitOnly => { + // TODO: merge with held unincluded segment. For now this branch produces an empty + // submission and is a no-op downstream. + }, } if collations.is_empty() { diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs index d18c487f6bf42..277364802e3bf 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs @@ -274,8 +274,6 @@ pub fn run { /// The hash of the relay chain block that provides the context for the parachain block. pub relay_parent: RelayHash, - /// V3 scheduling proof. `None` for V2 candidates emitted on this channel. - pub scheduling_proof: Option, /// The header of the parent block. pub parent_header: Block::Header, /// The built blocks. @@ -290,34 +288,25 @@ struct CollatorMessage { pub validation_data: PersistedValidationData, } -/// Segment message sent from the block builder for V3 candidates. Routed to -/// [`CollationGenerationMessage::SubmitSegment`] by the collation task. The builder always -/// emits length-1 segments. -// TODO: support multi-entry segments to enable unincluded-segment resubmission. +/// Segment message sent from the block builder for V3/V4 candidates. Routed to +/// [`CollationGenerationMessage::SubmitSegment`] by the collation task. The collation task +/// resubmits the held unincluded segment alongside the new bundle (if any) under the shared +/// `scheduling_proof` and to the `core_index` specified here. struct CollatorSegmentMessage { - /// Shared V3 scheduling proof. `scheduling_parent` for the whole segment is derived from - /// `scheduling_proof.scheduling_parent()`. `None` → V2 descriptors for every entry. - pub scheduling_proof: Option, - /// Target core for every entry in the segment. + /// Scheduling proof shared by every entry in the segment. `signed_scheduling_info` is + /// `Some` whenever any prior unincluded entry is being resubmitted. + pub scheduling_proof: SchedulingProof, + /// Target core for the whole segment submission. pub core_index: CoreIndex, - /// Per-entry payloads, in submission order. - pub entries: Vec>, + /// Whether this message carries a freshly-built bundle or signals "resubmit the held prior + /// alone, the builder produced nothing this slot". + pub kind: SegmentKind, } -/// One entry of a [`CollatorSegmentMessage`]. Each entry produces one `SegmentCollation` -/// (one PoV / one candidate on the relay chain), and may still bundle multiple parablocks -/// inside its PoV via `build_multi_block_collation`. -struct CollatorSegmentEntry { - /// The hash of the relay chain block that provides the context for the parachain block(s). - pub relay_parent: RelayHash, - /// The header of the parent of the first block in this entry. - pub parent_header: Block::Header, - /// The built blocks bundled into this entry. - pub blocks: Vec, - /// The storage proof collected while building all of `blocks`. - pub proof: StorageProof, - /// The validation code hash at the parent block. - pub validation_code_hash: ValidationCodeHash, - /// The persisted validation data for this entry. - pub validation_data: PersistedValidationData, +/// Kind of segment message: with a fresh bundle to merge, or resubmit-only. +enum SegmentKind { + /// New bundle to merge with the held prior unincluded segment. + WithBundle { bundle: CollatorMessage }, + /// No new bundle this slot — collation task should resubmit the held prior alone. + ResubmitOnly, } diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index ebaa6a63bda31..dfffa2ba12613 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -324,7 +324,7 @@ impl CollationGenerationSubsystem { validation_code_hash, result_sender, core_index, - scheduling_parent, + scheduling_parent: Some(scheduling_parent), session_index, validation_data, }, diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index a0b8824b0ec58..4ffad8f6ba4fb 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -581,13 +581,11 @@ pub struct SegmentCollation { /// segment (relay parent, collation payload, validation data, etc.). #[derive(Debug)] pub struct SubmitSegmentParams { - /// The scheduling parent for V3 candidate descriptors, shared by all collations in the - /// segment. If set, every candidate descriptor will use this as the scheduling parent - /// (creating V3 descriptors). If `None`, each collation's `relay_parent` is used (V2 - /// descriptors). + /// The scheduling parent shared by every collation in the segment. Segments are V3/V4-only, + /// so this is always present and produces V3 candidate descriptors. /// - /// WARNING: Should only be set if the `CandidateReceiptV3` node feature is set. - pub scheduling_parent: Option, + /// WARNING: only valid when the `CandidateReceiptV3` node feature is set. + pub scheduling_parent: Hash, /// The core index on which the resulting candidates should be backed. pub core_index: CoreIndex, /// The collations in this segment, in the order they should be submitted. From 524a9cbd1a106a338054e8b69ac389ea7398d915 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 10:40:46 +0000 Subject: [PATCH 06/11] cumulus: resubmit only checks empty Signed-off-by: Iulian Barbu --- .../collators/slot_based/collation_task.rs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index 586640e711bdc..ba56a6057bd72 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -140,18 +140,14 @@ async fn handle_collation_message collation, - None => { - tracing::warn!(target: LOG_TARGET, ?core_index, "Unable to build collation."); - return; - }, - }; + let (collation, block_data) = + match collator_service.build_multi_block_collation(&parent_header, blocks, proof, None) { + Some(collation) => collation, + None => { + tracing::warn!(target: LOG_TARGET, ?core_index, "Unable to build collation."); + return; + }, + }; block_data.log_size_info(); @@ -326,15 +322,12 @@ async fn handle_segment_message { - // TODO: merge with held unincluded segment. For now this branch produces an empty - // submission and is a no-op downstream. + if collations.is_empty() { + return; + } }, } - if collations.is_empty() { - return; - } - overseer_handle .send_msg( CollationGenerationMessage::SubmitSegment(SubmitSegmentParams { From b959e28ab39eecf8c216810ec58f8d1804b968bb Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 18:07:05 +0000 Subject: [PATCH 07/11] more docs/logic updates Signed-off-by: Iulian Barbu --- .../slot_based/block_builder_task.rs | 108 ++++++++++-------- .../collators/slot_based/collation_task.rs | 29 ++--- .../aura/src/collators/slot_based/mod.rs | 31 +++-- cumulus/primitives/core/src/scheduling.rs | 49 ++++---- polkadot/node/primitives/src/lib.rs | 9 +- 5 files changed, 126 insertions(+), 100 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index a6a6e7789c9cd..0d05608c3689f 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use super::{CollatorMessage, CollatorSegmentMessage, SegmentKind}; +use super::{CollatorMessage, CollatorResubmitSegment, SegmentKind}; use crate::{ collator::{self as collator_util, BuildBlockAndImportParams, Collator, SlotClaim}, collators::{ @@ -111,7 +111,7 @@ pub struct BuilderTaskParams< /// Channel for V2 single-bundle collations. pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, /// Channel for V3 segment collations. - pub segment_sender: sc_utils::mpsc::TracingUnboundedSender>, + pub resubmit_sender: sc_utils::mpsc::TracingUnboundedSender>, /// Slot duration of the relay chain. pub relay_chain_slot_duration: Duration, /// Offset all time operations by this duration. @@ -187,7 +187,7 @@ where proposer, collator_service, collator_sender, - segment_sender, + resubmit_sender, code_hash_provider, relay_chain_slot_duration, para_backend, @@ -277,7 +277,7 @@ where .await .unwrap_or(0); } - let Ok(Some(relay_parent_data)) = offset_relay_parent_find_descendants( + let Ok(Some(mut relay_parent_data)) = offset_relay_parent_find_descendants( &mut relay_chain_data_cache, scheduling_parent_header.clone(), relay_parent_offset, @@ -487,12 +487,43 @@ where "Core configuration", ); + // Build the V3 scheduling proof once per slot — it's shared by every candidate in the + // segment. Consumes `relay_parent_data.descendants`, so the inherent created later + // downstream sees an empty list (which V3 verification expects). + let scheduling_proof = if v3_enabled { + let descendants = relay_parent_data.take_descendants(); + let header_chain: Vec<_> = descendants.into_iter().rev().collect(); + let scheduling_parent = + header_chain.first().map(|header| header.hash()).unwrap_or(relay_parent_hash); + + tracing::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent_hash, + ?scheduling_parent, + header_chain_len = header_chain.len(), + "Building V3 collation with scheduling proof", + ); + + Some(SchedulingProof { + header_chain, + internal_scheduling_parent_header: relay_parent_header.clone(), + // TODO: sign and populate when resubmission lands. The relay-chain verifier + // rejects `ResubmitOnly` / mixed segments whose proof lacks + // `signed_scheduling_info`. + signed_scheduling_info: None, + }) + } else { + None + }; + let mut pov_parent_header = initial_parent_header; let mut pov_parent_hash = initial_parent_hash; let block_time = relay_chain_slot_duration / number_of_blocks; - for blocks_per_core in blocks_per_cores { + for (core_iter_index, blocks_per_core) in blocks_per_cores.into_iter().enumerate() { let time_for_core = slot_time.time_left() / cores.cores_left(); + let is_first_core = core_iter_index == 0; + let this_core_index = cores.core_index(); match build_collation_for_core(BuildCollationParams { pov_parent_header, @@ -505,11 +536,11 @@ where code_hash_provider: &code_hash_provider, slot_claim: &slot_claim, collator_sender: &collator_sender, - segment_sender: &segment_sender, + resubmit_sender: &resubmit_sender, collator: &mut collator, allowed_pov_size, core_info: cores.core_info(), - core_index: cores.core_index(), + core_index: this_core_index, block_time, blocks_per_core, time_for_core, @@ -522,7 +553,7 @@ where relay_slot, para_slot: para_slot.slot, para_client: &*para_client, - v3_enabled, + scheduling_proof: scheduling_proof.clone(), }) .await { @@ -530,8 +561,19 @@ where pov_parent_header = header; pov_parent_hash = pov_parent_header.hash(); }, - // Let's wait for the next slot - Ok(None) => break, + Ok(None) => { + // First-core failed to build a fresh bundle: still ship the held prior + // unincluded segment via `ResubmitOnly`. + if is_first_core { + if let Some(proof) = scheduling_proof.clone() { + let _ = resubmit_sender.unbounded_send(CollatorResubmitSegment { + scheduling_proof: proof, + kind: SegmentKind::ResubmitOnly { core_index: this_core_index }, + }); + } + } + break; + }, Err(()) => return, } @@ -566,7 +608,7 @@ struct BuildCollationParams< code_hash_provider: &'a CHP, slot_claim: &'a SlotClaim, collator_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, - segment_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, + resubmit_sender: &'a sc_utils::mpsc::TracingUnboundedSender>, collator: &'a mut Collator, allowed_pov_size: usize, core_info: CoreInfo, @@ -583,7 +625,8 @@ struct BuildCollationParams< relay_slot: cumulus_primitives_aura::Slot, para_slot: cumulus_primitives_aura::Slot, para_client: &'a Client, - v3_enabled: bool, + /// V3 scheduling proof, built once per slot. `Some` iff V3 is enabled. + scheduling_proof: Option, } /// Build a collation for one core. @@ -611,7 +654,7 @@ async fn build_collation_for_core< code_hash_provider, slot_claim, collator_sender, - segment_sender, + resubmit_sender, collator, allowed_pov_size, core_info, @@ -621,13 +664,13 @@ async fn build_collation_for_core< time_for_core: slot_time_for_core, is_last_core_in_parachain_slot, collator_peer_id, - mut relay_parent_data, + relay_parent_data, total_number_of_blocks, included_header_hash, relay_slot, para_slot, para_client, - v3_enabled, + scheduling_proof, }: BuildCollationParams<'_, Block, P, RelayClient, BI, CIDP, Proposer, CS, CHP, Client>, ) -> Result, ()> where @@ -655,34 +698,6 @@ where max_pov_size, }; - // Check if V3 scheduling is enabled and build scheduling proof if so. - let mut scheduling_proof = None; - if v3_enabled { - // The relay parent descendants are only needed for v2. - let descendants = relay_parent_data.take_descendants(); - // The descendants are ordered from oldest to newest, so we need to reverse them. - let header_chain: Vec<_> = descendants.into_iter().rev().collect(); - let scheduling_parent = - header_chain.first().map(|header| header.hash()).unwrap_or(relay_parent_hash); - - tracing::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent_hash, - ?scheduling_parent, - header_chain_len = header_chain.len(), - "Building V3 collation with scheduling proof", - ); - - scheduling_proof = Some(SchedulingProof { - header_chain, - // Initial submission: internal_scheduling_parent == relay_parent, so the - // internal scheduling parent header is the relay parent's header itself. - internal_scheduling_parent_header: relay_parent_header.clone(), - // Initial submission: no signature needed, core selection from UMP signals - signed_scheduling_info: None, - }); - } - let Some(validation_code_hash) = code_hash_provider.code_hash_at(pov_parent_hash) else { tracing::error!( target: LOG_TARGET, @@ -908,13 +923,12 @@ where "Sending out PoV" ); - // `scheduling_proof` is `Some` iff `v3_enabled`; routes V3 bundles to `segment_sender`, + // `scheduling_proof` is `Some` iff `v3_enabled`; routes V3 bundles to `resubmit_sender`, // V2 bundles (no proof) to `collator_sender`. let send_ok = if let Some(scheduling_proof) = scheduling_proof { - segment_sender - .unbounded_send(CollatorSegmentMessage { + resubmit_sender + .unbounded_send(CollatorResubmitSegment { scheduling_proof, - core_index, kind: SegmentKind::WithBundle { bundle: CollatorMessage { relay_parent: relay_parent_hash, diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index ba56a6057bd72..dc2b3201c5a4d 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -34,7 +34,7 @@ use crate::export_pov_to_path; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_runtime::traits::{Block as BlockT, Header}; -use super::{CollatorMessage, CollatorSegmentMessage, SegmentKind}; +use super::{CollatorMessage, CollatorResubmitSegment, SegmentKind}; const LOG_TARGET: &str = "aura::cumulus::collation_task"; @@ -53,7 +53,7 @@ pub struct Params { /// Receiver channel for V2 single-bundle collations from the block builder task. pub collator_receiver: TracingUnboundedReceiver>, /// Receiver channel for V3 segment collations from the block builder task. - pub segment_receiver: TracingUnboundedReceiver>, + pub resubmit_receiver: TracingUnboundedReceiver>, /// The handle from the special slot based block import. pub block_import_handle: super::SlotBasedBlockImportHandle, /// When set, the collator will export every produced `POV` to this folder. @@ -74,7 +74,7 @@ pub async fn run_collation_task( reinitialize, collator_service, mut collator_receiver, - mut segment_receiver, + mut resubmit_receiver, mut block_import_handle, export_pov, }: Params, @@ -105,12 +105,12 @@ pub async fn run_collation_task( handle_collation_message(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; }, - segment_message = segment_receiver.next() => { - let Some(message) = segment_message else { + resubmit_message = resubmit_receiver.next() => { + let Some(message) = resubmit_message else { return; }; - handle_segment_message(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; + handle_resubmit_segment(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; }, block_import_msg = block_import_handle.next().fuse() => { // TODO: Implement me. @@ -223,18 +223,18 @@ async fn handle_collation_message( - message: CollatorSegmentMessage, +async fn handle_resubmit_segment( + message: CollatorResubmitSegment, collator_service: &impl CollatorServiceInterface, overseer_handle: &mut OverseerHandle, relay_client: RClient, export_pov: Option, ) { - let CollatorSegmentMessage { scheduling_proof, core_index, kind } = message; + let CollatorResubmitSegment { scheduling_proof, kind } = message; let scheduling_parent = scheduling_proof.scheduling_parent(); let mut collations = Vec::new(); - match kind { + let core_index = match kind { SegmentKind::WithBundle { bundle } => { let CollatorMessage { relay_parent, @@ -242,7 +242,7 @@ async fn handle_segment_message { + SegmentKind::ResubmitOnly { core_index } => { if collations.is_empty() { return; } + core_index }, - } + }; overseer_handle .send_msg( diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs index 277364802e3bf..d5cc88381d152 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs @@ -220,7 +220,7 @@ pub fn run { pub validation_data: PersistedValidationData, } -/// Segment message sent from the block builder for V3/V4 candidates. Routed to -/// [`CollationGenerationMessage::SubmitSegment`] by the collation task. The collation task -/// resubmits the held unincluded segment alongside the new bundle (if any) under the shared -/// `scheduling_proof` and to the `core_index` specified here. -struct CollatorSegmentMessage { - /// Scheduling proof shared by every entry in the segment. `signed_scheduling_info` is - /// `Some` whenever any prior unincluded entry is being resubmitted. +/// V3/V4 resubmit-segment message sent by the block builder. Routed to +/// [`CollationGenerationMessage::SubmitSegment`] by the collation task. +struct CollatorResubmitSegment { + /// Scheduling proof shared by every entry in the submitted segment. pub scheduling_proof: SchedulingProof, - /// Target core for the whole segment submission. - pub core_index: CoreIndex, - /// Whether this message carries a freshly-built bundle or signals "resubmit the held prior - /// alone, the builder produced nothing this slot". + /// Whether this message carries a freshly-built block or signals resubmit-only. pub kind: SegmentKind, } -/// Kind of segment message: with a fresh bundle to merge, or resubmit-only. +/// Kind of resubmit-segment message. The target `CoreIndex` is read from `bundle.core_index` in +/// the `WithBundle` case and carried explicitly in the `ResubmitOnly` case. enum SegmentKind { - /// New bundle to merge with the held prior unincluded segment. + /// A freshly-built block to submit. WithBundle { bundle: CollatorMessage }, - /// No new bundle this slot — collation task should resubmit the held prior alone. - ResubmitOnly, + /// Resubmit only — no freshly-built block this slot. + ResubmitOnly { core_index: CoreIndex }, } diff --git a/cumulus/primitives/core/src/scheduling.rs b/cumulus/primitives/core/src/scheduling.rs index 74859a6ad8625..cb7ac4dd00ad0 100644 --- a/cumulus/primitives/core/src/scheduling.rs +++ b/cumulus/primitives/core/src/scheduling.rs @@ -10,9 +10,9 @@ //! //! # Resubmission //! -//! When a candidate fails to get backed in time, a different collator can resubmit -//! it with a new `scheduling_parent` (fresh relay tip) without re-executing the blocks. -//! The `relay_parent` stays the same since the execution context hasn't changed. +//! When a candidate fails to get backed in time, a collator can resubmit it with a new +//! `scheduling_parent` (fresh relay tip) without re-executing the blocks. The `relay_parent` +//! stays the same since the execution context hasn't changed. //! //! For resubmission, `signed_scheduling_info` must be provided. The resubmitting //! collator signs the core selection, proving they are the eligible author for the @@ -29,26 +29,29 @@ use sp_runtime::traits::{BlakeTwo256, Hash as HashT}; /// scheduling parent, preventing replay attacks across different scheduling contexts. #[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] pub struct SchedulingInfoPayload { - /// Which core to use (indexes into the parachain's assigned cores). + /// Index into the parachain's cores scheduled at depth `claim_queue_offset` of the relay + /// chain's claim queue. The relay chain resolves it to a `CoreIndex` at verification time. pub core_selector: CoreSelector, - /// The claim queue offset. + /// Depth in the relay chain's claim queue (at `scheduling_parent`) used together with + /// `core_selector` to resolve the target `CoreIndex`. pub claim_queue_offset: u8, /// Peer ID to receive reputation credit for successful collation delivery. pub peer_id: ApprovedPeerId, - /// The internal scheduling parent whom's slot decides the - /// eligible block author that must sign the payload. + /// The internal scheduling parent whose slot decides the eligible block author that must + /// sign the payload. pub internal_scheduling_parent: polkadot_primitives::Hash, } -/// Signed scheduling information for candidate resubmission. +/// Signed scheduling information attached to a [`SchedulingProof`]. /// -/// When a collator resubmits a candidate (with a newer `scheduling_parent` but same -/// `relay_parent`), they must sign the core selection to prove eligibility for the -/// slot at `internal_scheduling_parent`. +/// On **resubmission** (when the candidate is submitted once more under a newer +/// `scheduling_parent` than the one used for its initial submission), this signature is +/// required: the resubmitting collator signs the core selection to prove eligibility for the +/// slot at the `internal_scheduling_parent`. /// -/// The `claim_queue_offset` is derived from the runtime's `relay_parent_offset` -/// configuration and is not part of this struct - it cannot be overridden by the -/// collator. +/// On **initial submission**, this is optional. When present, it provides an explicit signed +/// core selection that overrides the parachain block's UMP signals; when absent, the relay +/// chain falls back to those UMP signals. #[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] pub struct SignedSchedulingInfo { /// The scheduling information. @@ -85,26 +88,30 @@ pub struct SchedulingProof { /// Forms a chain where each header's parent_hash equals the next header's hash. /// The first header's hash must equal the candidate's scheduling_parent. /// The last header's parent_hash is the internal scheduling parent. - /// Length is defined by the parachain runtime config (RelayParentOffset). + /// Length is defined by the parachain runtime config (`RelayParentOffset`); when that is + /// `0` the chain is empty and `scheduling_parent == internal_scheduling_parent`. pub header_chain: Vec, /// The relay chain header at `internal_scheduling_parent`. Its hash must equal the /// `internal_scheduling_parent` derived from `header_chain` (the parent of the chain's /// last header, or `scheduling_parent` if the chain is empty). pub internal_scheduling_parent_header: RelayChainHeader, - /// Signed scheduling info for core selection override. + /// Signed scheduling info. Required on resubmission, optional on initial submission. /// /// - `None` with `relay_parent == internal_scheduling_parent`: Initial submission. Core /// selection comes from the parachain block's UMP signals. /// /// - `Some` with `relay_parent == internal_scheduling_parent`: Initial submission with - /// explicit core selection. This is optional but legal. Collators should refuse to - /// acknowledge blocks with invalid scheduling info, so providing a signature is not required - /// for initial submissions. + /// explicit signed core selection. Legal but not required; collators should refuse to + /// acknowledge blocks with invalid scheduling info, so the signature isn't load-bearing on + /// initial submission. /// /// - `Some` with `relay_parent != internal_scheduling_parent`: Resubmission (required). The /// resubmitting collator signs the core selection, overriding the block's UMP signals. /// Signature is verified against the eligible author for the slot at /// `internal_scheduling_parent`. + /// + /// - `None` with `relay_parent != internal_scheduling_parent`: invalid; rejected by the + /// verifier. pub signed_scheduling_info: Option, } @@ -131,7 +138,9 @@ pub trait VerifySchedulingSignature { /// Whether V3 scheduling validation is enabled. const V3_SCHEDULING_ENABLED: bool; - /// Verifies `signed_info` against `internal_scheduling_parent_header`. + /// Verifies `signed_info.signature` over the encoded payload against the public key of the + /// eligible block author for the parachain slot derived from + /// `internal_scheduling_parent_header`'s BABE pre-digest. fn verify( signed_info: &SignedSchedulingInfo, internal_scheduling_parent_header: &RelayChainHeader, diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 4ffad8f6ba4fb..69d3cdf85dd78 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -576,9 +576,13 @@ pub struct SegmentCollation { /// Parameters for `CollationGenerationMessage::SubmitSegment`. /// -/// Submits multiple collations that share a common scheduling parent and target core. Each +/// Submits a segment of collations that share a common scheduling parent and target core. Each /// [`SegmentCollation`] in `collations` carries the fields that may differ between blocks of the /// segment (relay parent, collation payload, validation data, etc.). +/// +/// **Currently restricted to `collations.len() == 1`.** The collation-generation subsystem drops +/// submissions with more than one entry (the V4 collator-protocol work will lift this and +/// distribute the whole segment as a single unit). #[derive(Debug)] pub struct SubmitSegmentParams { /// The scheduling parent shared by every collation in the segment. Segments are V3/V4-only, @@ -588,7 +592,8 @@ pub struct SubmitSegmentParams { pub scheduling_parent: Hash, /// The core index on which the resulting candidates should be backed. pub core_index: CoreIndex, - /// The collations in this segment, in the order they should be submitted. + /// The collations in this segment, in the order they should be submitted. Must currently + /// contain exactly one entry; see the type-level note above. pub collations: Vec, } From 8b10509c581900d5d5881931f5fd76d4704c588c Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 18:13:53 +0000 Subject: [PATCH 08/11] cumulus: add more details in comments Signed-off-by: Iulian Barbu --- .../aura/src/collators/slot_based/block_builder_task.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 0d05608c3689f..8c3479d32df2f 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -564,6 +564,12 @@ where Ok(None) => { // First-core failed to build a fresh bundle: still ship the held prior // unincluded segment via `ResubmitOnly`. + // + // TODO: seding to a certain core can apply in a custom way too, and + // other strategies for resubmitting a segment should be available - e.g. + // split segment between cores equally, or attempt grouping unincluded + // blocks based on the core index resulting from their `core_selector` + // and `claim_queue_offset` combination, at the `scheduling_parent`). if is_first_core { if let Some(proof) = scheduling_proof.clone() { let _ = resubmit_sender.unbounded_send(CollatorResubmitSegment { From acfb99f072add2201ad0ef37c72db5f0076ef02f Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 18:17:32 +0000 Subject: [PATCH 09/11] cumulus: some more doc updates Signed-off-by: Iulian Barbu --- cumulus/primitives/core/src/scheduling.rs | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cumulus/primitives/core/src/scheduling.rs b/cumulus/primitives/core/src/scheduling.rs index cb7ac4dd00ad0..80fe0ecbe6305 100644 --- a/cumulus/primitives/core/src/scheduling.rs +++ b/cumulus/primitives/core/src/scheduling.rs @@ -23,7 +23,8 @@ use codec::{Decode, Encode}; use polkadot_primitives::{ApprovedPeerId, CoreSelector, Header as RelayChainHeader}; use sp_runtime::traits::{BlakeTwo256, Hash as HashT}; -/// Payload signed by a collator for resubmission. +/// Payload signed by a collator to authorize core selection — required on resubmission, +/// optional on initial submission. /// /// This binds the core selection and reputation-credit peer to a specific internal /// scheduling parent, preventing replay attacks across different scheduling contexts. @@ -78,22 +79,21 @@ impl SchedulingInfoPayload { /// V3 scheduling proof included in the POV. /// -/// Provides the ancestry from scheduling_parent back to the internal scheduling -/// parent. The PVF validates this against the relay_parent and scheduling_parent -/// from the candidate descriptor extension. +/// Carries the relay-chain ancestry starting at `scheduling_parent` and ending one step +/// before `internal_scheduling_parent` (whose header is supplied separately). The PVF +/// validates this against the `relay_parent` and `scheduling_parent` from the candidate +/// descriptor extension. #[derive(Clone, Encode, Decode, Debug, PartialEq, Eq)] pub struct SchedulingProof { - /// Relay chain headers proving ancestry from scheduling_parent backward. - /// /// Forms a chain where each header's parent_hash equals the next header's hash. /// The first header's hash must equal the candidate's scheduling_parent. /// The last header's parent_hash is the internal scheduling parent. /// Length is defined by the parachain runtime config (`RelayParentOffset`); when that is /// `0` the chain is empty and `scheduling_parent == internal_scheduling_parent`. pub header_chain: Vec, - /// The relay chain header at `internal_scheduling_parent`. Its hash must equal the - /// `internal_scheduling_parent` derived from `header_chain` (the parent of the chain's - /// last header, or `scheduling_parent` if the chain is empty). + /// The relay chain header at `internal_scheduling_parent` (the parent of the chain's last + /// header, or `scheduling_parent` if the chain is empty). Carries the BABE pre-digest used + /// to look up the eligible block author when verifying `signed_scheduling_info`. pub internal_scheduling_parent_header: RelayChainHeader, /// Signed scheduling info. Required on resubmission, optional on initial submission. /// @@ -101,14 +101,10 @@ pub struct SchedulingProof { /// selection comes from the parachain block's UMP signals. /// /// - `Some` with `relay_parent == internal_scheduling_parent`: Initial submission with - /// explicit signed core selection. Legal but not required; collators should refuse to - /// acknowledge blocks with invalid scheduling info, so the signature isn't load-bearing on - /// initial submission. + /// explicit signed core selection. Legal but not required. /// /// - `Some` with `relay_parent != internal_scheduling_parent`: Resubmission (required). The /// resubmitting collator signs the core selection, overriding the block's UMP signals. - /// Signature is verified against the eligible author for the slot at - /// `internal_scheduling_parent`. /// /// - `None` with `relay_parent != internal_scheduling_parent`: invalid; rejected by the /// verifier. From 3f6de1a2a0e7c55e8a08617993a2d8690c4cac30 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 18:42:14 +0000 Subject: [PATCH 10/11] cumulus: collations should be fetched from resubmission machinery Signed-off-by: Iulian Barbu --- .../aura/src/collators/slot_based/collation_task.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index dc2b3201c5a4d..c2c5fe1cf34a2 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -110,7 +110,11 @@ pub async fn run_collation_task( return; }; - handle_resubmit_segment(message, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; + // Seed the segment with the current unincluded-segment collations. Empty for now; + // future resubmission machinery — the layer that computes and keeps the unincluded + // segment up to date across parachain slots — will populate this before dispatch. + let collations = Vec::new(); + handle_resubmit_segment(message, collations, &collator_service, &mut overseer_handle, relay_client.clone(), export_pov.clone()).await; }, block_import_msg = block_import_handle.next().fuse() => { // TODO: Implement me. @@ -225,6 +229,7 @@ async fn handle_collation_message( message: CollatorResubmitSegment, + mut collations: Vec>, collator_service: &impl CollatorServiceInterface, overseer_handle: &mut OverseerHandle, relay_client: RClient, @@ -232,8 +237,6 @@ async fn handle_resubmit_segment { let CollatorMessage { From 730b34f24d082f7aae78a17ce97eb5dad9516743 Mon Sep 17 00:00:00 2001 From: Iulian Barbu Date: Thu, 4 Jun 2026 18:58:50 +0000 Subject: [PATCH 11/11] cumulus: unincluded segment passed from above Signed-off-by: Iulian Barbu --- .../consensus/aura/src/collators/slot_based/collation_task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs index c2c5fe1cf34a2..4b572b568664b 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/collation_task.rs @@ -229,7 +229,7 @@ async fn handle_collation_message( message: CollatorResubmitSegment, - mut collations: Vec>, + mut collations: Vec, collator_service: &impl CollatorServiceInterface, overseer_handle: &mut OverseerHandle, relay_client: RClient,