Abstract
This EIP introduces execution chain proofs, which make execution-layer chain sync constant time. A node establishes valid execution of all payloads in the chain from the head beacon block to the proof's origin beacon block, and their binding to the beacon chain, by verifying a single execution chain proof. Execution chain proofs are recursive execution proofs: each proof verifies the proof of the previous full execution payload on its branch and then proves one more payload. The origin is the block at which the prover started the recursion. Execution chain proofs build on the execution proofs of EIP-8025.
Motivation
A node syncing from a weak subjectivity checkpoint establishes valid execution from the checkpoint to the head with one proof whose origin is at or before the checkpoint. It downloads and verifies that single chain proof instead of every full payload for re-execution, so its bandwidth and computation are constant and execution-layer chain sync takes constant time (see Weak subjectivity sync). Because each proof supersedes the ones before it on its branch, nodes need not store proof history, and provers do constant work per payload. Execution chain proofs also advance the roadmap for stateless clients, validity-only partially stateless (VOPS) nodes and light clients.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Specification references
The normative specification is maintained in the consensus-specs repository. This section summarises it.
Terminology
- Full and empty. A beacon block is full when the next block on its branch applies its execution payload, and empty otherwise, as in EIP-7732.
- Step. One run of the recursive guest program. A step extends the parent proof's chain by one full payload, or starts a new chain with it.
- Head. The beacon block whose payload a proof's last step proved.
- Origin. The beacon block whose payload the first step of a recursion proved. The prover chooses it.
Public input
class PublicInput(ProgressiveContainer):
ACTIVE_FIELDS = active_fields(width=4)
origin_block_root: Root
head_block_root: Root
chain_id: Uint64
schema_id: Uint16head_block_rootis the head, the binding commitment of the proof.origin_block_rootis the origin, carried unchanged through every step.chain_idis the chain ID, andschema_idis the stateless input schema version.
Gossiped proof
The chain ID and schema are constants of the specification, so a proof carries only the two roots:
class ExecutionProof(Container):
proof_data: ProofData
proof_type: ProofType
origin_block_root: Root
head_block_root: Root
class SignedExecutionProof(Container):
message: ExecutionProof
validator_index: ValidatorIndex
signature: BLSSignatureThe proof engine verifies a proof against the public input built from its two
roots, DEPOSIT_CHAIN_ID and STATELESS_INPUT_SCHEMA_ID. Signed proofs are
gossiped on the execution_proof topic. The execution payload is not required
for proof verification, and a valid proof does not imply that the head's
payload is available or was applied. MAX_SIGNED_EXECUTION_PROOF_SIZE is 4194481.
Recursive guest program
The guest's private input is:
previous_proof: the parent proof, or none to start a new chain;previous_stateandstate: the beacon state witness, the fields of the beacon states after the parent proof's head block and after the target block that the step reads, each authenticated against its block root;signed_envelope: the target block's signed execution payload envelope; andexecution_witness: the execution witness of the target payload.
Both beacon states are taken immediately after their blocks, not advanced to a later slot. A step runs:
def verify_execution_transition(proof_engine, execution_engine, private_input) -> PublicInput:
state = private_input.state
envelope = private_input.signed_envelope.message
previous_proof = private_input.previous_proof
if previous_proof is None:
assert private_input.previous_state is None
origin_block_root = envelope.beacon_block_root
else:
# Verify the parent proof as consensus clients verify proofs
assert proof_engine.verify_execution_proof(previous_proof)
origin_block_root = previous_proof.origin_block_root
# Authenticate the parent head's beacon state against its block root
previous_state = private_input.previous_state
previous_header = previous_state.latest_block_header.copy()
previous_header.state_root = hash_tree_root(previous_state)
assert hash_tree_root(previous_header) == previous_proof.head_block_root
# Bind consecutive heads
assert state.latest_block_hash == previous_state.latest_execution_payload_bid.block_hash
# Per-payload limits that Gloas enforces only in envelope gossip. The gossip
# helper raises GossipReject, which fails the proof like an assertion.
verify_execution_requests_limits(envelope.execution_requests)
assert len(envelope.payload.withdrawals) <= MAX_WITHDRAWALS_PER_PAYLOAD
# Bind the payload to the target block and validate it statelessly
verify_execution_payload_envelope(state, private_input.signed_envelope, execution_engine)
return PublicInput(
origin_block_root=origin_block_root,
head_block_root=envelope.beacon_block_root,
chain_id=DEPOSIT_CHAIN_ID,
schema_id=STATELESS_INPUT_SCHEMA_ID,
)Any failed assertion or raised exception fails the proof.
verify_execution_payload_envelope is the EIP-7732 function that consensus
clients run on a received payload envelope. Given the target beacon state, it
authenticates that state against the envelope's block root and checks:
- the builder's envelope signature;
- the envelope against the block's committed bid;
- the payload's slot, timestamp, parent hash and withdrawals against the state; and
- the payload itself, through
verify_and_notify_new_payload, the call behindengine_newPayload.
The guest thus proves both sides of the Engine API: the consensus client's binding of the payload to its block, and the execution engine's validation of it. This lets the public input commit to the execution chain through beacon block roots.
Implementations SHOULD supply the beacon state witness as partial SSZ trees whose roots are the full state roots and in which only the fields the step reads are expanded. Reading a field that is not expanded MUST fail the proof.
Rationale
Committing through beacon blocks
The public input names beacon block roots rather than execution block hashes. A node checks a proof against beacon blocks it already holds, and the proof's statement is about the chain ending at its head block, which is what a node following that chain needs.
Reusing the envelope handler
The guest calls the function consensus clients run on payload envelopes rather than restating its checks. When a later fork changes that function, the guest follows, and the binding between a block and its payload cannot drift between the guest and consensus clients. The request and withdrawal count limits are the exception: EIP-7732 enforces them only in envelope gossip, so the guest checks them explicitly.
Prover-chosen origin
The prover may start a recursion at any full block. A proof that starts at its own head proves one payload, and recursion extends it from there. No origin is configured into clients, so proving can begin at any time and restart after an interruption. A consumer reads the origin from the proof to see how far back a proof reaches.
Weak subjectivity sync
A node syncing from a weak subjectivity checkpoint must establish valid execution of the full payloads between the checkpoint and the head. With execution costs and sizes , and a proof of size and verification cost , an execution chain proof whose origin is at or before the checkpoint reduces bandwidth and computation from
to
Both are constant in , as is the cost of state sync to the head, so execution-layer chain sync takes constant time. Data availability is established separately (see Security Considerations).
As an estimate: under current mainnet churn parameters the weak subjectivity bound is about 3,532 epochs, roughly 15.7 days (EIP-8383). Etherscan's 30-day averages to 8 October 2026 are about 7,170 blocks per day, 183 KB and 30.3 million gas per block. Syncing across one bound therefore means re-executing payloads, about 21 GB and gas, whereas a proof within the Ethereum Foundation's 300 KiB real-time proving target is about times smaller and is verified once.
One payload per step
Each step proves exactly one full payload, so the step's work is bounded by one payload and the binding is a single equality. A step spans any number of empty blocks without extra witnesses.
Backwards Compatibility
Execution chain proofs use a different public input and gossip message from
EIP-8025, so nodes implementing each cannot share the execution_proof topic.
Test Cases
Consensus-layer tests for the guest program and proof handling are in the consensus specifications. They cover base and recursive steps, steps across empty blocks and missed slots, skipped and unapplied payloads, a parent extended to its own head, mismatched parent states, invalid parent proofs and payloads, signed proofs and proofs keyed by their head, envelopes inconsistent with the state, and the request and withdrawal limits.
Reference Implementation
The executable consensus specification is the reference implementation of the guest step.
Security Considerations
Separation of consensus and execution. An execution chain proof covers only the execution state transition: each payload's execution and its binding to the beacon block that committed to it. The verifying node executes the beacon state transition natively, including for the blocks between consecutive proof heads. Verifying a proof does not change fork choice or payload status.
Reorgs. A recursion follows one branch. When the chain reorganises, proofs whose heads lie only on the abandoned branch cannot be extended onto the new branch, because the new branch's blocks record a different last applied payload. Recursion on the new branch continues from the last proof whose head payload both branches applied, or restarts with a new origin. Until then, nodes on the new branch have no execution chain proof for its head.
Origin depth. A proof says nothing about payloads before its origin. Nodes
MUST check that a proof's origin is at or before the point from which they need
execution validity, such as the checkpoint block they synced from. Nodes SHOULD
place an older origin using the checkpoint state's block_roots or
historical_summaries; how they obtain the required data is out of scope.
Program and fork upgrades. A guest program MUST accept parent proofs only of its own proof type and, to continue recursion across an upgrade, of its predecessor proof types.
Chain binding. The guest MUST NOT take the chain ID or fork schedule from its private input.
Fork activation. The guest's execution engine MUST reject payloads outside the fork it implements; otherwise a payload could be proven under the wrong fork rules.
Data availability. The guest does not prove blob data availability. Nodes MUST continue to establish availability by the means EIP-7732 and its successors define.
Copyright
Copyright and related rights waived via CC0.
