Skip to content

Make ExecutionEngine highly recommended but optional#3

Closed
barnabasbusa wants to merge 3 commits intofrisitano:feat/eip8025-refactorfrom
barnabasbusa:eip8025-optional-execution-engine
Closed

Make ExecutionEngine highly recommended but optional#3
barnabasbusa wants to merge 3 commits intofrisitano:feat/eip8025-refactorfrom
barnabasbusa:eip8025-optional-execution-engine

Conversation

@barnabasbusa
Copy link
Copy Markdown

@barnabasbusa barnabasbusa commented Apr 17, 2026

Summary

This PR targets ethereum#5055 (feat/eip8025-refactor) and proposes extending the
fire-and-forget pattern you introduced for ProofEngine to the
ExecutionEngine, while adding a validator-level rule that preserves the
penalty signal for validators who opt out of running a local EL.

Proposal

Two changes:

  1. Drop the assert on execution_engine.verify_and_notify_new_payload
    in process_execution_payload, matching the fire-and-forget ProofEngine
    treatment. A node that chooses not to run a local execution engine (e.g.
    a stateless client consuming proofs) should not be forced to by the
    consensus spec.
  2. Add an attestation execution-validity gating rule to validator.md
    (SHOULD-level): a validator SHOULD NOT attest to a block whose
    execution_payload has not been independently validated — either by a
    local EL returning VALID, or by a verified SignedExecutionProof whose
    public_input.new_payload_request_root matches the block's
    NewPayloadRequest, received by the attestation deadline. If neither
    signal is available, the validator SHOULD withhold attestation.

Why both changes are needed

Removing the consensus-level assert alone would be unsafe during the
optional-proof phase:

  • proof_engine.notify_new_payload on L166 is fire-and-forget; it performs
    no verification.
  • process_execution_proof (which does verify proofs) is only invoked via
    execution_proof gossip validation — it is not part of block
    state-transition.
  • Proofs are explicitly optional, arrive asynchronously, and may not be
    available before the 4-second attestation deadline.
  • Without a gating rule, validators with no EL and no proof-in-hand would
    still happily attest to invalid blocks, and there would be no
    missed-attestation penalty to self-correct the behavior.

Change (2) restores the penalty signal. Validators that opt out of the EL
and also fail to receive a valid proof in time simply miss their
attestation and pay the standard inactivity penalty — making the opt-out
economically self-regulating rather than a silent safety risk.

What changed

  • specs/_features/eip8025/beacon-chain.md
    • Removed assert wrapper on
      execution_engine.verify_and_notify_new_payload(...).
    • Updated the *Note* above process_execution_payload to describe
      both engines as fire-and-forget notifications and to be explicit that
      execution-validity verification becomes out-of-band.
    • Updated the inline comment next to the EL call to match.
  • specs/_features/eip8025/validator.md
    • New section Attestation execution-validity gating under Beacon
      chain responsibilities
      encoding the SHOULD-rule described above, plus
      non-normative notes clarifying (a) follower/non-attesting nodes are
      unaffected and (b) validators running an EL see no behavioral change
      in practice.

Why "highly recommended" rather than "removed"

Running an execution engine remains the right default for defense-in-depth:
independent check against proof-engine bugs/malicious provers, needed for
local block building and fast attesting, useful fallback while proof
availability/latency matures. Validators who want to opt out now pay the
cost of either running a proof subscription or missing attestations —
which seems like the right incentive shape for an optional-proof phase.

Open questions for discussion

  • Do we want to go further and rename/repoint the call to a pure
    execution_engine.notify_new_payload (returning None) to match the
    proof engine signature exactly? That is more invasive since
    verify_and_notify_new_payload is defined up the fork hierarchy
    (Bellatrix onwards), so I kept this PR to the minimal semantic change.
  • Should the SHOULD-rule be strengthened to a MUST during the transitional
    phase, or is SHOULD the right level given it's guidance for honest
    validators and we cannot punish deviation on-chain?
  • Any need for a fork-choice-level rule in addition to the attestation
    rule, or is the attestation penalty signal sufficient?

Test plan

  • Rendered markdown reads correctly on GitHub (ToC, section anchors).
  • Spec builder still parses eip8025/beacon-chain.md (no syntactic
    change to the Python block, only assert removal).
  • Review of the new validator.md section for correctness of the
    proof-matching condition (public_input.new_payload_request_root
    against hash_tree_root(new_payload_request)).
  • Discussion with reviewers on SHOULD vs MUST level and on any
    interaction with fork choice.

Mirror the fire-and-forget pattern used for ProofEngine by dropping the
assert on `execution_engine.verify_and_notify_new_payload`. With EIP-8025
execution proofs, consensus correctness is established via the proof
engine path, so gating consensus on the execution engine's response is no
longer strictly required. Running an execution engine remains highly
recommended for defense-in-depth.
…ted by proofs

The previous phrasing "consensus correctness is established via the proof
engine path" was misleading -- `process_execution_proof` runs during gossip
validation, not as part of block state-transition, and proofs are explicitly
optional in this EIP. With the execution-engine assert removed, consensus no
longer verifies execution validity at all; validity becomes an out-of-band
concern handled by a locally-run execution engine and/or by asynchronously
gossiped execution proofs. State this explicitly so the trade-off is visible
to readers.
Introduce a validator SHOULD-rule that gates attestation on having an
independent execution-validity signal: either a local ExecutionEngine VALID
response or a verified SignedExecutionProof for the corresponding
NewPayloadRequest. This restores the missed-attestation penalty signal for
validators that opt out of running a local execution engine, so that
opt-out validators who also fail to verify a proof before the attestation
deadline self-correct via standard inactivity penalties rather than silently
attesting to invalid blocks.

