From 510fbdcb168446c65072c041f5893db36c5e4b42 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 09:47:15 -0700 Subject: [PATCH 1/8] fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec --- .../common/fork_choice/beacon/src/handlers.rs | 13 ++-- crates/common/fork_choice/beacon/src/store.rs | 62 +++---------------- crates/storage/src/db/mod.rs | 2 + testing/ef-tests/src/macros/fork_choice.rs | 19 +++--- 4 files changed, 24 insertions(+), 72 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 84e5fa524..20dcec71c 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -1,8 +1,7 @@ use alloy_primitives::{B256, map::HashSet}; use anyhow::{anyhow, ensure}; use ream_consensus_beacon::{ - attestation::Attestation, attester_slashing::AttesterSlashing, - electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data, + attestation::Attestation, attester_slashing::AttesterSlashing, electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data }; use ream_consensus_misc::{ constants::beacon::INTERVALS_PER_SLOT, misc::compute_start_slot_at_epoch, @@ -47,9 +46,11 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) - let finalized_slot = - compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); - ensure!(block.slot > finalized_slot); + let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); + ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", + block.slot, + finalized_slot + ); // Check block is a descendant of the finalized block at the checkpoint finalized slot let finalized_checkpoint_block = store.get_checkpoint_block( @@ -63,7 +64,7 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available - ensure!(store.is_data_available(block.tree_hash_root(), &block.body.blob_kzg_commitments)?); + ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index 2b61aa1ab..2d516f3a4 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -6,7 +6,6 @@ use hashbrown::HashMap; use ream_bls::BLSSignature; use ream_consensus_beacon::{ attestation::Attestation, - blob_sidecar::BlobIdentifier, data_column_sidecar::{ColumnIdentifier, DataColumnSidecar, NUMBER_OF_COLUMNS}, electra::{ beacon_block::{BeaconBlock, SignedBeaconBlock}, @@ -19,13 +18,10 @@ use ream_consensus_misc::{ checkpoint::Checkpoint, constants::beacon::{GENESIS_EPOCH, GENESIS_SLOT, INTERVALS_PER_SLOT, SLOTS_PER_EPOCH}, misc::{compute_epoch_at_slot, compute_start_slot_at_epoch, is_shuffling_stable}, - polynomial_commitments::kzg_commitment::KZGCommitment, }; use ream_network_spec::networks::beacon_network_spec; use ream_operation_pool::OperationPool; -use ream_polynomial_commitments::handlers::{ - verify_blob_kzg_proof_batch, verify_data_column_sidecar_kzg_proofs, -}; +use ream_polynomial_commitments::handlers::verify_data_column_sidecar_kzg_proofs; use ream_storage::{ db::beacon::BeaconDB, tables::{ @@ -737,8 +733,7 @@ impl Store { /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available pub fn is_data_available( &self, - beacon_block_root: B256, - blob_kzg_commitments: &[KZGCommitment], + beacon_block_root: B256 ) -> anyhow::Result { // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column @@ -747,57 +742,16 @@ impl Store { // `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs. let column_sidecars = self.retrieve_column_sidecars(beacon_block_root)?; - if !column_sidecars.is_empty() { - // Fulu column sidecars validation - for column_sidecar in column_sidecars { - if !column_sidecar.verify() { - return Ok(false); - } - if !verify_data_column_sidecar_kzg_proofs(&column_sidecar)? { - return Ok(false); - } - } - Ok(true) - } else { - // Fallback to Deneb blobs validation - if blob_kzg_commitments.is_empty() { - return Ok(true); + // Fulu column sidecars validation + for column_sidecar in column_sidecars { + if !column_sidecar.verify() { + return Ok(false); } - - self.is_blob_data_available(beacon_block_root, blob_kzg_commitments) - } - } - - /// Check if blob data is available for a Deneb block. - /// - /// Spec: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available - fn is_blob_data_available( - &self, - beacon_block_root: B256, - blob_kzg_commitments: &[KZGCommitment], - ) -> anyhow::Result { - let mut blobs = Vec::with_capacity(blob_kzg_commitments.len()); - let mut proofs = Vec::with_capacity(blob_kzg_commitments.len()); - - // Retrieve all blobs and proofs - for index in 0..blob_kzg_commitments.len() { - let blob_id = BlobIdentifier::new(beacon_block_root, index as u64); - if let Some(blob_and_proof) = self.db.blobs_and_proofs_provider().get(blob_id)? { - blobs.push(blob_and_proof.blob); - proofs.push(blob_and_proof.proof); - } else { - // Data not available + if !verify_data_column_sidecar_kzg_proofs(&column_sidecar)? { return Ok(false); } } - - // Verify the number of blobs matches - if blobs.len() != blob_kzg_commitments.len() { - return Ok(false); - } - - // Verify blob KZG proofs - verify_blob_kzg_proof_batch(&blobs, blob_kzg_commitments, &proofs) + Ok(true) } /// Retrieve column sidecars for a block. diff --git a/crates/storage/src/db/mod.rs b/crates/storage/src/db/mod.rs index e1fb2b59c..38e281406 100644 --- a/crates/storage/src/db/mod.rs +++ b/crates/storage/src/db/mod.rs @@ -13,6 +13,7 @@ use crate::{ errors::StoreError, tables::{ beacon::{ + column_sidecars::COLUMN_FOLDER_NAME, beacon_block::BeaconBlockTable, beacon_state::BeaconStateTable, blobs_and_proofs::BLOB_FOLDER_NAME, block_timeliness::BlockTimelinessTable, checkpoint_states::CheckpointStatesTable, @@ -89,6 +90,7 @@ impl ReamDB { write_txn.commit()?; fs::create_dir_all(self.data_dir.join(BLOB_FOLDER_NAME))?; + fs::create_dir_all(self.data_dir.join(COLUMN_FOLDER_NAME))?; Ok(BeaconDB { db: self.db.clone(), diff --git a/testing/ef-tests/src/macros/fork_choice.rs b/testing/ef-tests/src/macros/fork_choice.rs index 1c6cdda32..170c911ba 100644 --- a/testing/ef-tests/src/macros/fork_choice.rs +++ b/testing/ef-tests/src/macros/fork_choice.rs @@ -9,6 +9,7 @@ macro_rules! test_fork_choice { use alloy_primitives::{hex, map::HashMap, B256, hex::FromHex}; use ream_bls::BLSSignature; use ream_consensus_beacon::{ + data_column_sidecar::{ColumnIdentifier, DataColumnSidecar}, attestation::Attestation, attester_slashing::AttesterSlashing, blob_sidecar::BlobIdentifier, electra::{beacon_block::{BeaconBlock, SignedBeaconBlock}, beacon_state::BeaconState}, }; use ream_consensus_misc::{checkpoint::Checkpoint, polynomial_commitments::kzg_proof::KZGProof}; @@ -67,8 +68,7 @@ macro_rules! test_fork_choice { #[derive(Debug, Deserialize)] pub struct Block { pub block: String, - pub blobs: Option, - pub proofs: Option>, + pub columns: Option>, pub valid: Option, } @@ -152,16 +152,11 @@ macro_rules! test_fork_choice { panic!("cannot find test asset (block_{blocks:?}.ssz_snappy)") }); - if let (Some(blobs), Some(proof)) = (blocks.blobs, blocks.proofs) { - let blobs_path = case_dir.join(format!("{}.ssz_snappy", blobs)); - let blobs: VariableList = utils::read_ssz_snappy(&blobs_path).expect("Could not read blob file."); - let proof: Vec = proof - .into_iter() - .map(|proof| KZGProof::from_hex(proof).expect("could not get KZGProof")) - .collect(); - let blobs_and_proofs = blobs.into_iter().zip(proof.into_iter()).map(|(blob, proof)| BlobAndProofV1 { blob, proof } ).collect::>(); - for (index, blob_and_proof) in blobs_and_proofs.into_iter().enumerate() { - store.db.blobs_and_proofs_provider().insert(BlobIdentifier::new(block.message.tree_hash_root(), index as u64), blob_and_proof)?; + if let Some(columns) = blocks.columns { + for (index, column) in columns.into_iter().enumerate() { + let column_path = case_dir.join(format!("{}.ssz_snappy", column)); + let column: DataColumnSidecar = utils::read_ssz_snappy(&column_path).expect("Could not read column file."); + store.db.column_sidecars_provider().insert(ColumnIdentifier::new(block.message.tree_hash_root(), index as u64), column)?; } } From be824dc9017a40c2ca8e075501255f3d57391935 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 14:13:07 -0700 Subject: [PATCH 2/8] feat: updated on_block to fulu --- crates/common/fork_choice/beacon/src/handlers.rs | 16 ++++++++++++---- crates/common/fork_choice/beacon/src/store.rs | 6 ++---- crates/storage/src/db/mod.rs | 3 +-- testing/ef-tests/src/macros/fork_choice.rs | 4 +++- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 20dcec71c..77e131b95 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -1,7 +1,8 @@ use alloy_primitives::{B256, map::HashSet}; use anyhow::{anyhow, ensure}; use ream_consensus_beacon::{ - attestation::Attestation, attester_slashing::AttesterSlashing, electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data + attestation::Attestation, attester_slashing::AttesterSlashing, + electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data, }; use ream_consensus_misc::{ constants::beacon::INTERVALS_PER_SLOT, misc::compute_start_slot_at_epoch, @@ -46,8 +47,11 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) - let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); - ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", + let finalized_slot = + compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); + ensure!( + block.slot > finalized_slot, + "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", block.slot, finalized_slot ); @@ -64,7 +68,11 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available - ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); + ensure!( + store.is_data_available(block.tree_hash_root())?, + "Data not available for block root: {:x}", + block.tree_hash_root() + ); } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index 2d516f3a4..4419d948e 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -731,16 +731,14 @@ impl Store { /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available - pub fn is_data_available( - &self, - beacon_block_root: B256 - ) -> anyhow::Result { + pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column // sidecars to sample, or raises an exception if they are not available. // The p2p network does not guarantee sidecar retrieval outside of // `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs. let column_sidecars = self.retrieve_column_sidecars(beacon_block_root)?; + ensure!(!column_sidecars.is_empty(), "No column sidecars available"); // Fulu column sidecars validation for column_sidecar in column_sidecars { diff --git a/crates/storage/src/db/mod.rs b/crates/storage/src/db/mod.rs index 38e281406..e1b4f974b 100644 --- a/crates/storage/src/db/mod.rs +++ b/crates/storage/src/db/mod.rs @@ -13,10 +13,9 @@ use crate::{ errors::StoreError, tables::{ beacon::{ - column_sidecars::COLUMN_FOLDER_NAME, beacon_block::BeaconBlockTable, beacon_state::BeaconStateTable, blobs_and_proofs::BLOB_FOLDER_NAME, block_timeliness::BlockTimelinessTable, - checkpoint_states::CheckpointStatesTable, + checkpoint_states::CheckpointStatesTable, column_sidecars::COLUMN_FOLDER_NAME, equivocating_indices::EQUIVOCATING_INDICES_FIELD, finalized_checkpoint::FinalizedCheckpointField, genesis_time::GenesisTimeField, justified_checkpoint::JustifiedCheckpointField, latest_messages::LatestMessagesTable, diff --git a/testing/ef-tests/src/macros/fork_choice.rs b/testing/ef-tests/src/macros/fork_choice.rs index 170c911ba..ff7002734 100644 --- a/testing/ef-tests/src/macros/fork_choice.rs +++ b/testing/ef-tests/src/macros/fork_choice.rs @@ -152,6 +152,8 @@ macro_rules! test_fork_choice { panic!("cannot find test asset (block_{blocks:?}.ssz_snappy)") }); + let verify_blob_availability = blocks.columns.is_some(); + if let Some(columns) = blocks.columns { for (index, column) in columns.into_iter().enumerate() { let column_path = case_dir.join(format!("{}.ssz_snappy", column)); @@ -160,7 +162,7 @@ macro_rules! test_fork_choice { } } - assert_eq!(on_block(&mut store, &block, &mock_engine, true).await.is_ok(), blocks.valid.unwrap_or(true), "Unexpected result on on_block"); + assert_eq!(on_block(&mut store, &block, &mock_engine, verify_blob_availability).await.is_ok(), blocks.valid.unwrap_or(true), "Unexpected result on on_block"); } ForkChoiceStep::Attestation(attestations) => { let attestation_path = From fe8cc394e979b2586dfabfc41311b12ee3ae2ff6 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 09:47:15 -0700 Subject: [PATCH 3/8] fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec --- crates/common/fork_choice/beacon/src/handlers.rs | 12 ++++++++++-- crates/common/fork_choice/beacon/src/store.rs | 7 +++++++ crates/storage/src/db/mod.rs | 1 + testing/ef-tests/src/macros/fork_choice.rs | 3 +++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 77e131b95..85dca1c20 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -1,8 +1,7 @@ use alloy_primitives::{B256, map::HashSet}; use anyhow::{anyhow, ensure}; use ream_consensus_beacon::{ - attestation::Attestation, attester_slashing::AttesterSlashing, - electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data, + attestation::Attestation, attester_slashing::AttesterSlashing, electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data }; use ream_consensus_misc::{ constants::beacon::INTERVALS_PER_SLOT, misc::compute_start_slot_at_epoch, @@ -47,11 +46,16 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) +<<<<<<< HEAD let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); ensure!( block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", +======= + let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); + ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) block.slot, finalized_slot ); @@ -68,11 +72,15 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available +<<<<<<< HEAD ensure!( store.is_data_available(block.tree_hash_root())?, "Data not available for block root: {:x}", block.tree_hash_root() ); +======= + ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index 4419d948e..f417a6445 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -731,7 +731,14 @@ impl Store { /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available +<<<<<<< HEAD pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { +======= + pub fn is_data_available( + &self, + beacon_block_root: B256 + ) -> anyhow::Result { +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column // sidecars to sample, or raises an exception if they are not available. diff --git a/crates/storage/src/db/mod.rs b/crates/storage/src/db/mod.rs index e1b4f974b..dae22d37c 100644 --- a/crates/storage/src/db/mod.rs +++ b/crates/storage/src/db/mod.rs @@ -13,6 +13,7 @@ use crate::{ errors::StoreError, tables::{ beacon::{ + column_sidecars::COLUMN_FOLDER_NAME, beacon_block::BeaconBlockTable, beacon_state::BeaconStateTable, blobs_and_proofs::BLOB_FOLDER_NAME, block_timeliness::BlockTimelinessTable, checkpoint_states::CheckpointStatesTable, column_sidecars::COLUMN_FOLDER_NAME, diff --git a/testing/ef-tests/src/macros/fork_choice.rs b/testing/ef-tests/src/macros/fork_choice.rs index ff7002734..c90e8824f 100644 --- a/testing/ef-tests/src/macros/fork_choice.rs +++ b/testing/ef-tests/src/macros/fork_choice.rs @@ -152,8 +152,11 @@ macro_rules! test_fork_choice { panic!("cannot find test asset (block_{blocks:?}.ssz_snappy)") }); +<<<<<<< HEAD let verify_blob_availability = blocks.columns.is_some(); +======= +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) if let Some(columns) = blocks.columns { for (index, column) in columns.into_iter().enumerate() { let column_path = case_dir.join(format!("{}.ssz_snappy", column)); From b6f8b3bf05d3e2d6c6876eec583b161a75228ffb Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 15:15:13 -0700 Subject: [PATCH 4/8] feat: update fork choice test harness to use columns and avoid blobs and proofs as noted in the spec --- crates/common/fork_choice/beacon/src/handlers.rs | 9 --------- crates/common/fork_choice/beacon/src/store.rs | 7 ------- crates/storage/src/db/mod.rs | 2 +- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 85dca1c20..d66252831 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -46,16 +46,11 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) -<<<<<<< HEAD let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); ensure!( block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", -======= - let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); - ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) block.slot, finalized_slot ); @@ -72,15 +67,11 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available -<<<<<<< HEAD ensure!( store.is_data_available(block.tree_hash_root())?, "Data not available for block root: {:x}", block.tree_hash_root() ); -======= - ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index f417a6445..4419d948e 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -731,14 +731,7 @@ impl Store { /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available -<<<<<<< HEAD pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { -======= - pub fn is_data_available( - &self, - beacon_block_root: B256 - ) -> anyhow::Result { ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column // sidecars to sample, or raises an exception if they are not available. diff --git a/crates/storage/src/db/mod.rs b/crates/storage/src/db/mod.rs index dae22d37c..38e281406 100644 --- a/crates/storage/src/db/mod.rs +++ b/crates/storage/src/db/mod.rs @@ -16,7 +16,7 @@ use crate::{ column_sidecars::COLUMN_FOLDER_NAME, beacon_block::BeaconBlockTable, beacon_state::BeaconStateTable, blobs_and_proofs::BLOB_FOLDER_NAME, block_timeliness::BlockTimelinessTable, - checkpoint_states::CheckpointStatesTable, column_sidecars::COLUMN_FOLDER_NAME, + checkpoint_states::CheckpointStatesTable, equivocating_indices::EQUIVOCATING_INDICES_FIELD, finalized_checkpoint::FinalizedCheckpointField, genesis_time::GenesisTimeField, justified_checkpoint::JustifiedCheckpointField, latest_messages::LatestMessagesTable, From 0cc2001a621102981faac693474ce490ab5269b7 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 09:47:15 -0700 Subject: [PATCH 5/8] fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec --- crates/common/fork_choice/beacon/src/handlers.rs | 9 +++++++++ crates/common/fork_choice/beacon/src/store.rs | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index d66252831..85dca1c20 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -46,11 +46,16 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) +<<<<<<< HEAD let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); ensure!( block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", +======= + let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); + ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) block.slot, finalized_slot ); @@ -67,11 +72,15 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available +<<<<<<< HEAD ensure!( store.is_data_available(block.tree_hash_root())?, "Data not available for block root: {:x}", block.tree_hash_root() ); +======= + ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index 4419d948e..f417a6445 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -731,7 +731,14 @@ impl Store { /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available +<<<<<<< HEAD pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { +======= + pub fn is_data_available( + &self, + beacon_block_root: B256 + ) -> anyhow::Result { +>>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column // sidecars to sample, or raises an exception if they are not available. From cdfb9b95c1e208316e9531f8bc9300328703c8f8 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 14:13:07 -0700 Subject: [PATCH 6/8] feat: updated on_block to fulu --- crates/common/fork_choice/beacon/src/handlers.rs | 12 ++---------- crates/common/fork_choice/beacon/src/store.rs | 7 ------- crates/storage/src/db/mod.rs | 3 +-- testing/ef-tests/src/macros/fork_choice.rs | 3 --- 4 files changed, 3 insertions(+), 22 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 85dca1c20..77e131b95 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -1,7 +1,8 @@ use alloy_primitives::{B256, map::HashSet}; use anyhow::{anyhow, ensure}; use ream_consensus_beacon::{ - attestation::Attestation, attester_slashing::AttesterSlashing, electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data + attestation::Attestation, attester_slashing::AttesterSlashing, + electra::beacon_block::SignedBeaconBlock, predicates::is_slashable_attestation_data, }; use ream_consensus_misc::{ constants::beacon::INTERVALS_PER_SLOT, misc::compute_start_slot_at_epoch, @@ -46,16 +47,11 @@ pub async fn on_block( // Check that block is later than the finalized epoch slot (optimization to reduce calls to // get_ancestor) -<<<<<<< HEAD let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); ensure!( block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", -======= - let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); - ensure!(block.slot > finalized_slot, "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) block.slot, finalized_slot ); @@ -72,15 +68,11 @@ pub async fn on_block( // available *Note*: Extraneous or invalid data (in addition to the // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available -<<<<<<< HEAD ensure!( store.is_data_available(block.tree_hash_root())?, "Data not available for block root: {:x}", block.tree_hash_root() ); -======= - ensure!(store.is_data_available(block.tree_hash_root()).unwrap_or(false)); ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) } // Check the block is valid and compute the post-state diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index f417a6445..4419d948e 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -731,14 +731,7 @@ impl Store { /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available -<<<<<<< HEAD pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { -======= - pub fn is_data_available( - &self, - beacon_block_root: B256 - ) -> anyhow::Result { ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column // sidecars to sample, or raises an exception if they are not available. diff --git a/crates/storage/src/db/mod.rs b/crates/storage/src/db/mod.rs index 38e281406..e1b4f974b 100644 --- a/crates/storage/src/db/mod.rs +++ b/crates/storage/src/db/mod.rs @@ -13,10 +13,9 @@ use crate::{ errors::StoreError, tables::{ beacon::{ - column_sidecars::COLUMN_FOLDER_NAME, beacon_block::BeaconBlockTable, beacon_state::BeaconStateTable, blobs_and_proofs::BLOB_FOLDER_NAME, block_timeliness::BlockTimelinessTable, - checkpoint_states::CheckpointStatesTable, + checkpoint_states::CheckpointStatesTable, column_sidecars::COLUMN_FOLDER_NAME, equivocating_indices::EQUIVOCATING_INDICES_FIELD, finalized_checkpoint::FinalizedCheckpointField, genesis_time::GenesisTimeField, justified_checkpoint::JustifiedCheckpointField, latest_messages::LatestMessagesTable, diff --git a/testing/ef-tests/src/macros/fork_choice.rs b/testing/ef-tests/src/macros/fork_choice.rs index c90e8824f..ff7002734 100644 --- a/testing/ef-tests/src/macros/fork_choice.rs +++ b/testing/ef-tests/src/macros/fork_choice.rs @@ -152,11 +152,8 @@ macro_rules! test_fork_choice { panic!("cannot find test asset (block_{blocks:?}.ssz_snappy)") }); -<<<<<<< HEAD let verify_blob_availability = blocks.columns.is_some(); -======= ->>>>>>> 510fbdc (fix: updated the fork choice test harness to use columns and avoid blobs and proofs as noted in the spec) if let Some(columns) = blocks.columns { for (index, column) in columns.into_iter().enumerate() { let column_path = case_dir.join(format!("{}.ssz_snappy", column)); From 4fc65adeed03e187303a1155de45f345b48ec8e0 Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 15:55:28 -0700 Subject: [PATCH 7/8] docs: updated the is_data_available function docs --- .../common/fork_choice/beacon/src/handlers.rs | 31 +++++++++---------- crates/common/fork_choice/beacon/src/store.rs | 1 - 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/crates/common/fork_choice/beacon/src/handlers.rs b/crates/common/fork_choice/beacon/src/handlers.rs index 77e131b95..e87a26507 100644 --- a/crates/common/fork_choice/beacon/src/handlers.rs +++ b/crates/common/fork_choice/beacon/src/handlers.rs @@ -28,21 +28,22 @@ pub async fn on_block( verify_blob_availability: bool, ) -> anyhow::Result<()> { let block = &signed_block.message; + let parent_root = block.parent_root; + let block_slot = block.slot; + let block_root = block.tree_hash_root(); // Parent block must be known ensure!( - store.db.state_provider().get(block.parent_root)?.is_some(), - "Missing parent block state for parent_root: {:x}", - block.parent_root + store.db.state_provider().get(parent_root)?.is_some(), + "Missing parent block state for parent_root: {parent_root:x}", ); // Blocks cannot be in the future. If they are, their consideration must be delayed until they // are in the past. + let current_slot = store.get_current_slot()?; ensure!( - store.get_current_slot()? >= block.slot, - "Block slot is ahead of current slot: block.slot = {}, store.get_current_slot() = {}", - block.slot, - store.get_current_slot()? + current_slot >= block_slot, + "Block slot is ahead of current slot: block.slot = {block_slot}, store.get_current_slot() = {current_slot}", ); // Check that block is later than the finalized epoch slot (optimization to reduce calls to @@ -50,15 +51,13 @@ pub async fn on_block( let finalized_slot = compute_start_slot_at_epoch(store.db.finalized_checkpoint_provider().get()?.epoch); ensure!( - block.slot > finalized_slot, - "Block slot must be greater than finalized slot: block.slot = {}, finalized_slot = {}", - block.slot, - finalized_slot + block_slot > finalized_slot, + "Block slot must be greater than finalized slot: block.slot = {block_slot}, finalized_slot = {finalized_slot}", ); // Check block is a descendant of the finalized block at the checkpoint finalized slot let finalized_checkpoint_block = store.get_checkpoint_block( - block.parent_root, + parent_root, store.db.finalized_checkpoint_provider().get()?.epoch, )?; ensure!(store.db.finalized_checkpoint_provider().get()?.root == finalized_checkpoint_block); @@ -69,9 +68,8 @@ pub async fn on_block( // expected/referenced valid data) received on the p2p network MUST NOT invalidate // a block that is otherwise valid and available ensure!( - store.is_data_available(block.tree_hash_root())?, - "Data not available for block root: {:x}", - block.tree_hash_root() + store.is_data_available(block_root)?, + "Data not available for block root: {block_root:x}", ); } @@ -80,10 +78,9 @@ pub async fn on_block( let mut state = store .db .state_provider() - .get(block.parent_root)? + .get(parent_root)? .ok_or_else(|| anyhow!("beacon state not found"))? .clone(); - let block_root = block.tree_hash_root(); state .state_transition(signed_block, true, execution_engine) .await?; diff --git a/crates/common/fork_choice/beacon/src/store.rs b/crates/common/fork_choice/beacon/src/store.rs index 4419d948e..975a77869 100644 --- a/crates/common/fork_choice/beacon/src/store.rs +++ b/crates/common/fork_choice/beacon/src/store.rs @@ -730,7 +730,6 @@ impl Store { /// Check if data is available for a block. /// /// For Fulu: https://ethereum.github.io/consensus-specs/specs/fulu/fork-choice/#modified-is_data_available - /// For Deneb: https://ethereum.github.io/consensus-specs/specs/deneb/fork-choice/#is_data_available pub fn is_data_available(&self, beacon_block_root: B256) -> anyhow::Result { // `retrieve_column_sidecars` is implementation and context dependent, replacing // `retrieve_blobs_and_proofs`. For the given block root, it returns all column From b7ae4d6b6bf2eafc0a29433b271fe8d7c87dbd7b Mon Sep 17 00:00:00 2001 From: Patchoulis Date: Thu, 19 Feb 2026 16:07:49 -0700 Subject: [PATCH 8/8] fix: ran clippy --- crates/storage/src/db/lean.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/src/db/lean.rs b/crates/storage/src/db/lean.rs index 95c7ea184..ce1f7901d 100644 --- a/crates/storage/src/db/lean.rs +++ b/crates/storage/src/db/lean.rs @@ -173,7 +173,7 @@ impl LeanDB { return Ok(()); } - table_metrics.sort_by(|a, b| b.1.cmp(&a.1)); + table_metrics.sort_by_key(|b| std::cmp::Reverse(b.1)); let mut report = String::with_capacity(512); let total_mb = total_bytes as f64 / (1024.0 * 1024.0); if total_mb >= 1024.0 {