Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions src/engine/eip8025.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Engine API -- EIP-8025

Engine API methods for stateless validation using execution proofs.

This specification is based on and extends [Engine API - Osaka](./osaka.md) specification.

## Table of contents

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Structures](#structures)
- [PublicInputV1](#publicinputv1)
- [ExecutionProofV1](#executionproofv1)
- [ProofAttributesV1](#proofAttributesv1)
- [ExecutionPayloadHeaderV1](#executionpayloadheaderv1)
- [NewPayloadRequestHeaderV1](#newpayloadrequestheaderv1)
- [ProofStatusV1](#proofstatusv1)
- [Methods](#methods)
- [engine_verifyExecutionProofV1](#engine_verifyexecutionproofv1)
- [engine_verifyNewPayloadRequestHeaderV1](#engine_verifynewpayloadrequestheaderv1)
- [engine_requestProofsV1](#engine_requestproofsv1)
- [Errors](#errors)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Configuration

*Note*: The configuration values are not definitive.

| Name | Value | Description |
|--------------------------------|-------|-------------|
| `MIN_REQUIRED_EXECUTION_PROOFS` | `1` | Minimum number of valid execution proofs required to satisfy proof requirements |

## Structures

### PublicInputV1

The fields are encoded as follows:

- `newPayloadRequestRoot`: `DATA`, 32 Bytes - The hash tree root of the `NewPayloadRequest`.

### ExecutionProofV1

The fields are encoded as follows:

- `proofData`: `DATA` - Variable length proof data, maximum 300 KiB.
- `proofType`: `QUANTITY`, 64 Bits - Type identifier for the proof system.
- `publicInput`: `Object` - A [`PublicInputV1`](#publicinputv1) object.

### ProofAttributesV1

The fields are encoded as follows:

- `proofTypes`: `Array of QUANTITY` - Array of proof type identifiers to request.

### ExecutionPayloadHeaderV1

This structure maps on the [`ExecutionPayloadHeader`] structure of the beacon chain spec.
The fields are encoded as follows:

- `parentHash`: `DATA`, 32 Bytes
- `feeRecipient`: `DATA`, 20 Bytes
- `stateRoot`: `DATA`, 32 Bytes
- `receiptsRoot`: `DATA`, 32 Bytes
- `logsBloom`: `DATA`, 256 Bytes
- `prevRandao`: `DATA`, 32 Bytes
- `blockNumber`: `QUANTITY`, 64 Bits
- `gasLimit`: `QUANTITY`, 64 Bits
- `gasUsed`: `QUANTITY`, 64 Bits
- `timestamp`: `QUANTITY`, 64 Bits
- `extraData`: `DATA`, 0 to 32 Bytes
- `baseFeePerGas`: `QUANTITY`, 256 Bits
- `blockHash`: `DATA`, 32 Bytes
- `transactionsRoot`: `DATA`, 32 Bytes - The hash tree root of the transactions included in the payload.
- `withdrawalsRoot`: `DATA`, 32 Bytes - The hash tree root of the withdrawals included in the payload.
- `blobGasUsed`: `QUANTITY`, 64 Bits
- `excessBlobGas`: `QUANTITY`, 64 Bits

### NewPayloadRequestHeaderV1

The fields are encoded as follows:

- `executionPayloadHeader`: `Object` - An [`ExecutionPayloadHeaderV1`](#executionpayloadheaderv1) object.
- `versionedHashes`: `Array of DATA`, 32 Bytes each - Array of blob versioned hashes.
- `parentBeaconBlockRoot`: `DATA`, 32 Bytes - Root of the parent beacon block.
- `executionRequests`: `Array of DATA` - Execution layer triggered requests.

### ProofStatusV1

The fields are encoded as follows:

- `status`: `String` - The verification status. One of:
- `"VALID"` - The proof/header verification succeeded.
- `"INVALID"` - The proof/header verification failed.
- `"SYNCING"` - Less than `MIN_REQUIRED_EXECUTION_PROOFS` valid proofs have been verified for the given header.
- `"NOT_SUPPORTED"` - The proof type is not supported by this client.
- `error`: `String` - Optional error message if status is not `"VALID"`.

## Methods

*Note*: The timeouts are not definitive.

### engine_verifyExecutionProofV1

#### Request

* method: `engine_verifyExecutionProofV1`
* params:
1. `executionProof`: [`ExecutionProofV1`](#executionproofv1) - The execution proof to verify.
* timeout: 1s

#### Response

* result: [`ProofStatusV1`](#proofstatusv1) - The verification result.
* error: code and message set in case an exception happens while verifying the proof.

#### Specification

1. Client software **MUST** validate the `executionProof` parameter:
- `proofData` **MUST** be non-empty and not exceed 300 KiB.
- `proofType` **MUST** be a supported proof type.
- `publicInput.newPayloadRequestRoot` **MUST** be a valid 32-byte hash.

2. Client software **MUST** verify the execution proof using the appropriate proof verification algorithm based on `proofType`.

3. Client software **MUST** respond with `{status: "VALID"}` if the proof is valid, `{status: "INVALID", error: "..."}` if the proof is invalid, or `{status: "NOT_SUPPORTED", error: "..."}` if the proof type is not supported.

### engine_verifyNewPayloadRequestHeaderV1

#### Request

* method: `engine_verifyNewPayloadRequestHeaderV1`
* params:
1. `newPayloadRequestHeader`: [`NewPayloadRequestHeaderV1`](#newpayloadrequestheaderv1) - The header to verify.
* timeout: 1s

#### Response

* result: [`ProofStatusV1`](#proofstatusv1) - The verification result.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Maybe a nit, but for this engine_verifyNewPayloadRequestHeaderV1 we should technically name the return type ProofsStatusV1 instead of ProofStatusV1?

Since technically we are checking a proofs policy, no?

Not sure it is worth changing tbh -- just surfacing thought.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think it is fine to use the same return type ProofStatusV1 for both engine_verifyNewPayloadRequestHeaderV1 and engine_verifyExecutionProofV1 and have slightly different interpretations. This is analogous to how PayloadStatusV* is used as the return type for both engine_newPayload* and engine_forkchoiceUpdated*.

* error: code and message set in case an exception happens while verifying.

#### Specification

1. Client software **MUST** validate the `newPayloadRequestHeader` parameter structure.

2. Client software **MUST** respond with `{status: "VALID"}` if at least `MIN_REQUIRED_EXECUTION_PROOFS` proofs have been verified for this `NewPayloadRequestHeader`, or `{status: "SYNCING "}` if not.

### engine_requestProofsV1

#### Request

* method: `engine_requestProofsV1`
* params:
1. `executionPayload`: [`ExecutionPayloadV3`](./cancun.md#executionpayloadv3) - The execution payload to generate proofs for.
2. `versionedHashes`: `Array of DATA`, 32 Bytes each - Array of blob versioned hashes.
3. `parentBeaconBlockRoot`: `DATA`, 32 Bytes - Root of the parent beacon block.
4. `executionRequests`: `Array of DATA` - List of execution layer triggered requests.
5. `proofAttributes`: [`ProofAttributesV1`](#proofAttributesv1) - Attributes specifying which proofs to generate.
* timeout: 1s
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this TBD?

Copy link
Copy Markdown
Author

@frisitano frisitano Jan 23, 2026

Choose a reason for hiding this comment

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

Good catch, I added a note - *Note*: The timeouts are not definitive.


#### Response

* result: `DATA`, 8 Bytes - A `ProofGenId` to track the proof generation request.
* error: code and message set in case an exception happens while initiating proof generation.

#### Specification

1. Client software **MUST** validate all parameters.

2. Client software **MUST** initiate asynchronous proof generation for the given payload and return a `ProofGenId` immediately.

3. Generated proofs are delivered asynchronously via the beacon API endpoint `POST /eth/v1/prover/execution_proofs`. The client **MUST NOT** block waiting for proof generation to complete.

4. If proof generation is unavailable (e.g., no prover configured), client software **MUST** return error code `-39004`.

## Errors

| Code | Message | Meaning |
|------|---------|---------|
| -39001 | Invalid proof format | The execution proof structure is malformed |
| -39002 | Invalid header format | The new payload request header structure is malformed |
| -39003 | Invalid payload | The execution payload is invalid |
| -39004 | Proof generation unavailable | The client cannot generate proofs |
145 changes: 145 additions & 0 deletions src/engine/openrpc/methods/proof.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
- name: engine_verifyExecutionProofV1
summary: Verifies an execution proof
externalDocs:
description: Method specification
url: https://github.com/ethereum/execution-apis/blob/main/src/engine/eip8025.md#engine_verifyexecutionproofv1
params:
- name: Execution proof
required: true
schema:
$ref: '#/components/schemas/ExecutionProofV1'
result:
name: Proof status
schema:
$ref: '#/components/schemas/ProofStatusV1'
errors:
- code: -39001
message: Invalid proof format
examples:
- name: engine_verifyExecutionProofV1 example
params:
- name: Execution proof
value:
proofData: '0x0000000000000000000000000000000000000000000000000000000000000000'
proofType: '0x1'
publicInput:
newPayloadRequestRoot: '0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858'
result:
name: Proof status
value:
valid: true

- name: engine_verifyNewPayloadRequestHeaderV1
summary: Verifies a new payload request header has sufficient proof coverage
externalDocs:
description: Method specification
url: https://github.com/ethereum/execution-apis/blob/main/src/engine/eip8025.md#engine_verifynewpayloadrequestheaderv1
params:
- name: New payload request header
required: true
schema:
$ref: '#/components/schemas/NewPayloadRequestHeaderV1'
result:
name: Verification status
schema:
$ref: '#/components/schemas/ProofStatusV1'
errors:
- code: -39002
message: Invalid header format
examples:
- name: engine_verifyNewPayloadRequestHeaderV1 example
params:
- name: New payload request header
value:
executionPayloadHeader:
parentHash: '0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a'
feeRecipient: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b'
stateRoot: '0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45'
receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
prevRandao: '0x0000000000000000000000000000000000000000000000000000000000000000'
blockNumber: '0x1'
gasLimit: '0x1c9c380'
gasUsed: '0x0'
timestamp: '0x5'
extraData: '0x'
baseFeePerGas: '0x7'
blockHash: '0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858'
transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
withdrawalsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
blobGasUsed: '0x0'
excessBlobGas: '0x0'
versionedHashes: []
parentBeaconBlockRoot: '0x0000000000000000000000000000000000000000000000000000000000000000'
executionRequests: []
result:
name: Verification status
value:
valid: true

- name: engine_requestProofsV1
summary: Requests proof generation for a new payload request
Comment on lines +80 to +81
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checking, do we still need this if the proofs are pushed to the CL versus the CL requesting proofs from the proof engine?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think it makes sense to keep it. The CL should instruct the proof engine what blocks to generate proofs for. It requires consensus layer context to determine when a proof is required by that nodes builder. I think the flow is pretty clean if you look at the first diagram in this PR (prover replaced by builder in the future) ethereum/consensus-specs#4828. What do you think?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ah I see, I think I merged some of the prover related responsibilities into zkboost -- but perhaps it doesn't matter

externalDocs:
description: Method specification
url: https://github.com/ethereum/execution-apis/blob/main/src/engine/eip8025.md#engine_requestproofsv1
params:
- name: Execution payload
required: true
schema:
$ref: '#/components/schemas/ExecutionPayloadV3'
- name: Versioned hashes
required: true
schema:
type: array
items:
$ref: '#/components/schemas/hash32'
- name: Parent beacon block root
required: true
schema:
$ref: '#/components/schemas/hash32'
- name: Proof attributes
required: true
schema:
$ref: '#/components/schemas/ProofAttributesV1'
result:
name: Proof generation ID
schema:
$ref: '#/components/schemas/bytes8'
errors:
- code: -39003
message: Invalid payload
- code: -39004
message: Proof generation unavailable
examples:
- name: engine_requestProofsV1 example
params:
- name: Execution payload
value:
parentHash: '0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a'
feeRecipient: '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b'
stateRoot: '0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45'
receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
prevRandao: '0x0000000000000000000000000000000000000000000000000000000000000000'
blockNumber: '0x1'
gasLimit: '0x1c9c380'
gasUsed: '0x0'
timestamp: '0x5'
extraData: '0x'
baseFeePerGas: '0x7'
blockHash: '0x3559e851470f6e7bbed1db474980683e8c315bfce99b2a6ef47c057c04de7858'
transactions: []
withdrawals: []
blobGasUsed: '0x0'
excessBlobGas: '0x0'
- name: Versioned hashes
value: []
- name: Parent beacon block root
value: '0x0000000000000000000000000000000000000000000000000000000000000000'
- name: Proof attributes
value:
proofTypes:
- '0x1'
result:
name: Proof generation ID
value: '0x0000000000000001'
Loading