Partial columns cleanup (#9321)

#8314 left a few ugly potentially panicking location behind - all of them believed to be unreachable, but this PR fixes them regardless for good hygiene.


  Update to `ethereum_ssz 0.10.4` for two new helpers: `not_inplace` and `clone_zeroed`.

Remove remaining `expect` and `todo!` in favour of these helpers and one new fallible (but practically infallible) method.


Co-Authored-By: Daniel Knopik <daniel@dknopik.de>
This commit is contained in:
Daniel Knopik
2026-05-21 05:25:02 +02:00
committed by GitHub
parent 2c76ee5b6b
commit a9637c1650
6 changed files with 47 additions and 39 deletions

4
Cargo.lock generated
View File

@@ -3282,9 +3282,9 @@ dependencies = [
[[package]] [[package]]
name = "ethereum_ssz" name = "ethereum_ssz"
version = "0.10.3" version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368a4a4e4273b0135111fe9464e35465067766a8f664615b5a86338b73864407" checksum = "e462875ad8693755ea8913d6e905715c76ea4836e2254e18c9cf0f7a8f8c2a13"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"arbitrary", "arbitrary",

View File

@@ -1220,7 +1220,16 @@ pub fn validate_partial_data_column_sidecar_for_gossip<T: BeaconChainTypes>(
header, header,
}; };
} }
Err(MissingCellsError::UnexpectedError(e)) => todo!("handle unexpected error {:?}", e), Err(MissingCellsError::UnexpectedError(e)) => {
return PartialColumnVerificationResult::ErrWithValidHeader {
err: GossipDataColumnError::InternalError(format!(
"An unexpected error occurred while validating partial data columns: {:?}",
e
))
.into(),
header,
};
}
}; };
// We do not have to check block related data here, as we create the verifiable column from // We do not have to check block related data here, as we create the verifiable column from

View File

