feat(builder): observe beacon node blocks - #48
Draft
krisoshea-eth wants to merge 8 commits into
Draft
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
krisoshea-eth
marked this pull request as draft
August 10, 2026 21:03
…-events-and-retrieve-fork-correct
7 tasks
…-events-and-retrieve-fork-correct
This was referenced Aug 13, 2026
…-events-and-retrieve-fork-correct
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
The Lodestar Builder has no source-BN observation path for learning that a proposer selected a bid. API-02 requires this signal to come from the Builder's trusted beacon node REST API, without adding a libp2p subscription.
Current behavior
Proposed behavior
BlockObserverthat subscribes only to the standardblockSSE topic.getBlockV2, passing the Builder abort signal to both the event stream and HTTP request.retry()utility because the API client's built-in retries do not provide the status predicate needed here and would also retry deterministic 4xx responses.signedExecutionPayloadBid.BUILDER_INDEX_SELF_BUILD.runOnBlockcallbacks so one slow or failing consumer cannot block another. Callback cancellation is silent; genuine callback failures are logged.start(), because roots observed before registration remain consumed until FIFO eviction.This PR does not add p2p,
block_gossip, canonical-chain filtering, local-bid matching, reveal behavior, metrics, reconnect, replay, restart recovery, multi-BN failover, or a new API endpoint.API audit
The standard
blockevent contains slot, beacon block root, and execution optimism, but not fork version, Builder index, or execution block hash.getBlockV2supplies the signed fork-correct block andEth-Consensus-Versionmetadata, so the standard flow is sufficient for correctness at the cost of one fetch per newly observed post-Gloas block.headandhead_v2are not substitutes because they describe the current head and can omit an imported non-head block;head_v2also reports payload status rather than selected-bid identity.The sibling
execution_payloadevent already usesbuilder_index,block_hash, andblock_root, but is emitted only after a signed payload envelope is received and imported. It therefore carries the relevant selection identity too late to trigger reveal. A future enrichedblockevent or dedicatedbid_includedevent could reuse those existing field names.Lodestar emits the
blockevent after state transition and fork-choice import. Its root lookup checks fork choice for presence, then serves the block from the seen-block input cache or database, with cache pruning after the database write. Lodestar therefore has no expected event-before-block 404 window. The Beacon API does not guarantee equivalent ordering across clients, so the Builder still applies bounded 404 retry as a cross-client precaution. This implementation provides evidence for ethereum/beacon-APIs#599, but no API change is required for correctness.On Node 24.13.0, Lodestar uses its npm
eventsourcefallback. Connection establishment and BN unavailability surface asynchronously through the stream error callback; the eventstream promise rejection covers setup failures rather than connectivity. The current Builder CLI supplies one BN URL, although the shared API client would otherwise pin SSE to its first URL while allowing ordinary REST fallbacks. The Beacon API event-stream contract defines neither an SSEidnorLast-Event-IDresumption, so no conforming client can be assumed to provide exact replay. ENV-01 owns connected/reconnecting shutdown evidence, REL-01 owns bounded same-source reconciliation, and the existing deferred multi-BN and advanced replay issues retain the wider work.The detailed audit and decision record are in Builder docs PR #13. The implementation evidence was posted to beacon-APIs #599.
Baseline: ChainSafe/lodestar
unstableat713b21812905bb34e547ca85d5f630060179da67.Specification: consensus-specs
v1.7.0-alpha.13.Project issue: #12.
Testing
pnpm vitest run --project unit packages/builder/test/unit/services/blockObserver.test.tspnpm --filter @lodestar/builder test:unitpnpm --filter @lodestar/builder check-typespnpm --filter @lodestar/builder lintpnpm --filter @lodestar/builder buildpnpm --filter @lodestar/builder check-buildgit diff --checkUsing Node 24.13.0, all focused checks pass. The observer suite contains 25 tests. The complete Builder package suite contains 31 passing tests, including the six existing Builder signer tests.
Coverage includes subscription wiring and attempt logging, unexpected-event defense, Gloas and Heze fork-correct signed-bid identity, returned-slot consistency, sequential and concurrent duplicates, bounded 404 and 5xx recovery, persistent 404 exhaustion, retry classification, deterministic cancellation, fork gates, response decoding failures, metadata/body disagreement, FIFO eviction, self-build, expected and unexpected stream closure, stream and setup failures, concurrent callback isolation, callback cancellation, and shared abort-signal use.
The clean baseline passed the existing Builder type-check and six Builder unit tests after building the Builder dependency graph.
AI Assistance Disclosure