Conversation
…nsition) Moves execution request processing from process_execution_payload into process_parent_execution_payload, which runs first in process_block of the next beacon block. This eliminates payload_states entirely — only block_states exist. process_execution_payload becomes pure verification. Key changes: - BeaconBlockBody: add parent_execution_requests field - ExecutionPayloadEnvelope: remove state_root field - process_block: add process_parent_execution_payload as first step - process_execution_payload: pure verification, returns ExecutionRequests - Store: replace payload_states with verified_execution_requests - on_block: verify parent_execution_requests, always use block_states - on_execution_payload: verify and buffer, no state persisted Reference: https://gist.github.com/lodekeeper/6abbbc1f04bb1985f2f7bf165bfa0fa8 🤖 Generated with AI assistance
- Use local latest_block_header.parent_root in process_execution_payload for consistency with pure-verification refactor - Add note about deposit slot semantics in process_parent_execution_payload (state.slot is child's slot, not parent's) - Add pruning note for verified_execution_requests in Store 🤖 Generated with AI assistance
- Add note explaining why is_parent_block_full cannot be reused in process_parent_execution_payload (latest_block_hash not yet updated) - Use local requests variable in return statement for consistency 🤖 Generated with AI assistance
specs/gloas/beacon-chain.md
Outdated
| # [Modified in Gloas] | ||
| # Removed `state_root` -- redundant with `beacon_block_root` | ||
| # since `process_execution_payload` no longer mutates state |
There was a problem hiding this comment.
this container was added in gloas and didn't exist before, so [Modified in Gloas] in gloas doesn't make any sense, same for the comment you added there, this is fine to note in PR description (like rational why it was removed) but should not be part of the spec
There was a problem hiding this comment.
Done — removed the comment entirely. The state_root removal rationale is already in the PR description.
specs/gloas/beacon-chain.md
Outdated
| signed_execution_payload_bid: SignedExecutionPayloadBid | ||
| # [New in Gloas:EIP7732] | ||
| payload_attestations: List[PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS] | ||
| # [New in Gloas] |
There was a problem hiding this comment.
| # [New in Gloas] | |
| [New in Gloas:EIP7732] |
please make sure to use the right comment
There was a problem hiding this comment.
Fixed — using [New in Gloas:EIP7732] now.
specs/gloas/beacon-chain.md
Outdated
|
|
||
| ```python | ||
| def process_block(state: BeaconState, block: BeaconBlock) -> None: | ||
| # [New in Gloas] |
There was a problem hiding this comment.
| # [New in Gloas] | |
| # [New in Gloas:EIP7732] |
specs/gloas/beacon-chain.md
Outdated
| *Note*: `process_execution_payload` is now a pure verification step in state | ||
| transition. It is called when importing a signed execution payload proposed by |
There was a problem hiding this comment.
this seems inaccurate since is not longer a "state transition" please reconsider how to update the spec in that regard
There was a problem hiding this comment.
Reworded — now describes it as a "pure verification function called by fork-choice" instead of referencing "state transition".
specs/gloas/beacon-chain.md
Outdated
| # [Modified in Gloas] | ||
| # Execution requests are verified by the execution engine and returned | ||
| # for fork-choice level buffering. State mutations are deferred to | ||
| # `process_parent_execution_payload` in the next block. |
There was a problem hiding this comment.
this isn't modified in gloas, the whole function here only exists in gloas, same as I noted in another comment
There was a problem hiding this comment.
Removed — the whole function is new in Gloas, no [Modified] annotation needed.
- Remove `[Modified in Gloas]` comment from ExecutionPayloadEnvelope (container is new in Gloas, not modified) - Use `[New in Gloas:EIP7732]` consistently for parent_execution_requests and process_parent_execution_payload - Reword process_execution_payload note to avoid "state transition" (it's a pure verification function called by fork-choice) - Remove redundant `[Modified in Gloas]` annotation at return site 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove inline [New/Modified in Gloas:EIP7732] annotations from process_execution_payload (new function — section header suffices) - Remove inline [Modified in Gloas] annotations from on_execution_payload (new function — section header suffices) - Add :EIP7732 suffix to all our Gloas annotations for consistency - Clean up Removed comment to match spec convention (no rationale suffix) - Remove explanatory inline comments that belong in PR description 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Round 2 review — annotation cleanup + adversarial analysisAnnotation fixes (edae793)
|
| # | Criterion | Verdict |
|---|---|---|
| 1 | BeaconBlockBody.parent_execution_requests |
✅ |
| 2 | process_parent_execution_payload first in process_block |
✅ |
| 3 | process_execution_payload pure verification |
✅ |
| 4 | state_root removed from envelope |
✅ |
| 5 | payload_states → verified_execution_requests |
✅ |
| 6 | on_block validates requests |
✅ |
| 7 | on_execution_payload buffers requests |
✅ |
| 8 | Validator guide | ✅ |
| 9 | Builder guide | ✅ |
| 10 | Infallibility | ✅ |
| 11 | is_parent_block_full ordering |
✅ |
| 12 | Builder payment epoch rotation | ✅ |
| 13 | Fork boundary handling | ❌ |
| 14 | Gossip validation | ✅ |
Critical finding: Fork boundary (first Gloas block)
upgrade_to_gloas initializes latest_execution_payload_bid.block_hash = pre.latest_execution_payload_header.block_hash (non-zero). The first Gloas block's bid must have bid.parent_block_hash == state.latest_block_hash (same value). So is_parent_full = True in process_parent_execution_payload — it enters the FULL branch for a pre-Gloas parent.
At fork-choice level, get_parent_payload_status accesses parent.body.signed_execution_payload_bid on a Fulu block that lacks this field.
Fix needed: Explicit epoch guard (Option A from gist) in both process_parent_execution_payload and on_block.
Other findings from adversarial analysis
MAJOR:
verified_execution_requestspruning comment removed — should document it follows same pattern asblocks/block_statesbuilder_pending_withdrawals.appendhas no overflow guard (list limit2^20)- p2p gossip: no content-level validation of
parent_execution_requests(only gated on parent payload being "seen", not "verified")
MINOR:
- Race between gossip "seen" and fork-choice "verified" — clarify in p2p spec
- Missing defensive assertion
parent_epoch <= get_current_epoch(state)in builder payment code
The first Gloas block has a pre-Gloas (Fulu) parent whose BeaconBlockBody lacks signed_execution_payload_bid. Guard both process_parent_execution_payload and on_block to skip parent payload processing when the parent is pre-Gloas, asserting parent_execution_requests is empty. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_parent_payload_status accesses parent.body.signed_execution_payload_bid which doesn't exist on pre-Gloas blocks. Guard the function itself (not just on_block) since it's called from get_ancestor, is_parent_strong, get_node_children, and should_extend_payload. Pre-Gloas parents always had their payload embedded in the block, so return PAYLOAD_STATUS_FULL. Also add fork boundary case to validator.md for proposer guidance. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gloas replaces payload_states with verified_execution_requests in Store, but Heze fork-choice still referenced the old field. Propagate the rename and semantic change (full BeaconState → lightweight ExecutionRequests) across all four Heze fork-choice references. 🤖 Generated with AI assistance
Replace get_parent_payload_status block-hash comparison with store.verified_execution_requests membership check. This eliminates all GLOAS_FORK_EPOCH guards while correctly handling the fork boundary via get_forkchoice_store anchor initialization. The map is only populated by on_execution_payload which does full cryptographic verification (EE check, hash binding, signature), so map presence is equivalent to the previous hash comparison. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with AI assistance
specs/gloas/fork-choice.md
Outdated
| parent = store.blocks[block.parent_root] | ||
| parent_block_hash = block.body.signed_execution_payload_bid.message.parent_block_hash | ||
| message_block_hash = parent.body.signed_execution_payload_bid.message.block_hash | ||
| return PAYLOAD_STATUS_FULL if parent_block_hash == message_block_hash else PAYLOAD_STATUS_EMPTY | ||
| return ( | ||
| PAYLOAD_STATUS_FULL | ||
| if block.parent_root in store.verified_execution_requests | ||
| else PAYLOAD_STATUS_EMPTY | ||
| ) |
There was a problem hiding this comment.
what's the reason this function is modified?
There was a problem hiding this comment.
payload_states was eliminated — all CL state mutations moved to process_parent_execution_payload in the next block's state transition. This function now checks verified_execution_payloads (set of roots for which we received and verified execution payloads) instead of looking up a payload state. Same semantic: "was the parent's payload received and valid?"
Latest commit (5a95ce8) renamed this from verified_execution_requests to verified_execution_payloads and simplified to Set[Root] since execution requests verification moved to the state transition via execution_requests_root on the bid.
specs/gloas/fork-choice.md
Outdated
| # Removed `payload_states` | ||
| # [New in Gloas:EIP7732] | ||
| payload_states: Dict[Root, BeaconState] = field(default_factory=dict) | ||
| verified_execution_requests: Dict[Root, ExecutionRequests] = field(default_factory=dict) |
There was a problem hiding this comment.
this can be changed to payloads: Dict[Root, ExecutionPayload] = field(default_factory=dict)
There was a problem hiding this comment.
One concern: get_forkchoice_store needs the anchor root in this structure for checkpoint sync bootstrapping — without it, get_parent_payload_status returns EMPTY for the first block after anchor, causing valid blocks with FULL parents to fail fork-choice validation (on_block asserts empty parent_execution_requests).
With Set[Root], anchor init is just {anchor_root}. With Dict[Root, ExecutionPayload], we'd need the actual payload at anchor time. In checkpoint sync only the beacon state + block are fetched — the execution payload envelope isn't available. Would require changing get_forkchoice_store signature or fetching additional data.
Is there a downstream use case you have in mind that needs the actual payload stored? Or should we find a way to handle the anchor case (e.g. sentinel value, separate anchor set)?
| assert ( | ||
| block.body.parent_execution_requests | ||
| == store.verified_execution_requests[block.parent_root] | ||
| ) |
There was a problem hiding this comment.
this check can be moved to state transition if we add a commitment to the bid
There was a problem hiding this comment.
Done in latest commit (5a95ce8). Added execution_requests_root: Root to ExecutionPayloadBid — verification now happens in process_parent_execution_payload during state transition:
assert hash_tree_root(block.body.parent_execution_requests) == parent_bid.execution_requests_rootFork-choice on_block no longer checks execution requests content. on_execution_payload does a consistency check against the bid commitment. verified_execution_requests: Dict[Root, ExecutionRequests] simplified to verified_execution_payloads: Set[Root].
Add execution_requests_root commitment to ExecutionPayloadBid, enabling CL-verifiable verification of parent_execution_requests without needing the actual execution payload. This eliminates verified_execution_requests from the fork-choice Store (replaced with lightweight verified_execution_payloads Set) and resolves the checkpoint sync bootstrapping issue. Verification points: - state_transition: process_parent_execution_payload checks hash_tree_root(parent_execution_requests) == parent_bid.execution_requests_root - fork-choice: on_execution_payload checks EL-returned requests match the bid commitment 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move field to be grouped with other new Gloas fields in Store. Drop `verified_` prefix for consistency with existing Store fields (blocks, block_states, etc.) which are all implicitly validated. 🤖 Generated with AI assistance Co-Authored-By: Lodekeeper <lodekeeper@users.noreply.github.com>
…status Revert commits acfc095..a488227. get_parent_payload_status does not need to change — the block hash comparison correctly determines payload status. The execution_payloads Set[Root] and execution_requests_root bid commitment were unnecessary. The verified_execution_requests Dict remains for tracking EE-verified execution requests (needed for is_payload_timely, is_payload_data_available, etc.). 🤖 Generated with AI assistance Co-Authored-By: Lodekeeper <lodekeeper@users.noreply.github.com>
…payloads Set Restore execution_requests_root in ExecutionPayloadBid (Gloas + Heze) and add bid commitment verification in both process_parent_execution_payload (state transition) and on_execution_payload (fork-choice). Replace verified_execution_requests Dict[Root, ExecutionRequests] and payload_states Dict[Root, BeaconState] with a lightweight execution_payloads Set[Root] — only tracks which blocks have verified payloads, no need to store data. Fix process_execution_payload: - Return None instead of ExecutionRequests (read from envelope directly) - Use simple state mutation instead of local block header copy - Remove state_root verification (no post-state) Update Heze fork-choice.md consistently (was previously broken — still used payload_states and ignored process_execution_payload changes). 🤖 Generated with AI assistance Co-Authored-By: Lodekeeper <lodekeeper@users.noreply.github.com>
…ript in on_block Two lint fixes: 1. pysetup/helpers.py: add lowercase 'set' to ignored_dependencies so that 'default_factory=set' in Store dataclass fields doesn't cause the spec generator to fail with "'set' is not in list". 2. specs/gloas/fork-choice.md: execution_payloads is Set[Root] (not subscriptable). Replace store.execution_payloads[block.parent_root] in on_block with hash_tree_root(block.body.parent_execution_requests) == parent_bid.execution_requests_root (bid commitment check). 🤖 Generated with AI assistance
…ecution_payload Default-initialized latest_execution_payload_bid has block_hash == Hash32(), so is_parent_full naturally evaluates to False for the first Gloas block, falling into the else branch which asserts empty parent_execution_requests. 🤖 Generated with AI assistance Co-Authored-By: Lodekeeper <lodekeeper@users.noreply.github.com>
…ition Rename to `payloads` for consistency with `blocks` naming pattern. Move before payload_timeliness_vote to match original payload_states position. 🤖 Generated with AI assistance Co-Authored-By: Lodekeeper <lodekeeper@users.noreply.github.com>
🤖 Generated with AI assistance
specs/gloas/beacon-chain.md
Outdated
| `process_execution_payload(state, signed_envelope, execution_engine)`, which | ||
| returns the verified `ExecutionRequests` without mutating `state`. Execution | ||
| requests are deferred to the next beacon block via | ||
| `process_parent_execution_payload`. State transitions that trigger an unhandled |
There was a problem hiding this comment.
State transitions
process_execution_payload is no longer a state transition so this is not accurate.
There was a problem hiding this comment.
Fixed in 03cc5af — rewrote the prose to say "validity ... is checked by" instead of treating it as a state transition. Also changed "State transitions that trigger" → "Verification failures that trigger" in the following sentence.
There was a problem hiding this comment.
Updated. I removed the State transitions framing around process_execution_payload() and rewrote the section to describe it as a pure verification step returning ExecutionRequests, since the CL state mutations are now deferred into process_parent_execution_payload().
specs/gloas/beacon-chain.md
Outdated
| payload envelope `signed_envelope` is verified by | ||
| `process_execution_payload(state, signed_envelope, execution_engine)`, which | ||
| returns the verified `ExecutionRequests` without mutating `state`. Execution | ||
| requests are deferred to the next beacon block via |
There was a problem hiding this comment.
execution requests are deferred to the next beacon block via
I think this is too specific. Also there are other operations that are deferred too like queueing the builder payment and set latest block hash
There was a problem hiding this comment.
Good catch. Fixed in 03cc5af — the prose now lists all four deferred effects: "execution requests, builder payment, payload availability, and latest block hash."
There was a problem hiding this comment.
Good catch. I broadened the wording so it no longer singles out execution requests; it now explicitly reflects that multiple effects are deferred (including latest_block_hash, availability tracking, and builder-payment queueing), not just the execution-request application.
| state: BeaconState, | ||
| # [Modified in Gloas:EIP7732] | ||
| # Removed `body` | ||
| # [New in Gloas:EIP7732] |
There was a problem hiding this comment.
Restored in 03cc5af. These were accidentally dropped during the refactor.
There was a problem hiding this comment.
Restored the explanatory comments around process_execution_payload(). The branch now keeps the rationale/comments in place while still making the function verification-only.
specs/gloas/beacon-chain.md
Outdated
There was a problem hiding this comment.
This is a state mutation. Need to move it either very end or very beginning of the state_transition
There was a problem hiding this comment.
Agreed — this state root caching is a real state mutation that contradicts the "does not mutate state" claim. Two options: (a) move the state root caching to the fork-choice caller (on_execution_payload already copies state, so it can do this before calling process_execution_payload), or (b) accept the mutation and document it. Leaning toward (a). Will address in a follow-up commit once we align on the approach.
There was a problem hiding this comment.
Moved the availability-bit update to the very beginning of process_parent_execution_payload(), so it happens as part of the next block's durable state transition rather than mid-way through payload verification.
specs/gloas/builder.md
Outdated
| This function should not trigger an exception. | ||
|
|
||
| 7. Set `envelope.state_root` to `hash_tree_root(state)`. | ||
| This pure verification function should not trigger an exception and does not |
There was a problem hiding this comment.
| This pure verification function should not trigger an exception and does not | |
| This function should not trigger an exception and does not |
There was a problem hiding this comment.
The word "pure" was already removed in a prior commit — current text reads: "This function should not trigger an exception and does not mutate state." Matches your suggestion.
There was a problem hiding this comment.
Applied the wording change. The builder text now says This function should not trigger an exception and does not mutate state.
specs/gloas/fork-choice.md
Outdated
| def get_parent_payload_status(store: Store, block: BeaconBlock) -> PayloadStatus: | ||
| parent = store.blocks[block.parent_root] | ||
| # Pre-Gloas parents always had their payload embedded in the block | ||
| if compute_epoch_at_slot(parent.slot) < GLOAS_FORK_EPOCH: |
There was a problem hiding this comment.
Not a fan of having GLOAS_FORK_EPOCH in the fork choice spec. Find a workaround either mention this as a comment, or put it somewhere in fork.md
There was a problem hiding this comment.
Valid concern. The guard exists because pre-Gloas blocks lack signed_execution_payload_bid — accessing it would crash. The cleanest alternative: seed all pre-Gloas block roots into store.payloads during fork activation in fork.md, so fork-choice can assume all entries have the Gloas fields. That would eliminate both epoch checks (this one and the one in on_block). Not yet addressed — will follow up once we align on approach.
There was a problem hiding this comment.
Addressed on the branch: I removed the raw GLOAS_FORK_EPOCH workaround from fork-choice and switched the pre-Gloas parent handling to a structural check instead. I also moved the initialization rationale into specs/gloas/fork.md where it belongs.
specs/gloas/fork-choice.md
Outdated
| assert block.parent_root in store.payload_states | ||
| state = copy(store.payload_states[block.parent_root]) | ||
| # First Gloas block: parent is pre-Gloas, no deferred payload processing | ||
| if compute_epoch_at_slot(parent_block.slot) < GLOAS_FORK_EPOCH: |
There was a problem hiding this comment.
Not a fan of having GLOAS_FORK_EPOCH in the fork choice spec. Find a workaround either mention this as a comment, or put it somewhere in fork.md
There was a problem hiding this comment.
Same as above — will address together with the get_parent_payload_status guard. Both can be removed if we seed pre-Gloas block roots into store.payloads at fork activation.
There was a problem hiding this comment.
Same fix here: the fork-choice workaround no longer relies on GLOAS_FORK_EPOCH; the branch now uses structural parent detection and keeps the initialization note in fork.md instead of encoding the fork epoch into fork choice.
specs/gloas/beacon-chain.md
Outdated
| # Execution request functions use state.slot for PendingDeposit.slot | ||
| # and builder deposit_epoch, shifting them by at least one slot. | ||
| requests = block.body.parent_execution_requests | ||
| for request in requests.deposits: |
There was a problem hiding this comment.
These should just be in the original for op style
def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None:
for operation in operations:
fn(state, operation)
There was a problem hiding this comment.
Already addressed in a prior commit — for_ops helper is now used in process_parent_execution_payload.
There was a problem hiding this comment.
Changed back to the original for_ops(...) style in process_parent_execution_payload() / process_execution_payload() so the loops stay aligned with the surrounding spec style.
specs/gloas/beacon-chain.md
Outdated
| parent_epoch = compute_epoch_at_slot(parent_slot) | ||
| if parent_epoch == get_current_epoch(state): | ||
| payment_index = SLOTS_PER_EPOCH + parent_slot % SLOTS_PER_EPOCH | ||
| else: |
There was a problem hiding this comment.
This is interesting. parent_epoch MUST be current or previous epoch.
If parent epoch is more than two epochs old, I am not sure how we should handle. (Maybe just skip updating state.builder_pending_payments? And then payment_index will be calculated based on clock slot)
There was a problem hiding this comment.
Good observation. In the base branch this is simpler because process_execution_payload runs same-slot (SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH). In the deferred model, parent can be from a different epoch.
Currently we handle current_epoch and previous_epoch but silently skip if parent is >2 epochs old. This means the builder payment is forfeit — which seems correct (the chain missed that many slots, the payment window has passed and the slot has been recycled in builder_pending_payments).
Should we add an explicit comment documenting the forfeit behavior, or handle it differently? This is a protocol-level decision with economic consequences for the builder.
There was a problem hiding this comment.
I made the parent-epoch handling explicit now. The branch only rotates/applies builder pending payments for current/previous epoch contexts, and I also fixed the cross-fork carryover bug by preserving execution_requests_root across Gloas→Heze / genesis paths so the deferred-payment/deferred-request commitments stay coherent on transition boundaries. Targeted fork tests are green after that fix.
There was a problem hiding this comment.
After reviewing the ring buffer mechanics and #epbs channel discussions: if parent is >2 epochs old, the payment entry has already been evicted from builder_pending_payments by process_builder_pending_payments at epoch boundaries. There is nothing left to read — silent skip is the only correct behavior.
This is consistent with the existing quorum-based forfeit mechanism (potuz confirmed in #epbs 2025-07-28: builders not paying when quorum is not met is intentional design).
Added a documenting comment in d84874b.
- Fix prose description: process_execution_payload is verification, not a state transition. Use "Verification failures" instead of "State transitions" - Broaden deferred effects description to list all four: execution requests, builder payment, payload availability, and latest block hash - Restore [Modified in Gloas:EIP7732] and [New in Gloas:EIP7732] annotation comments on process_execution_payload signature - Fix duplicated "verifies the payload against the execution engine" in process_execution_payload docstring 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Return value was unused by all callers — fork-choice reads execution_requests directly from the envelope object. Updated prose description and docstring accordingly. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-choice Remove GLOAS_FORK_EPOCH from fork-choice following spec convention that fork-choice files should not contain epoch constants. Use structural hasattr checks instead, which is a type-level guard rather than an epoch-level one. Add explanatory note above get_parent_payload_status documenting the pre-Gloas parent edge case. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…allers process_execution_payload must not mutate state — that is the core principle of the deferred payload design. The state root caching (filling latest_block_header.state_root) was a leftover from the base branch. Move it to fork-choice on_execution_payload callers in both Gloas and Heze. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the parent block is older than the previous epoch, the payment entry has already been settled or evicted from builder_pending_payments by epoch boundary processing. This is consistent with the existing quorum-based forfeit mechanism in ePBS. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…on_requests_root beacon_block topic: - REJECT if parent was FULL and parent_execution_requests doesn't match parent_bid.execution_requests_root - REJECT if parent was EMPTY and parent_execution_requests is not empty execution_payload topic: - REJECT if envelope.execution_requests doesn't match bid.execution_requests_root 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…p gossip The ExecutionPayloadEnvelope SSZ type uses `beacon_block_root`, not `block_root`. This bug also exists upstream. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
887bea0 to
2ece03d
Compare
Upstream p2p-interface.md issues (alpha.4)Noted during parity check. These exist in the upstream spec, not introduced by this PR. Fixed here
Deferred (out of scope — require upstream rework)
|
The fork-choice spec does not consider pre-fork blocks — this is consistent with all previous forks. The cross-fork parent block access issue (accessing signed_execution_payload_bid on pre-Gloas parents) is a pre-existing bug in the base spec, not something introduced by this PR. Documented separately. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…h_attestations F821 lint error: name referenced but not in function signature. Mirror mark_payload_available=False default from apply_next_epoch_with_attestations. 🤖 Generated with AI assistance
Fix line wrapping in beacon-chain.md, fork.md, p2p-interface.md and reformat long lines/function signature in fork_choice.py to satisfy CI. 🤖 Generated with AI assistance
🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s pattern Avoids adding lowercase "set" to pysetup ignored_dependencies. payloads is already explicitly initialized in get_forkchoice_store. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python dataclass requires fields without defaults before fields with defaults. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep payloads grouped with other payload_* fields. Add lowercase "set" to ignored_dependencies so default_factory=set is recognized. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The function was refactored to pure verification but the return type was left as None and the captured `requests` value was never returned. This caused all process_execution_payload tests to fail with `assert None == ExecutionRequests(...)`. 🤖 Generated with AI assistance
…ents Anchor payload status is unresolved at checkpoint sync time — it gets resolved by the first child block. Instead of optimistically seeding anchor_root in payloads, skip the local verification check for parents at or before the finalized checkpoint epoch. 🤖 Generated with AI assistance Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Closing — Nico is bringing these changes to the spec directly. Thanks for the review! |
Summary
Defer execution payload processing to the next block for Gloas/ePBS (EIP-7732).
process_execution_payloadno longer mutates beacon state. Instead, the CL effects (execution requests, builder payment, availability flag,latest_block_hash) are applied by a newprocess_parent_execution_payloadfunction at the start of the next block. The builder commits toexecution_requests_rootin the bid, and the next proposer carries the parent requests inBeaconBlockBody.parent_execution_requests.What changed
New types and fields
BeaconBlockBody.parent_execution_requests: ExecutionRequests— proposer delivers the parent execution requestsExecutionPayloadBid.execution_requests_root: Root— builder commits to execution requests at bid timeStore.payload_states: Dict[Root, BeaconState]replaced byStore.payloads: Set[Root]— no longer caches intermediate post-payload states, just tracks which roots have been locally verifiedstate_rootremoved fromExecutionPayloadEnvelope— no longer needed sinceprocess_execution_payloaddoes not mutate stateNew functions
process_parent_execution_payloadadded as the first step inprocess_block— processes deferred execution requests (deposits, withdrawals, consolidations), queues builder payment, updates availability flag andlatest_block_hashGossip validation (
p2p-interface.md)parent_execution_requestsdoes not match parent bidexecution_requests_root(FULL parent) or is not empty (EMPTY parent)hash_tree_root(envelope.execution_requests) != bid.execution_requests_rootValidator guidance (
validator.md)parent_execution_requestsfrom the parent envelopeFork boundaries (
fork.md)upgrade_to_gloasandupgrade_to_hezeinitializeexecution_requests_root = hash_tree_root(ExecutionRequests())Tests
test_process_parent_execution_payload.py— full, empty, cross-epoch, invalid requests, builder payment, deposit orderingpayload_statestopayloadsmigrationexecution_requests_rootinitialization