@@ -524,11 +524,15 @@ pub(crate) fn publish_column_sidecars<T: BeaconChainTypes>(
if chain.config.enable_partial_columns if chain.config.enable_partial_columns
&& let DataColumnSidecar::Fulu(fulu_data_col) = data_col.as_ref() && let DataColumnSidecar::Fulu(fulu_data_col) = data_col.as_ref()
{ {
let mut partial = fulu_data_col.to_partial(); match fulu_data_col.to_partial() {
if let Some(header) = partial.sidecar.header.take() { Ok(mut partial) => {
partial_header = Some(header); if let Some(header) = partial.sidecar.header.take() {
partial_header = Some(header);
}
partial_columns.push(Arc::new(partial));
}
Err(err) => crit!(?err, "Could not convert from full to partial"),
} }
partial_columns.push(Arc::new(partial));
} }
let subnet = DataColumnSubnetId::from_column_index(*data_col.index(), &chain.spec); let subnet = DataColumnSubnetId::from_column_index(*data_col.index(), &chain.spec);

View File

@@ -9,7 +9,7 @@ use std::sync::Arc;
use tracing::{debug, error}; use tracing::{debug, error};
use types::core::{EthSpec, Hash256}; use types::core::{EthSpec, Hash256};
use types::data::{ use types::data::{
CellBitmap, PartialDataColumn, PartialDataColumnHeader, PartialDataColumnPartsMetadata, PartialDataColumn, PartialDataColumnHeader, PartialDataColumnPartsMetadata,
PartialDataColumnSidecar, PartialDataColumnSidecarRef, PartialDataColumnSidecar, PartialDataColumnSidecarRef,
}; };
@@ -32,12 +32,8 @@ impl<E: EthSpec> OutgoingPartialColumn<E> {
header_sent_set: HeaderSentSet, header_sent_set: HeaderSentSet,
) -> Self { ) -> Self {
// For now, always request all cells // For now, always request all cells
let mut requests = partial_column.sidecar.cells_present_bitmap.clone(); let mut requests = partial_column.sidecar.cells_present_bitmap.clone_zeroed();
for idx in 0..requests.len() { requests.not_inplace();
requests
.set(idx, true)
.expect("Bound asserted via `len` above");
}
let metadata = PartialDataColumnPartsMetadata::<E> { let metadata = PartialDataColumnPartsMetadata::<E> {
available: partial_column.sidecar.cells_present_bitmap.clone(), available: partial_column.sidecar.cells_present_bitmap.clone(),
requests, requests,
@@ -45,10 +41,7 @@ impl<E: EthSpec> OutgoingPartialColumn<E> {
.into(); .into();
let header_message = PartialDataColumnSidecarRef { let header_message = PartialDataColumnSidecarRef {
cells_present_bitmap: CellBitmap::<E>::with_capacity( cells_present_bitmap: partial_column.sidecar.cells_present_bitmap.clone_zeroed(),
partial_column.sidecar.cells_present_bitmap.len(),
)
.expect("Taking length from bitmap with same bound"),
column: vec![], column: vec![],
kzg_proofs: vec![], kzg_proofs: vec![],
header: Some(header).into(), header: Some(header).into(),
@@ -210,7 +203,7 @@ impl<E: EthSpec> Partial for OutgoingPartialColumn<E> {
let send = self let send = self
.partial_column .partial_column
.sidecar .sidecar
.filter(|idx| want.get(idx).expect("Bound checked above")) .filter(|idx| want.get(idx).unwrap_or(false))
.map_err(|err| { .map_err(|err| {
error!(?err, "Unexpected error filtering sidecar"); error!(?err, "Unexpected error filtering sidecar");
PartialError::InvalidFormat PartialError::InvalidFormat
@@ -262,6 +255,7 @@ mod tests {
use fixed_bytes::FixedBytesExtended; use fixed_bytes::FixedBytesExtended;
use libp2p::identity::Keypair; use libp2p::identity::Keypair;
use ssz_types::FixedVector; use ssz_types::FixedVector;
use types::CellBitmap;
use types::block::{BeaconBlockHeader, SignedBeaconBlockHeader}; use types::block::{BeaconBlockHeader, SignedBeaconBlockHeader};
use types::core::{MinimalEthSpec, Slot}; use types::core::{MinimalEthSpec, Slot};
use types::data::PartialDataColumnHeader; use types::data::PartialDataColumnHeader;

View File

@@ -1381,16 +1381,20 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
&[&data_column_index.to_string()], &[&data_column_index.to_string()],
); );
let mut column = col.to_partial(); match col.to_partial() {
let header = column.sidecar.header.take(); Ok(mut column) => {
if let Some(header) = header { let header = column.sidecar.header.take();
self.send_network_message(NetworkMessage::PublishPartialColumns { if let Some(header) = header {
columns: vec![Arc::new(column)], self.send_network_message(NetworkMessage::PublishPartialColumns {
header: Arc::new(header), columns: vec![Arc::new(column)],
}); header: Arc::new(header),
} else { });
crit!("Converting from full to partial yielded headerless partial") } else {
}; crit!("Converting from full to partial yielded headerless partial")
};
}
Err(err) => crit!(?err, "Could not convert from full to partial"),
}
} }
let result = self let result = self

View File

@@ -250,19 +250,16 @@ impl<E: EthSpec> DataColumnSidecarFulu<E> {
} }
/// Convert this full data column into a verifiable partial data column. /// Convert this full data column into a verifiable partial data column.
pub fn to_partial(&self) -> PartialDataColumn<E> { /// Note: This is not expected to ever fail.
pub fn to_partial(&self) -> Result<PartialDataColumn<E>, PartialDataColumnSidecarError> {
let cell_count = self.column.len(); let cell_count = self.column.len();
let mut bitmap = let mut bitmap = CellBitmap::<E>::with_capacity(cell_count)
CellBitmap::<E>::with_capacity(cell_count).expect("our column has the same bound"); .map_err(|_| PartialDataColumnSidecarError::UnexpectedBounds)?;
for idx in 0..cell_count { bitmap.not_inplace();
bitmap
.set(idx, true)
.expect("The correct size is initialized right above");
}
let block_root = self.block_root(); let block_root = self.block_root();
PartialDataColumn { Ok(PartialDataColumn {
block_root, block_root,
index: self.index, index: self.index,
sidecar: PartialDataColumnSidecar { sidecar: PartialDataColumnSidecar {
@@ -276,7 +273,7 @@ impl<E: EthSpec> DataColumnSidecarFulu<E> {
}) })
.into(), .into(),
}, },
} })
} }
} }