Add Gloas data column support (#8682)

Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>
This commit is contained in:
Eitan Seri-Levi
2026-01-27 20:52:12 -08:00
committed by GitHub
parent 0f57fc9d8e
commit 9bec8df37a
44 changed files with 1507 additions and 680 deletions

View File

@@ -7,10 +7,11 @@ use kzg::{KzgCommitment, KzgProof};
use merkle_proof::verify_merkle_proof;
use safe_arith::ArithError;
use serde::{Deserialize, Serialize};
use ssz::Encode;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use ssz_types::Error as SszError;
use ssz_types::{FixedVector, VariableList};
use superstruct::superstruct;
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
@@ -38,15 +39,43 @@ pub struct DataColumnsByRootIdentifier<E: EthSpec> {
pub type DataColumnSidecarList<E> = Vec<Arc<DataColumnSidecar<E>>>;
#[superstruct(
variants(Fulu, Gloas),
variant_attributes(
derive(
Debug,
Clone,
Serialize,
Deserialize,
Decode,
Encode,
TestRandom,
Educe,
TreeHash,
),
context_deserialize(ForkName),
educe(PartialEq, Hash(bound(E: EthSpec))),
serde(bound = "E: EthSpec", deny_unknown_fields),
cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)
),
ref_attributes(derive(TreeHash), tree_hash(enum_behaviour = "transparent")),
cast_error(ty = "DataColumnSidecarError", expr = "DataColumnSidecarError::IncorrectStateVariant"),
partial_getter_error(ty = "DataColumnSidecarError", expr = "DataColumnSidecarError::IncorrectStateVariant")
)]
#[cfg_attr(
feature = "arbitrary",
derive(arbitrary::Arbitrary),
arbitrary(bound = "E: EthSpec")
)]
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, Educe)]
#[serde(bound = "E: EthSpec")]
#[educe(PartialEq, Eq, Hash(bound(E: EthSpec)))]
#[context_deserialize(ForkName)]
#[derive(Debug, Clone, Serialize, TreeHash, Encode, Educe, Deserialize)]
#[educe(PartialEq, Hash(bound(E: EthSpec)))]
#[serde(bound = "E: EthSpec", untagged, deny_unknown_fields)]
#[tree_hash(enum_behaviour = "transparent")]
#[ssz(enum_behaviour = "transparent")]
pub struct DataColumnSidecar<E: EthSpec> {
#[serde(with = "serde_utils::quoted_u64")]
pub index: ColumnIndex,
@@ -55,20 +84,63 @@ pub struct DataColumnSidecar<E: EthSpec> {
/// All the KZG commitments and proofs associated with the block, used for verifying sample cells.
pub kzg_commitments: KzgCommitments<E>,
pub kzg_proofs: VariableList<KzgProof, E::MaxBlobCommitmentsPerBlock>,
#[superstruct(only(Fulu))]
pub signed_block_header: SignedBeaconBlockHeader,
/// An inclusion proof, proving the inclusion of `blob_kzg_commitments` in `BeaconBlockBody`.
#[superstruct(only(Fulu))]
pub kzg_commitments_inclusion_proof: FixedVector<Hash256, E::KzgCommitmentsInclusionProofDepth>,
#[superstruct(only(Gloas), partial_getter(rename = "slot_gloas"))]
pub slot: Slot,
#[superstruct(only(Gloas))]
pub beacon_block_root: Hash256,
}
impl<E: EthSpec> DataColumnSidecar<E> {
pub fn slot(&self) -> Slot {
self.signed_block_header.message.slot
match self {
DataColumnSidecar::Fulu(column) => column.slot(),
DataColumnSidecar::Gloas(column) => column.slot,
}
}
pub fn epoch(&self) -> Epoch {
self.slot().epoch(E::slots_per_epoch())
}
pub fn block_root(&self) -> Hash256 {
match self {
DataColumnSidecar::Fulu(column) => column.block_root(),
DataColumnSidecar::Gloas(column) => column.beacon_block_root,
}
}
/// Custom SSZ decoder that takes a `ForkName` as context.
pub fn from_ssz_bytes_for_fork(
bytes: &[u8],
fork_name: ForkName,
) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base
| ForkName::Altair
| ForkName::Bellatrix
| ForkName::Capella
| ForkName::Deneb
| ForkName::Electra => Err(ssz::DecodeError::NoMatchingVariant),
ForkName::Fulu => Ok(DataColumnSidecar::Fulu(
DataColumnSidecarFulu::from_ssz_bytes(bytes)?,
)),
ForkName::Gloas => Ok(DataColumnSidecar::Gloas(
DataColumnSidecarGloas::from_ssz_bytes(bytes)?,
)),
}
}
}
impl<E: EthSpec> DataColumnSidecarFulu<E> {
pub fn slot(&self) -> Slot {
self.signed_block_header.message.slot
}
pub fn block_root(&self) -> Hash256 {
self.signed_block_header.message.tree_hash_root()
}
@@ -132,6 +204,39 @@ impl<E: EthSpec> DataColumnSidecar<E> {
}
}
impl<E: EthSpec> DataColumnSidecarGloas<E> {
pub fn min_size() -> usize {
// min size is one cell
Self {
index: 0,
column: VariableList::new(vec![Cell::<E>::default()]).unwrap(),
kzg_commitments: VariableList::new(vec![KzgCommitment::empty_for_testing()]).unwrap(),
kzg_proofs: VariableList::new(vec![KzgProof::empty()]).unwrap(),
slot: Slot::new(0),
beacon_block_root: Hash256::ZERO,
}
.as_ssz_bytes()
.len()
}
pub fn max_size(max_blobs_per_block: usize) -> usize {
Self {
index: 0,
column: VariableList::new(vec![Cell::<E>::default(); max_blobs_per_block]).unwrap(),
kzg_commitments: VariableList::new(vec![
KzgCommitment::empty_for_testing();
max_blobs_per_block
])
.unwrap(),
kzg_proofs: VariableList::new(vec![KzgProof::empty(); max_blobs_per_block]).unwrap(),
slot: Slot::new(0),
beacon_block_root: Hash256::ZERO,
}
.as_ssz_bytes()
.len()
}
}
#[derive(Debug)]
pub enum DataColumnSidecarError {
ArithError(ArithError),
@@ -145,6 +250,7 @@ pub enum DataColumnSidecarError {
SszError(SszError),
BuildSidecarFailed(String),
InvalidCellProofLength { expected: usize, actual: usize },
IncorrectStateVariant,
}
impl From<ArithError> for DataColumnSidecarError {

View File

@@ -13,7 +13,8 @@ pub use data_column_custody_group::{
};
pub use data_column_sidecar::{
Cell, ColumnIndex, DataColumn, DataColumnSidecar, DataColumnSidecarError,
DataColumnSidecarList, DataColumnsByRootIdentifier,
DataColumnSidecarFulu, DataColumnSidecarGloas, DataColumnSidecarList,
DataColumnsByRootIdentifier,
};
pub use data_column_subnet_id::{DataColumnSubnetId, all_data_column_sidecar_subnets_from_spec};