Emit NewHead SSE event earlier in block import (#8718)

Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
Jimmy Chen
2026-01-29 18:39:05 +11:00
committed by GitHub
parent 119dc565a1
commit cd8049a696
4 changed files with 107 additions and 38 deletions

View File

@@ -77,6 +77,7 @@ use bls::{PublicKey, PublicKeyBytes, Signature};
use eth2::beacon_response::ForkVersionedResponse;
use eth2::types::{
EventKind, SseBlobSidecar, SseBlock, SseDataColumnSidecar, SseExtendedPayloadAttributes,
SseHead,
};
use execution_layer::{
BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer,
@@ -3863,6 +3864,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
};
// Read the cached head prior to taking the fork choice lock to avoid potential deadlocks.
let old_head_slot = self.canonical_head.cached_head().head_slot();
// Take an upgradable read lock on fork choice so we can check if this block has already
// been imported. We don't want to repeat work importing a block that is already imported.
let fork_choice_reader = self.canonical_head.fork_choice_upgradable_read_lock();
@@ -3918,6 +3922,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// This block became the head, add it to the early attester cache.
Ok(new_head_root) if new_head_root == block_root => {
if let Some(proto_block) = fork_choice.get_block(&block_root) {
let new_head_is_optimistic =
proto_block.execution_status.is_optimistic_or_invalid();
if let Err(e) = self.early_attester_cache.add_head_block(
block_root,
&signed_block,
@@ -3937,6 +3944,50 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
attestable_timestamp,
)
}
// Register a server-sent-event for a new head.
if let Some(event_handler) = self
.event_handler
.as_ref()
.filter(|handler| handler.has_head_subscribers())
{
let head_slot = state.slot();
let state_root = block.state_root();
let is_epoch_transition = state.current_epoch()
> old_head_slot.epoch(T::EthSpec::slots_per_epoch());
let dependent_root = state.attester_shuffling_decision_root(
self.genesis_block_root,
RelativeEpoch::Next,
);
let prev_dependent_root = state.attester_shuffling_decision_root(
self.genesis_block_root,
RelativeEpoch::Current,
);
match (dependent_root, prev_dependent_root) {
(
Ok(current_duty_dependent_root),
Ok(previous_duty_dependent_root),
) => {
event_handler.register(EventKind::Head(SseHead {
slot: head_slot,
block: block_root,
state: state_root,
current_duty_dependent_root,
previous_duty_dependent_root,
epoch_transition: is_epoch_transition,
execution_optimistic: new_head_is_optimistic,
}));
}
(Err(e), _) | (_, Err(e)) => {
warn!(
error = ?e,
"Unable to find dependent roots, cannot register head event"
);
}
}
}
} else {
warn!(?block_root, "Early attester block missing");
}

View File

@@ -41,7 +41,7 @@ use crate::{
metrics,
validator_monitor::get_slot_delay_ms,
};
use eth2::types::{EventKind, SseChainReorg, SseFinalizedCheckpoint, SseHead, SseLateHead};
use eth2::types::{EventKind, SseChainReorg, SseFinalizedCheckpoint, SseLateHead};
use fork_choice::{
ExecutionStatus, ForkChoiceStore, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock,
ResetPayloadStatuses,
@@ -824,15 +824,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.slot()
.epoch(T::EthSpec::slots_per_epoch());
// These fields are used for server-sent events.
let state_root = new_snapshot.beacon_state_root();
// This field is used for server-sent events.
let head_slot = new_snapshot.beacon_state.slot();
let dependent_root = new_snapshot
.beacon_state
.attester_shuffling_decision_root(self.genesis_block_root, RelativeEpoch::Next);
let prev_dependent_root = new_snapshot
.beacon_state
.attester_shuffling_decision_root(self.genesis_block_root, RelativeEpoch::Current);
match BlockShufflingIds::try_from_head(
new_snapshot.beacon_block_root,
@@ -870,33 +863,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.op_pool.prune_attestations(self.epoch()?);
}
// Register server-sent-events for a new head.
if let Some(event_handler) = self
.event_handler
.as_ref()
.filter(|handler| handler.has_head_subscribers())
{
match (dependent_root, prev_dependent_root) {
(Ok(current_duty_dependent_root), Ok(previous_duty_dependent_root)) => {
event_handler.register(EventKind::Head(SseHead {
slot: head_slot,
block: new_snapshot.beacon_block_root,
state: state_root,
current_duty_dependent_root,
previous_duty_dependent_root,
epoch_transition: is_epoch_transition,
execution_optimistic: new_head_is_optimistic,
}));
}
(Err(e), _) | (_, Err(e)) => {
warn!(
error = ?e,
"Unable to find dependent roots, cannot register head event"
);
}
}
}
// Register a server-sent-event for a reorg (if necessary).
if let Some(depth) = reorg_distance
&& let Some(event_handler) = self