Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/blockchain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1620,7 +1620,8 @@ mod tests {
},
};
let backend = Arc::new(InMemoryBackend::new());
let mut store = Store::get_forkchoice_store(backend, genesis_state, genesis_block);
let mut store = Store::get_forkchoice_store(backend, genesis_state, genesis_block)
.expect("anchor state and block must match");

let head_root = store.head();
let att_data = AttestationData {
Expand Down
3 changes: 2 additions & 1 deletion crates/blockchain/tests/forkchoice_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
let anchor_block: Block = test.anchor_block.into();
let genesis_time = anchor_state.config.genesis_time;
let backend = Arc::new(InMemoryBackend::new());
let mut store = Store::get_forkchoice_store(backend, anchor_state, anchor_block);
let mut store = Store::get_forkchoice_store(backend, anchor_state, anchor_block)
.expect("anchor state and block must match");

// Block registry: maps block labels to their roots
let mut block_registry: HashMap<String, H256> = HashMap::new();
Expand Down
3 changes: 2 additions & 1 deletion crates/blockchain/tests/signature_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
// Initialize the store with the anchor state and block
let genesis_time = anchor_state.config.genesis_time;
let backend = Arc::new(InMemoryBackend::new());
let mut st = Store::get_forkchoice_store(backend, anchor_state, anchor_block);
let mut st = Store::get_forkchoice_store(backend, anchor_state, anchor_block)
.expect("anchor state and block must match");

// Step 2: Run the state transition function with the block fixture
let signed_block: SignedBlock = test.signed_block.into();
Expand Down
1 change: 1 addition & 0 deletions crates/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ethlambda-types.workspace = true

tracing.workspace = true
rocksdb.workspace = true
thiserror.workspace = true

libssz.workspace = true
libssz-derive.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ pub mod backend;
mod store;

pub use api::{ALL_TABLES, StorageBackend, StorageReadView, StorageWriteBatch, Table};
pub use store::{ForkCheckpoints, Store};
pub use store::{ForkCheckpoints, GetForkchoiceStoreError, Store};
40 changes: 31 additions & 9 deletions crates/storage/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,23 @@ use ethlambda_types::{
state::{ChainConfig, State},
};
use libssz::{SszDecode, SszEncode};
use thiserror::Error;
use tracing::info;

/// Errors returned by [`Store::get_forkchoice_store`].
#[derive(Debug, Error)]
pub enum GetForkchoiceStoreError {
#[error(
"anchor block header doesn't match state's latest_block_header \
(compared with state_root zeroed): state header = {state_header:?}, \
block header = {block_header:?}"
)]
HeaderMismatch {
state_header: Box<BlockHeader>,
block_header: Box<BlockHeader>,
},
}

/// Checkpoints to update in the forkchoice store.
///
/// Used with `Store::update_checkpoints` to update head and optionally
Expand Down Expand Up @@ -470,27 +485,34 @@ impl Store {
/// The block must match the state's `latest_block_header`.
/// Named to mirror the spec's `get_forkchoice_store` function.
///
/// # Panics
/// # Errors
///
/// Panics if the block's header doesn't match the state's `latest_block_header`
/// (comparing all fields except `state_root`, which is computed internally).
/// Returns [`GetForkchoiceStoreError::HeaderMismatch`] if the block's header
/// doesn't match the state's `latest_block_header` (comparing all fields
/// except `state_root`, which is computed internally).
pub fn get_forkchoice_store(
backend: Arc<dyn StorageBackend>,
anchor_state: State,
anchor_block: Block,
) -> Self {
) -> Result<Self, GetForkchoiceStoreError> {
// Compare headers with state_root zeroed (init_store handles state_root separately)
let mut state_header = anchor_state.latest_block_header.clone();
let mut block_header = anchor_block.header();
state_header.state_root = H256::ZERO;
block_header.state_root = H256::ZERO;

assert_eq!(
state_header, block_header,
"block header doesn't match state's latest_block_header"
);
if state_header != block_header {
return Err(GetForkchoiceStoreError::HeaderMismatch {
state_header: Box::new(state_header),
block_header: Box::new(block_header),
});
}

Self::init_store(backend, anchor_state, Some(anchor_block.body))
Ok(Self::init_store(
backend,
anchor_state,
Some(anchor_block.body),
))
}

/// Internal helper to initialize the store with anchor data.
Expand Down