Skip to content

feat: gloas range sync#9242

Draft
twoeths wants to merge 49 commits intonc/alpha.5-vibefrom
nc/alpha.5-vibe-te/range_sync
Draft

feat: gloas range sync#9242
twoeths wants to merge 49 commits intonc/alpha.5-vibefrom
nc/alpha.5-vibe-te/range_sync

Conversation

@twoeths
Copy link
Copy Markdown
Contributor

@twoeths twoeths commented Apr 21, 2026

Motivation

Description

Range sync for gloas

  • processChainSegment carries Map<Slot, PayloadEnvelopeInput> end-to-end through batch → chain → block processor.
  • cacheByRangeResponses builds PayloadEnvelopeInput wrappers from downloaded envelopes + columns and seeds seenPayloadEnvelopeInputCache.
  • assertLinearChainSegment ported to validate the gloas execution-hash chain + envelope binding; returns orphaned-envelope warnings instead of throwing on bid.parentBlockHash mismatch (per feat: enhance processChainSegment for ePBS #9103 shape).
  • verifyBlock.ts awaits verifyPayloadsDataAvailability on the gloas path in parallel with state-transition + signature verification.
  • importExecutionPayload is now called inline from processBlocks (DA is already awaited upstream). Split out of the existing function: DA wait + import moved into a thin wrapper processExecutionPayload that the envelope queue calls instead.

Included block-production fixes (may be lifted into separate PRs)

These are needed for Node A to produce a FULL canonical chain that Node B can range-sync from, but are otherwise independent of the range-sync changes above:

  • produceBlockBody: bid.parentBlockHash = executionPayload.parentHash (previously currentState.latestBlockHash, which always encoded the EMPTY-parent hash).
  • persistPayloadEnvelopeInput: stop pruning seenPayloadEnvelopeInputCache immediately after DB persist — synchronous consumers still read the envelope from here on subsequent slots. Cache is now pruned only on finalization; TODO added to cap it under non-finality.
  • Fork choice shouldApplyProposerBoost: pick the right PayloadStatus variant when looking up the parent node instead of hard-coding PENDING (threw INVALID_NODE_INDEX for pre-gloas parents at the fork boundary).

Verified: pnpm vitest run --project e2e test/e2e/sync/finalizedSync.test.ts passes (sync / finalized sync for gloas, ~76 s).

AI Assistance Disclosure

Used Claude Code to assist with implementation and review.

nflaig and others added 30 commits April 13, 2026 16:21
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Nico Flaig <nflaig@protonmail.com>
Implement the `should_apply_proposer_boost` logic from consensus-specs
commit 71d1151 (PR #4807). This addresses the builder reveal safety
concern where a colluding next-slot proposer could use proposer boost
to override a legitimately revealed block.

Changes:
- Add `ptcTimeliness` and `proposerIndex` fields to ProtoBlock
- Add `isBlockPtcTimely` to track PTC deadline timeliness
- Add `shouldApplyProposerBoost` which withholds boost when the parent
  is a weak, equivocating block from the previous slot
- Add `findEquivocatingBlocks` in ProtoArray to detect proposer
  equivocations by scanning for PTC-timely blocks at the same slot
  from the same proposer
- Gate proposer boost in `getWeight` on `shouldApplyProposerBoost()`
- Pre-gloas blocks retain unconditional boost (backward compatible)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cs#5094)

- Add executionRequestsRoot to ExecutionPayloadBid
- Remove stateRoot from ExecutionPayloadEnvelope
- Add parentExecutionRequests to BeaconBlockBody

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eturn

New function implements deferred parent execution payload processing
(consensus-specs#5094). Moves execution requests, builder payment, and
availability updates from envelope processing to block processing.

Removes the isParentBlockFull early return in processWithdrawals since
processParentExecutionPayload now runs before withdrawals.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lock

New ordering for Gloas blocks:
1. processParentExecutionPayload (NEW - before header)
2. processBlockHeader
3. processWithdrawals (no longer has empty-parent early return)
4. processExecutionPayloadBid
5. ... rest unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Envelope verification no longer mutates state. All state effects (execution
requests, builder payment, availability, latestBlockHash) have moved to
processParentExecutionPayload in the next block.

Changes:
- Remove state cloning, execution request processing, builder payment,
  availability/latestBlockHash updates, and state root verification
- Add executionRequestsRoot check against bid commitment
- Return void instead of post-state
- Remove verifyStateRoot and dontTransferCache options
- Rename stateView method to verifyExecutionPayloadEnvelope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…onPayload

With deferred payload processing, the envelope no longer produces a
separate post-state. The FULL variant node shares the same stateRoot
as the PENDING node.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- importExecutionPayload now does pure verification only — no state
  cloning, no post-payload state computation, no state root check,
  no regen.processState(), no checkpoint caching
- Remove stateRoot from executionPayload and executionPayloadGossip events
- Remove stateRoot from envelope construction in validator API
- Remove stateRoot from fork choice onExecutionPayload call
- Envelope verification runs via verifyExecutionPayloadEnvelope (void)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rocessing

Block production:
- Add executionRequestsRoot to ExecutionPayloadBid construction
- Add parentExecutionRequests to BeaconBlockBody via getParentExecutionRequests()
- Add getParentExecutionRequests() to BeaconChain (returns parent's
  execution requests from cached envelope, or empty if EMPTY parent)

Gossip validation:
- Add executionRequestsRoot check in envelope validation
- Add EXECUTION_REQUESTS_ROOT_MISMATCH error code

Cleanup:
- Remove stateRoot from validator envelope logging
- Fix spec test for void-returning processExecutionPayloadEnvelope
- Update stale TODO comment in writePayloadEnvelopeInputToDb

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use electra.ExecutionRequests type instead of unknown[]
- Fix parentBlockRootHex -> parentBlock.blockRoot variable name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ensi321 and others added 17 commits April 19, 2026 18:34
Range sync now fetches and validates execution payload envelopes alongside
blocks for Gloas forks. With deferred processing, blocks can sync
optimistically without envelopes, but envelopes are fetched in parallel
for fork-choice FULL/EMPTY variant accuracy.

Changes:
- Batch carries payloadEnvelopes across all state transitions
- downloadByRange fetches envelopes via sendExecutionPayloadEnvelopesByRange
- validateEnvelopesByRangeResponse verifies beaconBlockRoot matches blocks
- processChainSegment accepts payloadEnvelopes parameter
- SyncChainFns types updated for envelope data flow
- Add INVALID_ENVELOPE_BEACON_BLOCK_ROOT error code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a gossip block claims its parent was FULL but the parent's envelope
hasn't been seen, the block is queued and the parent envelope is fetched
via sendExecutionPayloadEnvelopesByRoot.

Changes:
- Add PARENT_PAYLOAD_UNKNOWN block error code and gossip validation
- Add PendingPayloadEnvelope type for tracking missing envelopes
- BlockInputSync: pendingPayloads map, triggerPayloadSearch,
  fetchPayloadEnvelope, onUnknownPayloadEnvelope handler
- Subscribe to unknownEnvelopeBlockRoot and executionPayloadAvailable events
- Add metrics for payload fetch tracking
- Handle PARENT_PAYLOAD_UNKNOWN in processBlock error recovery

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Handle both Fulu and Gloas column shapes in validateColumnsByRangeResponse.
Fulu columns use signedBlockHeader.message.slot, Gloas columns use slot
directly. Dispatch to validateGloasBlockDataColumnSidecars vs
validateFuluBlockDataColumnSidecars based on fork.

Gloas columns in cacheByRangeResponses are skipped (routed to
PayloadEnvelopeInput by the caller, not to IBlockInput).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Only check payload-seen when block is known; unknown blocks are
  handled later by verifyHeadBlockAndTargetRoot which enables the
  API retry path for unknown roots
- Use chain.seenPayloadEnvelope which checks both the gossip cache
  (seenPayloadEnvelopeInputCache) and fork choice FULL variant

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move slot from ExecutionPayloadEnvelope to ExecutionPayload as
slotNumber (uint64) and add slotNumber to PayloadAttributes per
consensus-specs#4840. Updates all verification, gossip validation,
signing, serialization, and SSZ byte extraction accordingly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements deferred payload processing and data availability handling for the Gloas fork. Key changes include refactoring the block and envelope import paths to be independent, updating range sync to manage payload envelopes alongside blocks, and adjusting caching strategies to ensure consistency across the execution hash chain. Feedback focuses on an inconsistency in warning reporting for orphaned envelopes at the start of a chain segment and potential queue saturation risks in the payload envelope processor due to increased residency time for incomplete envelopes.

Comment on lines +77 to +82
if (lastFullSlot !== null && payloadEnvelopes !== null) {
const orphanedInput = payloadEnvelopes.get(lastFullSlot);
if (orphanedInput != null) {
warnings.push({slot: lastFullSlot, payloadEnvelopeInput: orphanedInput});
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The warning for an orphaned payload envelope is only pushed if lastFullSlot is not null. However, if the very first block in the segment (i=0) has a bid.parentBlockHash mismatch with the parentBlock.executionPayloadBlockHash, lastFullSlot will be null and no warning will be recorded, even though the recovery logic currentExecHash = prevExecHash still executes. While this might be rare if sanity checks pass, it's inconsistent with how subsequent mismatches are handled.

Comment thread packages/beacon-node/src/chain/blocks/payloadEnvelopeProcessor.ts
@ensi321 ensi321 force-pushed the nc/alpha.5-vibe branch 2 times, most recently from 3b7e592 to 89e60ae Compare April 22, 2026 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants