From 940fa81a5b974efd10143f71596a0b3dc0804df4 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Mon, 2 Feb 2026 17:41:43 +1100 Subject: [PATCH] Fast path for `/eth/v1/beacon/blocks/head/root` (#8729) Closes: - https://github.com/sigp/lighthouse/issues/8667 Use the `early_attester_cache` to serve the head block root (if present). This should be faster than waiting for the head to finish importing. Co-Authored-By: Michael Sproul --- .../beacon_chain/src/early_attester_cache.rs | 8 +++++++ beacon_node/http_api/src/lib.rs | 23 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/beacon_node/beacon_chain/src/early_attester_cache.rs b/beacon_node/beacon_chain/src/early_attester_cache.rs index 8b26cb2f58..8d9eb950f3 100644 --- a/beacon_node/beacon_chain/src/early_attester_cache.rs +++ b/beacon_node/beacon_chain/src/early_attester_cache.rs @@ -254,4 +254,12 @@ impl EarlyAttesterCache { .filter(|item| item.beacon_block_root == block_root) .map(|item| item.proto_block.clone()) } + + /// Fetch the slot and block root of the current head block. + pub fn get_head_block_root(&self) -> Option<(Slot, Hash256)> { + self.item + .read() + .as_ref() + .map(|item| (item.block.slot(), item.beacon_block_root)) + } } diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 58cd2a3bdb..4d7c76eb20 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -1190,7 +1190,28 @@ pub fn serve( Priority::P1 }; task_spawner.blocking_json_task(priority, move || { - let (block_root, execution_optimistic, finalized) = block_id.root(&chain)?; + // Fast-path for the head block root. We read from the early attester cache + // so that we can produce sync committee messages for the new head prior + // to it being fully imported (written to the DB/etc). We also check that the + // cache is not stale or out of date by comparing against the cached head + // prior to using it. + // + // See: https://github.com/sigp/lighthouse/issues/8667 + let (block_root, execution_optimistic, finalized) = + if let BlockId(eth2::types::BlockId::Head) = block_id + && let Some((head_block_slot, head_block_root)) = + chain.early_attester_cache.get_head_block_root() + && head_block_slot >= chain.canonical_head.cached_head().head_slot() + { + // We know execution is NOT optimistic if the block is from the early + // attester cache because only properly validated blocks are added. + // Similarly we know it is NOT finalized. + let execution_optimistic = false; + let finalized = false; + (head_block_root, execution_optimistic, finalized) + } else { + block_id.root(&chain)? + }; Ok( api_types::GenericResponse::from(api_types::RootData::from(block_root)) .add_execution_optimistic_finalized(execution_optimistic, finalized),