--- eip: 8440 title: Execution Chain Proofs description: Proves valid execution of all payloads from the head beacon block to the proof's origin, and their binding to the beacon chain author: Francesco Risitano (@frisitano) discussions-to: https://ethereum-magicians.org/t/eip-8440-execution-chain-proofs/29930 status: Draft type: Standards Track category: Core created: 2026-10-08 requires: 7732, 8025 --- ## 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](./eip-8025.md). ## 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](#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](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174). ### Specification references The normative specification is maintained in the consensus-specs repository. This section summarises it. - [Consensus Layer](https://github.com/ethereum/consensus-specs/tree/ca292a5c5d42cf8f094aaf60778fc95a4db05d33/specs/_features/eip8440) - [Execution Layer stateless validation interfaces](https://github.com/ethereum/execution-specs/blob/ad678168edc5a2c989a94265ff8f484b12cef4cd/src/ethereum/forks/amsterdam/stateless.py) ### 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](./eip-7732.md). - **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 ```python class PublicInput(ProgressiveContainer): ACTIVE_FIELDS = active_fields(width=4) origin_block_root: Root head_block_root: Root chain_id: Uint64 schema_id: Uint16 ``` - `head_block_root` is the head, the binding commitment of the proof. - `origin_block_root` is the origin, carried unchanged through every step. - `chain_id` is the chain ID, and `schema_id` is 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: ```python 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: BLSSignature ``` The 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_state` and `state`: 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; and - `execution_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: ```python 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 behind `engine_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 $N$ full payloads between the checkpoint and the head. With execution costs $e_i$ and sizes $b_i$, and a proof of size $\pi$ and verification cost $v$, an execution chain proof whose origin is at or before the checkpoint reduces bandwidth and computation from $$ B = \sum_{i=1}^{N} b_i, \qquad C = \sum_{i=1}^{N} e_i $$ to $$ B = \pi, \qquad C = v. $$ Both are constant in $N$, 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](#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](./eip-8383.md)). 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 $N \approx 1.1 \times 10^5$ payloads, about 21 GB and $3.4 \times 10^{12}$ gas, whereas a proof within the Ethereum Foundation's 300 KiB real-time proving target is about $7 \times 10^4$ 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](https://github.com/ethereum/consensus-specs/tree/ca292a5c5d42cf8f094aaf60778fc95a4db05d33/tests/core/pyspec/eth_consensus_specs/test/eip8440). 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](../LICENSE.md).