Resolve builder index from chain state instead of only lifecycle registration - #159
Closed
damilolaedwards wants to merge 1 commit into
Closed
Conversation
…stration RevealService and the epbs Builder API handler only learned the on-chain builder index through a lifecycle registration callback, which never fires when the process runs without --el-rpc/--wallet-privkey. A builder already registered on-chain in that setup would sign every bid and reveal envelope with index 0, producing consensus-invalid messages with no error surfaced anywhere. Both now resolve the index from chain state directly (GetBuilderByPubkey) at startup, matching the pattern the p2p bidder already uses. The lifecycle callback still updates the index live when registration happens during the run.
damilolaedwards
force-pushed
the
fix-builder-index-lifecycle-fallback
branch
from
August 10, 2026 20:27
09f150a to
cb0724e
Compare
pk910
added a commit
that referenced
this pull request
Aug 12, 2026
PR #159 fixes RevealService and the epbs Builder API handler signing with BuilderIndex=0 when the process runs without --lifecycle: both learned the index only from the lifecycle registration callback, which never fires without --el-rpc / --wallet-privkey. The managed key set removed both single-index caches. Every key's index now comes from chainSvc.GetBuilders() in Registry.Refresh(), so the fleet resolves its indexes with no callback involved, and the p2p bid and reveal paths refuse a key that reads unregistered instead of signing with the zero value. The Builder API bid path was the one site still discarding that flag. Key selection only offers active keys, which by definition carry an on-chain index, but signing with the zero value there would be silent: the bid is well-formed and served, its signature just belongs to whoever holds builder index 0. It now takes the same 204 path as "no key ready". Cover the property the whole fleet's ability to bid rests on: the registry resolving indexes from beacon state alone.
Member
|
Heya @damilolaedwards |
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.
Problem
RevealService and the epbs Builder API handler only learn the on-chain
builder index through a lifecycle registration callback wired in
cmd/run.go, gated behindlifecycleMgr != nil && epbsSvc != nil. Thatcallback only fires when the process runs with
--el-rpcand--wallet-privkey.Run
--epbswithout those flags and a builder that is already registeredon-chain will sign every bid and reveal envelope with
BuilderIndex=0instead of its real index. This happens silently: the envelope still gets
signed and published, the payload builder still marks it a success, and
nothing logs an error. The signature just won't verify against the right
builder, so the bid or reveal is rejected downstream.
Fix
Resolve the builder index directly from chain state
(
chainSvc.GetBuilderByPubkey(pubkey).Index) at startup, in bothRevealService.Start()andepbs.NewHandler(), instead of relying solely onthe lifecycle callback. This mirrors the pattern
p2p_bidderalready usescorrectly for the same value. The lifecycle callback still updates the index
live if registration happens while the process is running.
Testing
Added a test per package proving the index is picked up from chain state
without the lifecycle callback ever firing:
TestRevealService_ResolvesBuilderIndexFromChainStateOnStartTestNewHandler_ResolvesBuilderIndexFromChainStatego test ./pkg/...passes (aside from a pre-existing, unrelated failure inpkg/webuicaused by the frontend not being built in this checkout).