Addresses reviewer concern that merely removing the consensus-level assert
left validators free to attest to any syntactically valid block without
any execution-validity verification.
Copy link
Copy Markdown
Owner

@frisitano frisitano left a comment

Choose a reason for hiding this comment

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

I agree with the sentiment. Uncertain on how best to capture this in the spec. Added some points for discussion inline.

Comment on lines +118 to +120
**highly recommended** for defense-in-depth, but is no longer mandatory. A
node doing neither accepts execution payloads without execution-validity
verification.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Some wording here may need tightening: "accepts execution payloads". The node will optimistically import the execution payloads in an optimistic (NOT_VALIDATED) state. I would argue that this is not equal to accepting the execution payload. This point is a little pedantic, but I think it is important for interpretation.

Comment on lines +148 to +155
# [Modified in EIP8025]
# Notify ExecutionEngine of the new execution payload. Running an execution
# engine is highly recommended for defense-in-depth but is no longer
# mandatory -- consensus is not gated on the execution engine's response.
# Execution validity becomes an out-of-band concern: it is verified by
# the locally-run execution engine (if present) and/or by asynchronously
# gossiped execution proofs processed via `process_execution_proof`.
execution_engine.verify_and_notify_new_payload(
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I'm not sure if we should remove the assertion here or if we should modify the implementation of verify_and_notify_new_payload to note that it should return a NOT_VALIDATED response if the external execution_engine has not been configured.

Maybe it's better to do it as you have here, as it's higher in the call stack and therefore "propagates" the implications to the caller. Just thinking out loud.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

IMO, we should keep the assertion and modify verify_and_notify_new_payload to return True only if either holds:

  • ExecutionEngine returned VALID
  • There is a VALID proof received from p2p

In practice, both verification modes can turn a node to optimistic state:

  • EE returned SYNCING | ACCEPTED — resolved by the signal mechanism of the Optimistic sync (subsequent newPayload or forkchoiceUpdated responding with VALID | INVALID when EE pulls missing blocks and completes verification makes a node non-optimistic again)
  • There is no proof yet received — this is new, but can be handled in a similar way relying on a different signal mechanism. In this case the signal will be upon receiving gossiped proof or pulling it of a remote peer and validating it

Today a validator client can already recognize when a node it connects to is in optimistic state and skip attesting if this is the case.

Comment on lines +35 to +76
### Attestation execution-validity gating

*[New in EIP8025]*

With `process_execution_payload` no longer gating consensus on execution
validity, an honest validator SHOULD NOT attest to a block whose
`execution_payload` has not been independently validated. Specifically, a
validator SHOULD withhold its attestation for a block until at least one of
the following conditions holds by the attestation deadline:

1. A locally-run `ExecutionEngine` has returned `VALID` for the block's
corresponding `NewPayloadRequest` (i.e.
`execution_engine.verify_and_notify_new_payload(new_payload_request)`
returned `True`).
2. A `SignedExecutionProof` has been received on the `execution_proof`
gossip topic for the block, and:
- `proof.public_input.new_payload_request_root` equals
`hash_tree_root(new_payload_request)` for the block's
`NewPayloadRequest`, and
- `proof_engine.verify_execution_proof(proof)` returned `True` (i.e. the
proof passed `process_execution_proof`).

If neither signal is available by the attestation deadline, the validator
SHOULD withhold its attestation for that block. This restores a
defense-in-depth penalty signal against attesting to blocks with invalid
execution payloads after the removal of the consensus-level assert on the
execution engine: validators that opt out of both running a local execution
engine and subscribing to execution proofs will miss attestations for blocks
whose validity they cannot independently establish, and will therefore
incur the standard missed-attestation penalty.

*Note*: This guidance applies only to validators performing attestation
duties. Follower or non-attesting nodes MAY accept blocks through
state-transition without any execution-validity signal; they do not put
network safety at risk by doing so.

*Note*: Validators running a local execution engine are unaffected in
practice — condition (1) will be satisfied via the existing
`verify_and_notify_new_payload` call in `process_execution_payload`. This
rule only changes behavior for validators that have opted out of running an
execution engine.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think this adds clarity, but it may already be implied by the rest of the specs / optimistic block import handling. I see some overlap with this. Essentially, if a block has not been validated by the attestation deadline, then it will remain in the optimistic state and not be attested by the validator.

proof passed `process_execution_proof`).

If neither signal is available by the attestation deadline, the validator
SHOULD withhold its attestation for that block. This restores a
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
SHOULD withhold its attestation for that block. This restores a
MUST NOT attest to that block. This restores a

This must be a MUST, otherwise, it reads as if neither of the listed verifications complete a client MAY attest and remain spec compliant.


*Note*: This guidance applies only to validators performing attestation
duties. Follower or non-attesting nodes MAY accept blocks through
state-transition without any execution-validity signal; they do not put
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Then these nodes exposes their consumers to rely on unverified data. Each node MUST NOT import a block or MUST signal that it is “optimistic” until block execution is verified.

@barnabasbusa
Copy link
Copy Markdown
Author

This PR has been reopened against upstream ethereum/consensus-specs:master now that ethereum#5055 is merged: ethereum#5151. I've linked back to the review comments from @frisitano and @mkalinin in the new PR description so the context carries over — let's continue the discussion there. I'll leave this PR open for a few days as a pointer before closing.

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