From f45e6ce10ed6db767ebe727f14918c4b3316c175 Mon Sep 17 00:00:00 2001 From: frisitano Date: Sun, 11 Jan 2026 20:57:58 +0000 Subject: [PATCH 01/28] initial eip 8025 proposal --- specs/_features/eip8025/beacon-chain.md | 235 +++++++++++++++-------- specs/_features/eip8025/p2p-interface.md | 51 +++-- specs/_features/eip8025/validator.md | 76 +------- specs/_features/eip8025/zkevm.md | 28 ++- 4 files changed, 207 insertions(+), 183 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index b383668f06..16504974fb 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -16,14 +16,22 @@ - [New containers](#new-containers) - [`ExecutionProof`](#executionproof) - [`SignedExecutionProof`](#signedexecutionproof) + - [`NewPayloadRequestHeader`](#newpayloadrequestheader) + - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) + - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) - [Extended containers](#extended-containers) - [Helpers](#helpers) - [Execution proof functions](#execution-proof-functions) - [`verify_execution_proof`](#verify_execution_proof) - [`verify_execution_proofs`](#verify_execution_proofs) + - [ExecutionEngine methods](#executionengine-methods) + - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) + - [New `verify_and_notify_new_execution_proof`](#new-verify_and_notify_new_execution_proof) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution payload processing](#execution-payload-processing) - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Execution proof handlers](#execution-proof-handlers) + - [New `process_signed_execution_proof`](#new-process_signed_execution_proof) @@ -32,7 +40,7 @@ These are the beacon-chain specifications to add EIP-8025. This enables stateless validation of execution payloads through cryptographic proofs. -*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md). +*Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md). *Note*: This specification assumes the reader is familiar with the [public zkEVM methods exposed](./zkevm.md). @@ -68,9 +76,8 @@ stateless validation of execution payloads through cryptographic proofs. ```python class ExecutionProof(Container): - beacon_root: Root zk_proof: ZKEVMProof - validator_index: ValidatorIndex + builder_index: BuilderIndex ``` #### `SignedExecutionProof` @@ -81,6 +88,38 @@ class SignedExecutionProof(Container): signature: BLSSignature ``` +#### `NewPayloadRequestHeader` + +```python +@dataclass +class NewPayloadRequestHeader(object): + execution_payload_header: ExecutionPayloadHeader + versioned_hashes: Sequence[VersionedHash] + parent_beacon_block_root: Root + execution_requests: ExecutionRequests +``` + +#### `ExecutionPayloadHeaderEnvelope` + +```python +class ExecutionPayloadHeaderEnvelope(Container): + payload: ExecutionPayloadHeader + execution_requests: ExecutionRequests + builder_index: BuilderIndex + beacon_block_root: Root + slot: Slot + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] + state_root: Root +``` + +#### `SignedExecutionPayloadHeaderEnvelope` + +```python +class SignedExecutionPayloadHeaderEnvelope(Container): + message: ExecutionPayloadHeaderEnvelope + signature: BLSSignature +``` + ### Extended containers *Note*: `BeaconState` and `BeaconBlockBody` remain the same. No modifications @@ -94,56 +133,99 @@ are required for execution proofs since they are handled externally. ```python def verify_execution_proof( - signed_proof: SignedExecutionProof, - parent_hash: Hash32, - block_hash: Hash32, - state: BeaconState, + proof: ExecutionProof, el_program: ProgramBytecode, ) -> bool: """ - Verify an execution proof against a payload header using zkEVM verification. + Verify an execution proof against a NewPayloadRequest root using zkEVM verification. """ - - # Note: signed_proof.message.beacon_root verification will be done at a higher level - - # Verify the validator signature - proof_message = signed_proof.message - validator = state.validators[proof_message.validator_index] - signing_root = compute_signing_root(proof_message, get_domain(state, DOMAIN_EXECUTION_PROOF)) - if not bls.Verify(validator.pubkey, signing_root, signed_proof.signature): - return False - # Derive program bytecode from the EL program identifier and proof type program_bytecode = ProgramBytecode( - el_program + proof_message.zk_proof.proof_type.to_bytes(1, "little") + el_program + proof.zk_proof.proof_type.to_bytes(1, "little") ) - return verify_zkevm_proof(proof_message.zk_proof, parent_hash, block_hash, program_bytecode) + return verify_zkevm_proof(proof.zk_proof, program_bytecode) ``` #### `verify_execution_proofs` ```python -def verify_execution_proofs(parent_hash: Hash32, block_hash: Hash32, state: BeaconState) -> bool: +def verify_execution_proofs(self: ExecutionEngine, new_payload_request_root: Root) -> bool: """ Verify that execution proofs are available and valid for an execution payload. """ # `retrieve_execution_proofs` is implementation and context dependent. - # It returns all execution proofs for the given payload block hash. - signed_execution_proofs = retrieve_execution_proofs(block_hash) + # It returns all execution proofs for the given new_payload_request_root. + proofs = self.execution_proof_store[new_payload_request_root] # Verify there are sufficient proofs - if len(signed_execution_proofs) < MIN_REQUIRED_EXECUTION_PROOFS: + if len(proofs) < MIN_REQUIRED_EXECUTION_PROOFS: + return False + + return True +``` + +### ExecutionEngine methods + +#### Modified `verify_and_notify_new_payload` + +```python +def verify_and_notify_new_payload( + self: ExecutionEngine, + new_payload_request: NewPayloadRequest | NewPayloadRequestHeader, +) -> bool: + """ + Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. + When a ``NewPayloadRequestHeader`` is provided, validation uses execution proofs. + """ + if isinstance(new_payload_request, NewPayloadRequestHeader): + # Header-only validation using execution proofs + # The proof verifies the full NewPayloadRequest + return self.verify_execution_proofs(new_payload_request.hash_tree_root()) + + # Full payload validation (existing GLOAS logic) + execution_payload = new_payload_request.execution_payload + parent_beacon_block_root = new_payload_request.parent_beacon_block_root + + if b"" in execution_payload.transactions: return False - # Verify all execution proofs - for signed_proof in signed_execution_proofs: - if not verify_execution_proof(signed_proof, parent_hash, block_hash, state, PROGRAM): - return False + if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root): + return False + + if not self.is_valid_versioned_hashes(new_payload_request): + return False + + if not self.notify_new_payload(execution_payload, parent_beacon_block_root): + return False return True ``` +#### New `verify_and_notify_new_execution_proof` + +```python +def verify_and_notify_new_execution_proof( + self: ExecutionEngine, + proof: ExecutionProof, +) -> bool: + """ + Verify an execution proof and return the payload status. + """ + assert verify_execution_proof(proof, PROGRAM) + + # Store the valid proof + new_payload_request_root = proof.zk_proof.public_inputs.new_payload_request_root + self.execution_proof_store[new_payload_request_root].append(proof) + + # If we have sufficient proofs, we can consider the payload valid + if len(self.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: + return True + + # In practice this represents a SYNCING status until sufficient proofs are gathered + return False +``` + ## Beacon chain state transition function ### Execution payload processing @@ -153,64 +235,53 @@ def verify_execution_proofs(parent_hash: Hash32, block_hash: Hash32, state: Beac ```python def process_execution_payload( state: BeaconState, - body: BeaconBlockBody, + # [Modified in EIP-8025] + # Accept either full envelope or header-only envelope + signed_envelope: SignedExecutionPayloadEnvelope | SignedExecutionPayloadHeaderEnvelope, execution_engine: ExecutionEngine, - stateless_validation: bool = False, + verify: bool = True, ) -> None: """ - Note: This function is modified to support optional stateless validation with execution proofs. + Process an execution payload envelope or header. + When a header is provided, validation uses execution proofs. """ - payload = body.execution_payload - - # Verify consistency of the parent hash with respect to the previous execution payload header - assert payload.parent_hash == state.latest_execution_payload_header.block_hash - # Verify prev_randao - assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_time_at_slot(state, state.slot) - # Verify commitments are under limit - assert ( - len(body.blob_kzg_commitments) - <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block - ) - if stateless_validation: - # Stateless validation using execution proofs - assert verify_execution_proofs(payload.parent_hash, payload.block_hash, state) + ... + + # The rest of the function remains unchanged +``` + +### Execution proof handlers + +#### New `process_signed_execution_proof` + +```python +def process_signed_execution_proof( + state: BeaconState, + signed_proof: SignedExecutionProof, + execution_engine: ExecutionEngine, +) -> None: + """ + Handler for SignedExecutionProof. + Verifies the signature and calls verify_and_notify_new_execution_proof on the execution engine. + """ + proof_message = signed_proof.message + + # Verify the builder signature + builder_index = proof_message.builder_index + if builder_index == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey else: - # Compute list of versioned hashes - versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments - ] - - # Verify the execution payload is valid - assert execution_engine.verify_and_notify_new_payload( - NewPayloadRequest( - execution_payload=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=body.execution_requests, - ) - ) - - # Cache execution payload header - state.latest_execution_payload_header = ExecutionPayloadHeader( - parent_hash=payload.parent_hash, - fee_recipient=payload.fee_recipient, - state_root=payload.state_root, - receipts_root=payload.receipts_root, - logs_bloom=payload.logs_bloom, - prev_randao=payload.prev_randao, - block_number=payload.block_number, - gas_limit=payload.gas_limit, - gas_used=payload.gas_used, - timestamp=payload.timestamp, - extra_data=payload.extra_data, - base_fee_per_gas=payload.base_fee_per_gas, - block_hash=payload.block_hash, - transactions_root=hash_tree_root(payload.transactions), - withdrawals_root=hash_tree_root(payload.withdrawals), - blob_gas_used=payload.blob_gas_used, - excess_blob_gas=payload.excess_blob_gas, - ) + pubkey = state.builders[builder_index].pubkey + signing_root = compute_signing_root(proof_message, get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot))) + assert bls.Verify(pubkey, signing_root, signed_proof.signature) + + # Verify the execution proof with the execution engine + is_valid = execution_engine.verify_and_notify_new_execution_proof(proof_message) + + if is_valid: + # We have verified enough proofs to consider the payload valid + # Update the state to reflect this + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 ``` diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 85a0ca3992..045b1793cc 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -2,6 +2,8 @@ This document contains the networking specifications for EIP-8025. +*Note*: This specification is built upon [Gloas](../../gloas/p2p-interface.md). + ## Table of contents @@ -12,10 +14,11 @@ This document contains the networking specifications for EIP-8025. - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) + - [`signed_execution_payload_envelope_header`](#signed_execution_payload_envelope_header) - [`execution_proof_{subnet_id}`](#execution_proof_subnet_id) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) - - [ExecutionProofsByHash](#executionproofsbyhash) + - [ExecutionProofsByRoot](#executionproofsbyroot) @@ -36,6 +39,27 @@ containers. No additional message wrapper is needed. #### Global topics +##### `signed_execution_payload_envelope_header` + +This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. +ZK attesters subscribe to this topic to receive execution payload headers for +which they can generate execution proofs. + +The following validations MUST pass before forwarding the +`signed_execution_payload_envelope_header` on the network: + +- _[IGNORE]_ The header is the first valid header received for the tuple + `(signed_execution_payload_envelope_header.message.beacon_block_root, + signed_execution_payload_envelope_header.message.slot)`. +- _[REJECT]_ The `signed_execution_payload_envelope_header.message.beacon_block_root` + refers to a known beacon block. +- _[REJECT]_ The `signed_execution_payload_envelope_header.message.builder_index` + is within the known builder registry. +- _[REJECT]_ The `signed_execution_payload_envelope_header.signature` is valid + with respect to the builder's public key. +- _[REJECT]_ The `signed_execution_payload_envelope_header.message.slot` matches + the slot of the referenced beacon block. + ##### `execution_proof_{subnet_id}` Execution proof subnets are used to propagate execution proofs for specific @@ -53,26 +77,25 @@ The following validations MUST pass before forwarding the `signed_execution_proof` on the network: - _[IGNORE]_ The proof is the first valid proof received for the tuple - `(signed_execution_proof.message.zk_proof.public_inputs.block_hash, subnet_id)`. -- _[REJECT]_ The `signed_execution_proof.message.validator_index` is within the - known validator registry. + `(signed_execution_proof.message.zk_proof.public_inputs.new_payload_request_root, subnet_id)`. +- _[REJECT]_ The `signed_execution_proof.message.builder_index` is within the + known builder registry. - _[REJECT]_ The `signed_execution_proof.signature` is valid with respect to the - validator's public key. + builder's public key. - _[REJECT]_ The `signed_execution_proof.message.zk_proof.proof_data` is non-empty. - _[REJECT]_ The proof system ID matches the subnet: `signed_execution_proof.message.zk_proof.proof_type == subnet_id`. - _[REJECT]_ The execution proof is valid as verified by - `verify_execution_proof()` with the appropriate parent and block hashes from - the execution layer. + `process_signed_execution_proof()`. ## The Req/Resp domain ### Messages -#### ExecutionProofsByHash +#### ExecutionProofsByRoot -**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_hash/1/` +**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` The `` field is calculated as `context = compute_fork_digest(fork_version, genesis_validators_root)`. @@ -81,7 +104,7 @@ Request Content: ``` ( - Hash32 # block_hash + Root # new_payload_request_root ) ``` @@ -93,16 +116,16 @@ Response Content: ) ``` -Requests execution proofs for the given execution payload `block_hash`. The -response MUST contain all available proofs for the requested block hash, up to +Requests execution proofs for the given `new_payload_request_root`. The +response MUST contain all available proofs for the requested root, up to `MAX_EXECUTION_PROOFS_PER_PAYLOAD`. The following validations MUST pass: -- _[REJECT]_ The `block_hash` is a 32-byte value. +- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. The response MUST contain: -- All available execution proofs for the requested block hash. +- All available execution proofs for the requested `new_payload_request_root`. - The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` proofs. diff --git a/specs/_features/eip8025/validator.md b/specs/_features/eip8025/validator.md index 1f80bd0ffd..909196862f 100644 --- a/specs/_features/eip8025/validator.md +++ b/specs/_features/eip8025/validator.md @@ -1,7 +1,6 @@ # EIP-8025 -- Honest Validator -**Notice**: This document is a work-in-progress for researchers and -implementers. +*Note*: This document is a work-in-progress for researchers and implementers. ## Table of contents @@ -10,83 +9,20 @@ implementers. - [Table of contents](#table-of-contents) - [Introduction](#introduction) - [Prerequisites](#prerequisites) -- [Configuration](#configuration) -- [Optional execution proof generation](#optional-execution-proof-generation) - - [`generate_execution_proof`](#generate_execution_proof) - - [`broadcast_execution_proof`](#broadcast_execution_proof) ## Introduction -This document represents optional execution proof generation capabilities that -validators may choose to implement. +This document represents the changes to the validator guide accompanying +EIP-8025. ## Prerequisites This document is an extension of the -[Fulu -- Honest Validator](../../fulu/validator.md) guide. All behaviors and +[Gloas -- Honest Validator](../../gloas/validator.md) guide. All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. -## Configuration - -| Name | Value | -| ------------------------------------ | ------- | -| `EXECUTION_PROOF_GENERATION_ENABLED` | `False` | - -## Optional execution proof generation - -Validators MAY choose to generate execution proofs for payloads they propose or -receive. - -### `generate_execution_proof` - -```python -def generate_execution_proof( - payload: ExecutionPayload, execution_witness: ZKExecutionWitness, proof_id: ProofID -) -> Optional[SignedExecutionProof]: - """ - Generate an execution proof for the given payload. - """ - if not EXECUTION_PROOF_GENERATION_ENABLED: - return None - - zk_proof = generate_zkevm_proof(payload, execution_witness, PROGRAM, proof_id) - - if zk_proof is None: - return None - - validator_index = get_validator_index() - beacon_root = get_current_beacon_root() - - execution_proof_message = ExecutionProof( - beacon_root=beacon_root, - zk_proof=zk_proof, - validator_index=validator_index, - ) - - signing_root = compute_signing_root( - execution_proof_message, get_domain(get_current_state(), DOMAIN_EXECUTION_PROOF) - ) - signature = bls.Sign(get_validator_private_key(), signing_root) - - return SignedExecutionProof( - message=execution_proof_message, - signature=signature, - ) -``` - -### `broadcast_execution_proof` - -```python -def broadcast_execution_proof(signed_proof: SignedExecutionProof) -> None: - """ - Broadcast an execution proof to the network. - """ - # Broadcast on the appropriate subnet based on proof system - subnet_id = compute_subnet_for_execution_proof(signed_proof.message.zk_proof.proof_type) - topic = f"execution_proof_{subnet_id}" - - broadcast_to_topic(topic, signed_proof) -``` +*Note*: Execution proof generation is handled by builders. See +[builder.md](./builder.md). diff --git a/specs/_features/eip8025/zkevm.md b/specs/_features/eip8025/zkevm.md index 2831ea0fde..7833ff64b9 100644 --- a/specs/_features/eip8025/zkevm.md +++ b/specs/_features/eip8025/zkevm.md @@ -84,7 +84,7 @@ class ZKEVMProof(Container): ```python class PrivateInput(Container): - execution_payload: ExecutionPayload + new_payload_request: NewPayloadRequest execution_witness: ZKExecutionWitness ``` @@ -92,8 +92,7 @@ class PrivateInput(Container): ```python class PublicInput(Container): - block_hash: Hash32 - parent_hash: Hash32 + new_payload_request_root: Root # hash_tree_root(NewPayloadRequest) ``` ## Helpers @@ -155,10 +154,10 @@ def generate_execution_proof_impl( public_inputs: PublicInput, ) -> ZKEVMProof: """ - Generate a zkEVM execution proof using the proving key, private inputs and public inputs + Generate a zkEVM execution proof using the proving key, private inputs and public inputs. """ proof_data = hash( - public_inputs.block_hash + public_inputs.parent_hash + proof_id.to_bytes(1, "little") + public_inputs.new_payload_request_root + proof_id.to_bytes(1, "little") ) return ZKEVMProof( @@ -180,17 +179,12 @@ def generate_proving_key(program_bytecode: ProgramBytecode, proof_id: ProofID) - ```python def verify_zkevm_proof( - zk_proof: ZKEVMProof, parent_hash: Hash32, block_hash: Hash32, program_bytecode: ProgramBytecode + zk_proof: ZKEVMProof, + program_bytecode: ProgramBytecode, ) -> bool: """ - Public method to verify a zkEVM execution proof against block hashes. + Public method to verify a zkEVM execution proof against NewPayloadRequest root. """ - # Validate that public inputs match the provided parent and current block hash - if zk_proof.public_inputs.block_hash != block_hash: - return False - if zk_proof.public_inputs.parent_hash != parent_hash: - return False - _, verification_key = generate_keys(program_bytecode, zk_proof.proof_type) return verify_execution_proof_impl(zk_proof, verification_key) @@ -200,21 +194,21 @@ def verify_zkevm_proof( ```python def generate_zkevm_proof( - execution_payload: ExecutionPayload, + new_payload_request: NewPayloadRequest, execution_witness: ZKExecutionWitness, program_bytecode: ProgramBytecode, proof_id: ProofID, ) -> Optional[ZKEVMProof]: """ - Public method to generate an execution proof for a payload. + Public method to generate an execution proof for a NewPayloadRequest. """ proving_key, _ = generate_keys(program_bytecode, proof_id) public_inputs = PublicInput( - block_hash=execution_payload.block_hash, parent_hash=execution_payload.parent_hash + new_payload_request_root=hash_tree_root(new_payload_request) ) private_input = PrivateInput( - execution_payload=execution_payload, execution_witness=execution_witness + new_payload_request=new_payload_request, execution_witness=execution_witness ) return generate_execution_proof_impl(private_input, proving_key, proof_id, public_inputs) From 18bff2db4590283ec445afe828ea1b82f1b2392a Mon Sep 17 00:00:00 2001 From: frisitano Date: Sun, 11 Jan 2026 21:01:17 +0000 Subject: [PATCH 02/28] add new files --- specs/_features/eip8025/builder.md | 97 ++++++++++++++++++++++++++ specs/_features/eip8025/fork-choice.md | 53 ++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 specs/_features/eip8025/builder.md create mode 100644 specs/_features/eip8025/fork-choice.md diff --git a/specs/_features/eip8025/builder.md b/specs/_features/eip8025/builder.md new file mode 100644 index 0000000000..4b1927eb73 --- /dev/null +++ b/specs/_features/eip8025/builder.md @@ -0,0 +1,97 @@ +# EIP-8025 -- Honest Builder + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Configuration](#configuration) +- [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) +- [Execution proof signature](#execution-proof-signature) +- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) + + + +## Introduction + +This document represents the changes to the builder guide accompanying EIP-8025. + +## Prerequisites + +This document is an extension of the +[Gloas -- Honest Builder](../../gloas/builder.md) guide. All behaviors and +definitions defined in this document, and documents it extends, carry over +unless explicitly noted or overridden. + +All terminology, constants, functions, and protocol mechanics defined in the +[EIP-8025 -- Beacon Chain](./beacon-chain.md) and +[EIP-8025 -- zkEVM](./zkevm.md) documents are requisite for this document. + +## Configuration + +| Name | Value | +| ------------------------------------ | ------- | +| `EXECUTION_PROOF_GENERATION_ENABLED` | `False` | + +## Constructing the `SignedExecutionPayloadHeaderEnvelope` + +Builders MUST broadcast `SignedExecutionPayloadHeaderEnvelope` messages to allow +ZK attesters to perform proof verification. + +To construct the `SignedExecutionPayloadHeaderEnvelope` from an existing +`SignedExecutionPayloadEnvelope`: + +1. Set `header_envelope.payload` to the `ExecutionPayloadHeader` derived from + `envelope.payload`. +2. Copy all other fields from the original envelope: + - `header_envelope.execution_requests = envelope.execution_requests` + - `header_envelope.builder_index = envelope.builder_index` + - `header_envelope.beacon_block_root = envelope.beacon_block_root` + - `header_envelope.slot = envelope.slot` + - `header_envelope.blob_kzg_commitments = envelope.blob_kzg_commitments` + - `header_envelope.state_root = envelope.state_root` +3. Set `signed_header_envelope.message = header_envelope`. +4. Set `signed_header_envelope.signature = signed_envelope.signature`. + +Then the builder broadcasts `signed_header_envelope` on the +`signed_execution_payload_envelope_header` global gossip topic. + +## Execution proof signature + +```python +def get_execution_proof_signature( + state: BeaconState, proof: ExecutionProof, privkey: int +) -> BLSSignature: + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof, domain) + return bls.Sign(privkey, signing_root) +``` + +## Constructing the `SignedExecutionProof` + +After producing an `ExecutionPayloadEnvelope` the builder constructs a set of +`SignedExecutionProof` as follows: + +1. Extract the `NewPayloadRequest` from the envelope. +2. Obtain the `ZKExecutionWitness` from the execution layer. +3. Select a `proof_id` corresponding to the proof system being used. +4. Call `generate_zkevm_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` + to produce the `ZKEVMProof`. +5. Set `proof.zk_proof` to the generated `ZKEVMProof`. +6. Set `proof.builder_index` to the builder's index. +7. Set `signed_proof.message` to `proof`. +8. Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + +Then the builder assembles +`signed_execution_proof = SignedExecutionProof(message=proof, signature=signature)` +and broadcasts it on the `execution_proof_{subnet_id}` gossip topic, where +`subnet_id = proof.zk_proof.proof_type`. + +*Note*: The `proof_id` determines which subnet the proof is broadcast on. Each +proof system has a dedicated subnet to allow validators to subscribe to proofs +from specific proof systems. diff --git a/specs/_features/eip8025/fork-choice.md b/specs/_features/eip8025/fork-choice.md new file mode 100644 index 0000000000..2275b8fbf6 --- /dev/null +++ b/specs/_features/eip8025/fork-choice.md @@ -0,0 +1,53 @@ +# EIP-8025 -- Fork Choice + +*Note*: This document is a work-in-progress for researchers and implementers. + + + +- [Introduction](#introduction) +- [Handlers](#handlers) + - [Modified `on_execution_payload`](#modified-on_execution_payload) + + + +## Introduction + +This is the modification of the fork choice accompanying the EIP-8025 upgrade, +enabling stateless validation of execution payloads through cryptographic proofs. + +*Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md). + +## Handlers + +### Modified `on_execution_payload` + +The handler `on_execution_payload` is modified to accept either a full +`SignedExecutionPayloadEnvelope` or a `SignedExecutionPayloadHeaderEnvelope`. + +```python +def on_execution_payload( + store: Store, + # [Modified in EIP-8025] + # Accept either full envelope or header-only envelope + signed_envelope: SignedExecutionPayloadEnvelope | SignedExecutionPayloadHeaderEnvelope, +) -> None: + """ + Run ``on_execution_payload`` upon receiving a new execution payload or header. + """ + envelope = signed_envelope.message + # The corresponding beacon block root needs to be known + assert envelope.beacon_block_root in store.block_states + + # Check if blob data is available + # If not, this payload MAY be queued and subsequently considered when blob data becomes available + assert is_data_available(envelope.beacon_block_root) + + # Make a copy of the state to avoid mutability issues + state = copy(store.block_states[envelope.beacon_block_root]) + + # Process the execution payload (handles both full envelope and header) + process_execution_payload(state, signed_envelope, EXECUTION_ENGINE) + + # Add new state for this payload to the store + store.execution_payload_states[envelope.beacon_block_root] = state +``` From d046541e1322cb680f729d5a1743603ec88ac332 Mon Sep 17 00:00:00 2001 From: frisitano Date: Mon, 12 Jan 2026 02:26:56 +0000 Subject: [PATCH 03/28] remove engine API and introdue prover --- specs/_features/eip8025/beacon-chain.md | 250 ++++++++++++----------- specs/_features/eip8025/builder.md | 15 +- specs/_features/eip8025/p2p-interface.md | 23 ++- specs/_features/eip8025/prover.md | 102 +++++++++ specs/_features/eip8025/validator.md | 4 +- specs/_features/eip8025/zkevm.md | 36 ++-- 6 files changed, 277 insertions(+), 153 deletions(-) create mode 100644 specs/_features/eip8025/prover.md diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 16504974fb..f540f0be1c 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -14,19 +14,15 @@ - [Configuration](#configuration) - [Containers](#containers) - [New containers](#new-containers) - - [`ExecutionProof`](#executionproof) - [`SignedExecutionProof`](#signedexecutionproof) - [`NewPayloadRequestHeader`](#newpayloadrequestheader) - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) - [Extended containers](#extended-containers) + - [`BeaconState`](#beaconstate) - [Helpers](#helpers) - [Execution proof functions](#execution-proof-functions) - - [`verify_execution_proof`](#verify_execution_proof) - [`verify_execution_proofs`](#verify_execution_proofs) - - [ExecutionEngine methods](#executionengine-methods) - - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) - - [New `verify_and_notify_new_execution_proof`](#new-verify_and_notify_new_execution_proof) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution payload processing](#execution-payload-processing) - [Modified `process_execution_payload`](#modified-process_execution_payload) @@ -68,23 +64,22 @@ stateless validation of execution payloads through cryptographic proofs. | ------------------------------- | ----------- | | `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | +```python +WHITELISTED_PROVERS: List[BLSPubkey] = [ + # List of allowed prover public keys +] +``` + ## Containers ### New containers -#### `ExecutionProof` - -```python -class ExecutionProof(Container): - zk_proof: ZKEVMProof - builder_index: BuilderIndex -``` - #### `SignedExecutionProof` ```python class SignedExecutionProof(Container): message: ExecutionProof + prover_id: Union[BuilderIndex, BLSPubkey] signature: BLSSignature ``` @@ -122,41 +117,29 @@ class SignedExecutionPayloadHeaderEnvelope(Container): ### Extended containers -*Note*: `BeaconState` and `BeaconBlockBody` remain the same. No modifications -are required for execution proofs since they are handled externally. - -## Helpers +#### `BeaconState` -### Execution proof functions +```python +class BeaconState(Container): + # ... existing fields ... + # [New in EIP-8025] + execution_proof_store: Map[Root, List[ExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] +``` -#### `verify_execution_proof` +*Note*: `BeaconBlockBody` remains unchanged. -```python -def verify_execution_proof( - proof: ExecutionProof, - el_program: ProgramBytecode, -) -> bool: - """ - Verify an execution proof against a NewPayloadRequest root using zkEVM verification. - """ - # Derive program bytecode from the EL program identifier and proof type - program_bytecode = ProgramBytecode( - el_program + proof.zk_proof.proof_type.to_bytes(1, "little") - ) +## Helpers - return verify_zkevm_proof(proof.zk_proof, program_bytecode) -``` +### Execution proof functions #### `verify_execution_proofs` ```python -def verify_execution_proofs(self: ExecutionEngine, new_payload_request_root: Root) -> bool: +def verify_execution_proofs(state: BeaconState, new_payload_request_root: Root) -> bool: """ Verify that execution proofs are available and valid for an execution payload. """ - # `retrieve_execution_proofs` is implementation and context dependent. - # It returns all execution proofs for the given new_payload_request_root. - proofs = self.execution_proof_store[new_payload_request_root] + proofs = state.execution_proof_store.get(new_payload_request_root, []) # Verify there are sufficient proofs if len(proofs) < MIN_REQUIRED_EXECUTION_PROOFS: @@ -165,67 +148,6 @@ def verify_execution_proofs(self: ExecutionEngine, new_payload_request_root: Roo return True ``` -### ExecutionEngine methods - -#### Modified `verify_and_notify_new_payload` - -```python -def verify_and_notify_new_payload( - self: ExecutionEngine, - new_payload_request: NewPayloadRequest | NewPayloadRequestHeader, -) -> bool: - """ - Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. - When a ``NewPayloadRequestHeader`` is provided, validation uses execution proofs. - """ - if isinstance(new_payload_request, NewPayloadRequestHeader): - # Header-only validation using execution proofs - # The proof verifies the full NewPayloadRequest - return self.verify_execution_proofs(new_payload_request.hash_tree_root()) - - # Full payload validation (existing GLOAS logic) - execution_payload = new_payload_request.execution_payload - parent_beacon_block_root = new_payload_request.parent_beacon_block_root - - if b"" in execution_payload.transactions: - return False - - if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root): - return False - - if not self.is_valid_versioned_hashes(new_payload_request): - return False - - if not self.notify_new_payload(execution_payload, parent_beacon_block_root): - return False - - return True -``` - -#### New `verify_and_notify_new_execution_proof` - -```python -def verify_and_notify_new_execution_proof( - self: ExecutionEngine, - proof: ExecutionProof, -) -> bool: - """ - Verify an execution proof and return the payload status. - """ - assert verify_execution_proof(proof, PROGRAM) - - # Store the valid proof - new_payload_request_root = proof.zk_proof.public_inputs.new_payload_request_root - self.execution_proof_store[new_payload_request_root].append(proof) - - # If we have sufficient proofs, we can consider the payload valid - if len(self.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: - return True - - # In practice this represents a SYNCING status until sufficient proofs are gathered - return False -``` - ## Beacon chain state transition function ### Execution payload processing @@ -237,7 +159,7 @@ def process_execution_payload( state: BeaconState, # [Modified in EIP-8025] # Accept either full envelope or header-only envelope - signed_envelope: SignedExecutionPayloadEnvelope | SignedExecutionPayloadHeaderEnvelope, + signed_envelope: Union[SignedExecutionPayloadEnvelope, SignedExecutionPayloadHeaderEnvelope], execution_engine: ExecutionEngine, verify: bool = True, ) -> None: @@ -246,9 +168,94 @@ def process_execution_payload( When a header is provided, validation uses execution proofs. """ - ... + envelope = signed_envelope.message + payload = envelope.payload + + # Verify signature + if verify: + assert verify_execution_payload_envelope_signature(state, signed_envelope) + + # Cache latest block header state root + previous_state_root = hash_tree_root(state) + if state.latest_block_header.state_root == Root(): + state.latest_block_header.state_root = previous_state_root + + # Verify consistency with the beacon block + assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) + assert envelope.slot == state.slot + + # Verify consistency with the committed bid + committed_bid = state.latest_execution_payload_bid + assert envelope.builder_index == committed_bid.builder_index + assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) + assert committed_bid.prev_randao == payload.prev_randao + + # Verify consistency with expected withdrawals + assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals) + + # Verify the gas_limit + assert committed_bid.gas_limit == payload.gas_limit + # Verify the block hash + assert committed_bid.block_hash == payload.block_hash + # Verify consistency of the parent hash with respect to the previous execution payload + assert payload.parent_hash == state.latest_block_hash + # Verify timestamp + assert payload.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(envelope.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + # Verify the execution payload is valid + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments + ] + requests = envelope.execution_requests + + if isinstance(signed_envelope, SignedExecutionPayloadHeaderEnvelope): + # Header-only validation using execution proofs + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + assert verify_execution_proofs(state, hash_tree_root(new_payload_request_header)) + else: + # Full payload validation via ExecutionEngine + assert execution_engine.verify_and_notify_new_payload( + NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + ) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(requests.deposits, process_deposit_request) + for_ops(requests.withdrawals, process_withdrawal_request) + for_ops(requests.consolidations, process_consolidation_request) + + # Queue the builder payment + payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] + amount = payment.withdrawal.amount + if amount > 0: + state.builder_pending_withdrawals.append(payment.withdrawal) + state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( + BuilderPendingPayment() + ) - # The rest of the function remains unchanged + # Cache the execution payload hash + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + state.latest_block_hash = payload.block_hash + + # Verify the state root + if verify: + assert envelope.state_root == hash_tree_root(state) ``` ### Execution proof handlers @@ -259,29 +266,42 @@ def process_execution_payload( def process_signed_execution_proof( state: BeaconState, signed_proof: SignedExecutionProof, - execution_engine: ExecutionEngine, ) -> None: """ Handler for SignedExecutionProof. - Verifies the signature and calls verify_and_notify_new_execution_proof on the execution engine. + Supports both builders (via BuilderIndex) and provers (via BLSPubkey). """ proof_message = signed_proof.message + prover_id = signed_proof.prover_id - # Verify the builder signature - builder_index = proof_message.builder_index - if builder_index == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey + # Determine pubkey based on prover_id type + if isinstance(prover_id, BLSPubkey): + # Prover path - verify whitelist + assert prover_id in WHITELISTED_PROVERS + pubkey = prover_id else: - pubkey = state.builders[builder_index].pubkey - signing_root = compute_signing_root(proof_message, get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot))) + # Builder path + if prover_id == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey + else: + pubkey = state.builders[prover_id].pubkey + + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof_message, domain) assert bls.Verify(pubkey, signing_root, signed_proof.signature) - # Verify the execution proof with the execution engine - is_valid = execution_engine.verify_and_notify_new_execution_proof(proof_message) + # Verify the execution proof + program_bytecode = ProgramBytecode(PROGRAM + proof_message.proof_type.to_bytes(1, "little")) + assert verify_execution_proof(proof_message, program_bytecode) + + # Store the valid proof in state + new_payload_request_root = proof_message.public_inputs.new_payload_request_root + if new_payload_request_root not in state.execution_proof_store: + state.execution_proof_store[new_payload_request_root] = [] + state.execution_proof_store[new_payload_request_root].append(proof_message) - if is_valid: - # We have verified enough proofs to consider the payload valid - # Update the state to reflect this + # Mark payload as available if sufficient proofs gathered + if len(state.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 ``` diff --git a/specs/_features/eip8025/builder.md b/specs/_features/eip8025/builder.md index 4b1927eb73..8ff464b691 100644 --- a/specs/_features/eip8025/builder.md +++ b/specs/_features/eip8025/builder.md @@ -79,18 +79,17 @@ After producing an `ExecutionPayloadEnvelope` the builder constructs a set of 1. Extract the `NewPayloadRequest` from the envelope. 2. Obtain the `ZKExecutionWitness` from the execution layer. 3. Select a `proof_id` corresponding to the proof system being used. -4. Call `generate_zkevm_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` - to produce the `ZKEVMProof`. -5. Set `proof.zk_proof` to the generated `ZKEVMProof`. -6. Set `proof.builder_index` to the builder's index. -7. Set `signed_proof.message` to `proof`. -8. Set `signed_proof.signature` to the result of +4. Call `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` + to produce the `ExecutionProof`. +5. Set `signed_proof.message` to the generated `ExecutionProof`. +6. Set `signed_proof.prover_id` to the builder's `BuilderIndex`. +7. Set `signed_proof.signature` to the result of `get_execution_proof_signature(state, proof, privkey)`. Then the builder assembles -`signed_execution_proof = SignedExecutionProof(message=proof, signature=signature)` +`signed_execution_proof = SignedExecutionProof(message=proof, prover_id=builder_index, signature=signature)` and broadcasts it on the `execution_proof_{subnet_id}` gossip topic, where -`subnet_id = proof.zk_proof.proof_type`. +`subnet_id = proof.proof_type`. *Note*: The `proof_id` determines which subnet the proof is broadcast on. Each proof system has a dedicated subnet to allow validators to subscribe to proofs diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 045b1793cc..f15b869fe2 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -30,8 +30,8 @@ mapping with proof systems. Each proof system gets its own dedicated subnet. ## Containers -*Note*: Execution proofs are broadcast directly as `SignedExecutionProof` -containers. No additional message wrapper is needed. +*Note*: Execution proofs are broadcast as `SignedExecutionProof` containers. No +additional message wrapper is needed. ## The gossip domain: gossipsub @@ -63,7 +63,8 @@ The following validations MUST pass before forwarding the ##### `execution_proof_{subnet_id}` Execution proof subnets are used to propagate execution proofs for specific -proof systems. +proof systems. `SignedExecutionProof` messages from both builders and provers +are propagated on these subnets. The execution proof subnet for a given `proof_id` is: @@ -73,19 +74,21 @@ def compute_subnet_for_execution_proof(proof_id: ProofID) -> SubnetID: return SubnetID(proof_id) ``` -The following validations MUST pass before forwarding the +The following validations MUST pass before forwarding a `signed_execution_proof` on the network: - _[IGNORE]_ The proof is the first valid proof received for the tuple - `(signed_execution_proof.message.zk_proof.public_inputs.new_payload_request_root, subnet_id)`. -- _[REJECT]_ The `signed_execution_proof.message.builder_index` is within the - known builder registry. + `(signed_execution_proof.message.public_inputs.new_payload_request_root, subnet_id)`. +- _[REJECT]_ If `signed_execution_proof.prover_id` is a `BuilderIndex`: the + index is within the known builder registry. +- _[REJECT]_ If `signed_execution_proof.prover_id` is a `BLSPubkey`: the pubkey + is in `WHITELISTED_PROVERS`. - _[REJECT]_ The `signed_execution_proof.signature` is valid with respect to the - builder's public key. -- _[REJECT]_ The `signed_execution_proof.message.zk_proof.proof_data` is + prover's public key. +- _[REJECT]_ The `signed_execution_proof.message.proof_data` is non-empty. - _[REJECT]_ The proof system ID matches the subnet: - `signed_execution_proof.message.zk_proof.proof_type == subnet_id`. + `signed_execution_proof.message.proof_type == subnet_id`. - _[REJECT]_ The execution proof is valid as verified by `process_signed_execution_proof()`. diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md new file mode 100644 index 0000000000..7467fdee6f --- /dev/null +++ b/specs/_features/eip8025/prover.md @@ -0,0 +1,102 @@ +# EIP-8025 -- Honest Prover + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Execution proof signature](#execution-proof-signature) +- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) +- [Honest Prover Relay](#honest-prover-relay) + - [Accepting proofs](#accepting-proofs) + - [Signing and broadcasting](#signing-and-broadcasting) + + + +## Introduction + +This document represents the prover guide accompanying EIP-8025. Provers are +whitelisted network operators who generate execution proofs during the optional +proof phase. + +## Prerequisites + +All terminology, constants, functions, and protocol mechanics defined in the +[EIP-8025 -- Beacon Chain](./beacon-chain.md) and +[EIP-8025 -- zkEVM](./zkevm.md) documents are requisite for this document. + +The prover MUST have their public key included in `WHITELISTED_PROVERS` or alternatively use a whitelisted community proof relay. + +## Execution proof signature + +```python +def get_execution_proof_signature( + state: BeaconState, proof: ExecutionProof, privkey: int +) -> BLSSignature: + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof, domain) + return bls.Sign(privkey, signing_root) +``` + +## Constructing the `SignedExecutionProof` + +Provers subscribe to the `signed_execution_payload_envelope` gossip topic to +receive execution payloads for which they can generate execution proofs. + +To construct a `SignedExecutionProof`: + +1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. +2. Obtain the `ZKExecutionWitness` from the execution layer. +3. Select a `proof_id` corresponding to the proof system being used. +4. Call `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` + to produce the `ExecutionProof`. +5. Set `signed_proof.message` to the generated `ExecutionProof`. +6. Set `signed_proof.prover_id` to the prover's public key. +7. Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + +Then the prover assembles +`signed_execution_proof = SignedExecutionProof(message=proof, prover_id=pubkey, signature=signature)` +and broadcasts it on the `execution_proof_{subnet_id}` gossip topic, where +`subnet_id = proof.proof_type`. + +*Note*: The `proof_id` determines which subnet the proof is broadcast on. Each +proof system has a dedicated subnet to allow validators to subscribe to proofs +from specific proof systems. + +## Honest Prover Relay + +A prover relay is a whitelisted service that accepts execution proofs from +community provers, validates them, signs them, and broadcasts them to the +network. This allows any prover to contribute proofs without needing to be +individually whitelisted. + +### Accepting proofs + +The relay exposes an API endpoint that accepts unsigned `ExecutionProof` +submissions from community provers. Upon receiving a proof, the relay MUST: + +1. Verify the `proof.proof_data` is non-empty. +2. Verify the execution proof is valid using `verify_execution_proof(proof, program_bytecode)`. +3. Verify a proof for the same `(new_payload_request_root, proof_type)` has not + already been signed and broadcast. + +If any validation fails, the relay SHOULD reject the submission. + +### Signing and broadcasting + +After successful validation, the relay signs and broadcasts the proof: + +1. Set `signed_proof.message` to the validated `proof`. +2. Set `signed_proof.prover_id` to the relay's whitelisted public key. +3. Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, relay_privkey)`. +4. Broadcast the `SignedExecutionProof` on the `execution_proof_{subnet_id}` + gossip topic, where `subnet_id = proof.proof_type`. + +*Note*: The relay's public key MUST be included in `WHITELISTED_PROVERS`. The +relay takes responsibility for the validity of proofs it signs. diff --git a/specs/_features/eip8025/validator.md b/specs/_features/eip8025/validator.md index 909196862f..400e153401 100644 --- a/specs/_features/eip8025/validator.md +++ b/specs/_features/eip8025/validator.md @@ -24,5 +24,5 @@ This document is an extension of the definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. -*Note*: Execution proof generation is handled by builders. See -[builder.md](./builder.md). +*Note*: Execution proof generation is handled by builders and provers. See +[builder.md](./builder.md) and [prover.md](./prover.md). diff --git a/specs/_features/eip8025/zkevm.md b/specs/_features/eip8025/zkevm.md index 7833ff64b9..170557fbc3 100644 --- a/specs/_features/eip8025/zkevm.md +++ b/specs/_features/eip8025/zkevm.md @@ -9,7 +9,7 @@ - [Types](#types) - [Cryptographic types](#cryptographic-types) - [Containers](#containers) - - [`ZKEVMProof`](#zkevmproof) + - [`ExecutionProof`](#zkevmproof) - [`PrivateInput`](#privateinput) - [`PublicInput`](#publicinput) - [Helpers](#helpers) @@ -21,8 +21,8 @@ - [Proof generation](#proof-generation) - [`generate_execution_proof_impl`](#generate_execution_proof_impl) - [`generate_proving_key`](#generate_proving_key) - - [`verify_zkevm_proof`](#verify_zkevm_proof) - - [`generate_zkevm_proof`](#generate_zkevm_proof) + - [`verify_execution_proof`](#verify_execution_proof) + - [`generate_execution_proof`](#generate_execution_proof) @@ -52,7 +52,7 @@ for a payload with a maximum gas limit of 30M gas. | Name | SSZ equivalent | Description | | ------------ | -------------- | ------------------------------- | -| `ZKEVMProof` | `Container` | Proof of execution of a program | +| `ExecutionProof` | `Container` | Proof of execution of a program | ## Cryptographic types @@ -71,10 +71,10 @@ layer client. The size depends on the client; `16` is a placeholder. ## Containers -### `ZKEVMProof` +### `ExecutionProof` ```python -class ZKEVMProof(Container): +class ExecutionProof(Container): proof_data: ByteList[MAX_PROOF_SIZE] proof_type: ProofID public_inputs: PublicInput @@ -119,7 +119,7 @@ def generate_keys( #### `verify_execution_proof_impl` ```python -def verify_execution_proof_impl(proof: ZKEVMProof, verification_key: VerificationKey) -> bool: +def verify_execution_proof_impl(proof: ExecutionProof, verification_key: VerificationKey) -> bool: """ Verify a zkEVM execution proof using the verification key. """ @@ -152,7 +152,7 @@ def generate_execution_proof_impl( proving_key: ProvingKey, proof_id: ProofID, public_inputs: PublicInput, -) -> ZKEVMProof: +) -> ExecutionProof: """ Generate a zkEVM execution proof using the proving key, private inputs and public inputs. """ @@ -160,7 +160,7 @@ def generate_execution_proof_impl( public_inputs.new_payload_request_root + proof_id.to_bytes(1, "little") ) - return ZKEVMProof( + return ExecutionProof( proof_data=ByteList(proof_data), proof_type=proof_id, public_inputs=public_inputs ) ``` @@ -175,30 +175,30 @@ def generate_proving_key(program_bytecode: ProgramBytecode, proof_id: ProofID) - return ProvingKey(program_bytecode + proof_id.to_bytes(1, "little")) ``` -### `verify_zkevm_proof` +### `verify_execution_proof` ```python -def verify_zkevm_proof( - zk_proof: ZKEVMProof, +def verify_execution_proof( + proof: ExecutionProof, program_bytecode: ProgramBytecode, ) -> bool: """ - Public method to verify a zkEVM execution proof against NewPayloadRequest root. + Public method to verify an execution proof against NewPayloadRequest root. """ - _, verification_key = generate_keys(program_bytecode, zk_proof.proof_type) + _, verification_key = generate_keys(program_bytecode, proof.proof_type) - return verify_execution_proof_impl(zk_proof, verification_key) + return verify_execution_proof_impl(proof, verification_key) ``` -### `generate_zkevm_proof` +### `generate_execution_proof` ```python -def generate_zkevm_proof( +def generate_execution_proof( new_payload_request: NewPayloadRequest, execution_witness: ZKExecutionWitness, program_bytecode: ProgramBytecode, proof_id: ProofID, -) -> Optional[ZKEVMProof]: +) -> ExecutionProof: """ Public method to generate an execution proof for a NewPayloadRequest. """ From d83368991971f873c99ae0a37b8bf65af8042115 Mon Sep 17 00:00:00 2001 From: frisitano Date: Mon, 12 Jan 2026 02:41:35 +0000 Subject: [PATCH 04/28] update proof ordering logic --- specs/_features/eip8025/beacon-chain.md | 37 +++++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index f540f0be1c..55f9f9e560 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -123,7 +123,8 @@ class SignedExecutionPayloadHeaderEnvelope(Container): class BeaconState(Container): # ... existing fields ... # [New in EIP-8025] - execution_proof_store: Map[Root, List[ExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] + execution_proof_store: Map[Root, List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] + pending_execution_proof_store: Map[Root, List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] ``` *Note*: `BeaconBlockBody` remains unchanged. @@ -220,7 +221,19 @@ def process_execution_payload( parent_beacon_block_root=state.latest_block_header.parent_root, execution_requests=requests, ) - assert verify_execution_proofs(state, hash_tree_root(new_payload_request_header)) + new_payload_request_root = hash_tree_root(new_payload_request_header) + + # Create entry in execution_proof_store (marks header as received) + state.execution_proof_store[new_payload_request_root] = [] + + # Move pending proofs to execution_proof_store + if new_payload_request_root in state.pending_execution_proof_store: + state.execution_proof_store[new_payload_request_root].extend( + state.pending_execution_proof_store[new_payload_request_root] + ) + del state.pending_execution_proof_store[new_payload_request_root] + + assert verify_execution_proofs(state, new_payload_request_root) else: # Full payload validation via ExecutionEngine assert execution_engine.verify_and_notify_new_payload( @@ -295,13 +308,19 @@ def process_signed_execution_proof( program_bytecode = ProgramBytecode(PROGRAM + proof_message.proof_type.to_bytes(1, "little")) assert verify_execution_proof(proof_message, program_bytecode) - # Store the valid proof in state + # Store proof based on whether header has been received new_payload_request_root = proof_message.public_inputs.new_payload_request_root - if new_payload_request_root not in state.execution_proof_store: - state.execution_proof_store[new_payload_request_root] = [] - state.execution_proof_store[new_payload_request_root].append(proof_message) - # Mark payload as available if sufficient proofs gathered - if len(state.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + if new_payload_request_root in state.execution_proof_store: + # Header already received, store directly + state.execution_proof_store[new_payload_request_root].append(signed_proof) + + # Mark payload as available if sufficient proofs gathered + if len(state.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + else: + # Header not yet received, cache in pending store + if new_payload_request_root not in state.pending_execution_proof_store: + state.pending_execution_proof_store[new_payload_request_root] = [] + state.pending_execution_proof_store[new_payload_request_root].append(signed_proof) ``` From a7136eff295656e4e292aab64a4c7afc2e4fe002 Mon Sep 17 00:00:00 2001 From: frisitano Date: Mon, 12 Jan 2026 02:56:28 +0000 Subject: [PATCH 05/28] update proof store --- specs/_features/eip8025/beacon-chain.md | 74 ++++++++++++++----------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 55f9f9e560..6758c064b6 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -18,6 +18,7 @@ - [`NewPayloadRequestHeader`](#newpayloadrequestheader) - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) + - [`ExecutionProofStoreEntry`](#executionproofstoreentry) - [Extended containers](#extended-containers) - [`BeaconState`](#beaconstate) - [Helpers](#helpers) @@ -115,6 +116,14 @@ class SignedExecutionPayloadHeaderEnvelope(Container): signature: BLSSignature ``` +#### `ExecutionProofStoreEntry` + +```python +class ExecutionProofStoreEntry(Container): + header: Optional[NewPayloadRequestHeader] + proofs: List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] +``` + ### Extended containers #### `BeaconState` @@ -123,8 +132,7 @@ class SignedExecutionPayloadHeaderEnvelope(Container): class BeaconState(Container): # ... existing fields ... # [New in EIP-8025] - execution_proof_store: Map[Root, List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] - pending_execution_proof_store: Map[Root, List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD]] + execution_proof_store: Map[Root, ExecutionProofStoreEntry] ``` *Note*: `BeaconBlockBody` remains unchanged. @@ -136,14 +144,27 @@ class BeaconState(Container): #### `verify_execution_proofs` ```python -def verify_execution_proofs(state: BeaconState, new_payload_request_root: Root) -> bool: +def verify_execution_proofs( + state: BeaconState, + new_payload_request_header: NewPayloadRequestHeader, +) -> bool: """ - Verify that execution proofs are available and valid for an execution payload. + Verify that execution proofs are available for an execution payload. + Sets the header in the store entry to mark it as received. """ - proofs = state.execution_proof_store.get(new_payload_request_root, []) + new_payload_request_root = hash_tree_root(new_payload_request_header) + + if new_payload_request_root not in state.execution_proof_store: + state.execution_proof_store[new_payload_request_root] = ExecutionProofStoreEntry( + header=new_payload_request_header, + proofs=[], + ) + else: + # Set header (proofs were pending) + state.execution_proof_store[new_payload_request_root].header = new_payload_request_header # Verify there are sufficient proofs - if len(proofs) < MIN_REQUIRED_EXECUTION_PROOFS: + if len(state.execution_proof_store[new_payload_request_root].proofs) < MIN_REQUIRED_EXECUTION_PROOFS: return False return True @@ -221,19 +242,7 @@ def process_execution_payload( parent_beacon_block_root=state.latest_block_header.parent_root, execution_requests=requests, ) - new_payload_request_root = hash_tree_root(new_payload_request_header) - - # Create entry in execution_proof_store (marks header as received) - state.execution_proof_store[new_payload_request_root] = [] - - # Move pending proofs to execution_proof_store - if new_payload_request_root in state.pending_execution_proof_store: - state.execution_proof_store[new_payload_request_root].extend( - state.pending_execution_proof_store[new_payload_request_root] - ) - del state.pending_execution_proof_store[new_payload_request_root] - - assert verify_execution_proofs(state, new_payload_request_root) + assert verify_execution_proofs(state, new_payload_request_header) else: # Full payload validation via ExecutionEngine assert execution_engine.verify_and_notify_new_payload( @@ -308,19 +317,22 @@ def process_signed_execution_proof( program_bytecode = ProgramBytecode(PROGRAM + proof_message.proof_type.to_bytes(1, "little")) assert verify_execution_proof(proof_message, program_bytecode) - # Store proof based on whether header has been received + # Store proof in execution_proof_store new_payload_request_root = proof_message.public_inputs.new_payload_request_root - if new_payload_request_root in state.execution_proof_store: - # Header already received, store directly - state.execution_proof_store[new_payload_request_root].append(signed_proof) - - # Mark payload as available if sufficient proofs gathered - if len(state.execution_proof_store[new_payload_request_root]) >= MIN_REQUIRED_EXECUTION_PROOFS: - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + if new_payload_request_root not in state.execution_proof_store: + # Header not yet received, create entry with header=None + state.execution_proof_store[new_payload_request_root] = ExecutionProofStoreEntry( + header=None, + proofs=[signed_proof], + ) else: - # Header not yet received, cache in pending store - if new_payload_request_root not in state.pending_execution_proof_store: - state.pending_execution_proof_store[new_payload_request_root] = [] - state.pending_execution_proof_store[new_payload_request_root].append(signed_proof) + # Entry exists, append proof + state.execution_proof_store[new_payload_request_root].proofs.append(signed_proof) + + entry = state.execution_proof_store[new_payload_request_root] + + # Mark payload as available if header received and sufficient proofs gathered + if entry.header is not None and len(entry.proofs) >= MIN_REQUIRED_EXECUTION_PROOFS: + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 ``` From ab94ca9b4c24727b5f28c6c13a8d1b3213f17d33 Mon Sep 17 00:00:00 2001 From: frisitano Date: Mon, 12 Jan 2026 19:22:54 +0000 Subject: [PATCH 06/28] chore: run lints --- specs/_features/eip8025/beacon-chain.md | 5 ++++- specs/_features/eip8025/builder.md | 3 ++- specs/_features/eip8025/fork-choice.md | 3 ++- specs/_features/eip8025/p2p-interface.md | 24 ++++++++++++------------ specs/_features/eip8025/prover.md | 9 ++++++--- specs/_features/eip8025/zkevm.md | 14 +++++--------- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 6758c064b6..8473eaa54a 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -164,7 +164,10 @@ def verify_execution_proofs( state.execution_proof_store[new_payload_request_root].header = new_payload_request_header # Verify there are sufficient proofs - if len(state.execution_proof_store[new_payload_request_root].proofs) < MIN_REQUIRED_EXECUTION_PROOFS: + if ( + len(state.execution_proof_store[new_payload_request_root].proofs) + < MIN_REQUIRED_EXECUTION_PROOFS + ): return False return True diff --git a/specs/_features/eip8025/builder.md b/specs/_features/eip8025/builder.md index 8ff464b691..94ab8f9351 100644 --- a/specs/_features/eip8025/builder.md +++ b/specs/_features/eip8025/builder.md @@ -79,7 +79,8 @@ After producing an `ExecutionPayloadEnvelope` the builder constructs a set of 1. Extract the `NewPayloadRequest` from the envelope. 2. Obtain the `ZKExecutionWitness` from the execution layer. 3. Select a `proof_id` corresponding to the proof system being used. -4. Call `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` +4. Call + `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` to produce the `ExecutionProof`. 5. Set `signed_proof.message` to the generated `ExecutionProof`. 6. Set `signed_proof.prover_id` to the builder's `BuilderIndex`. diff --git a/specs/_features/eip8025/fork-choice.md b/specs/_features/eip8025/fork-choice.md index 2275b8fbf6..d86ef77d94 100644 --- a/specs/_features/eip8025/fork-choice.md +++ b/specs/_features/eip8025/fork-choice.md @@ -13,7 +13,8 @@ ## Introduction This is the modification of the fork choice accompanying the EIP-8025 upgrade, -enabling stateless validation of execution payloads through cryptographic proofs. +enabling stateless validation of execution payloads through cryptographic +proofs. *Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md). diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index f15b869fe2..1cd6d656a8 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -49,12 +49,13 @@ The following validations MUST pass before forwarding the `signed_execution_payload_envelope_header` on the network: - _[IGNORE]_ The header is the first valid header received for the tuple - `(signed_execution_payload_envelope_header.message.beacon_block_root, - signed_execution_payload_envelope_header.message.slot)`. -- _[REJECT]_ The `signed_execution_payload_envelope_header.message.beacon_block_root` - refers to a known beacon block. -- _[REJECT]_ The `signed_execution_payload_envelope_header.message.builder_index` - is within the known builder registry. + `(signed_execution_payload_envelope_header.message.beacon_block_root, signed_execution_payload_envelope_header.message.slot)`. +- _[REJECT]_ The + `signed_execution_payload_envelope_header.message.beacon_block_root` refers to + a known beacon block. +- _[REJECT]_ The + `signed_execution_payload_envelope_header.message.builder_index` is within the + known builder registry. - _[REJECT]_ The `signed_execution_payload_envelope_header.signature` is valid with respect to the builder's public key. - _[REJECT]_ The `signed_execution_payload_envelope_header.message.slot` matches @@ -74,8 +75,8 @@ def compute_subnet_for_execution_proof(proof_id: ProofID) -> SubnetID: return SubnetID(proof_id) ``` -The following validations MUST pass before forwarding a -`signed_execution_proof` on the network: +The following validations MUST pass before forwarding a `signed_execution_proof` +on the network: - _[IGNORE]_ The proof is the first valid proof received for the tuple `(signed_execution_proof.message.public_inputs.new_payload_request_root, subnet_id)`. @@ -85,8 +86,7 @@ The following validations MUST pass before forwarding a is in `WHITELISTED_PROVERS`. - _[REJECT]_ The `signed_execution_proof.signature` is valid with respect to the prover's public key. -- _[REJECT]_ The `signed_execution_proof.message.proof_data` is - non-empty. +- _[REJECT]_ The `signed_execution_proof.message.proof_data` is non-empty. - _[REJECT]_ The proof system ID matches the subnet: `signed_execution_proof.message.proof_type == subnet_id`. - _[REJECT]_ The execution proof is valid as verified by @@ -119,8 +119,8 @@ Response Content: ) ``` -Requests execution proofs for the given `new_payload_request_root`. The -response MUST contain all available proofs for the requested root, up to +Requests execution proofs for the given `new_payload_request_root`. The response +MUST contain all available proofs for the requested root, up to `MAX_EXECUTION_PROOFS_PER_PAYLOAD`. The following validations MUST pass: diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index 7467fdee6f..97fec9f17e 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -29,7 +29,8 @@ All terminology, constants, functions, and protocol mechanics defined in the [EIP-8025 -- Beacon Chain](./beacon-chain.md) and [EIP-8025 -- zkEVM](./zkevm.md) documents are requisite for this document. -The prover MUST have their public key included in `WHITELISTED_PROVERS` or alternatively use a whitelisted community proof relay. +The prover MUST have their public key included in `WHITELISTED_PROVERS` or +alternatively use a whitelisted community proof relay. ## Execution proof signature @@ -52,7 +53,8 @@ To construct a `SignedExecutionProof`: 1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. 2. Obtain the `ZKExecutionWitness` from the execution layer. 3. Select a `proof_id` corresponding to the proof system being used. -4. Call `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` +4. Call + `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` to produce the `ExecutionProof`. 5. Set `signed_proof.message` to the generated `ExecutionProof`. 6. Set `signed_proof.prover_id` to the prover's public key. @@ -81,7 +83,8 @@ The relay exposes an API endpoint that accepts unsigned `ExecutionProof` submissions from community provers. Upon receiving a proof, the relay MUST: 1. Verify the `proof.proof_data` is non-empty. -2. Verify the execution proof is valid using `verify_execution_proof(proof, program_bytecode)`. +2. Verify the execution proof is valid using + `verify_execution_proof(proof, program_bytecode)`. 3. Verify a proof for the same `(new_payload_request_root, proof_type)` has not already been signed and broadcast. diff --git a/specs/_features/eip8025/zkevm.md b/specs/_features/eip8025/zkevm.md index 170557fbc3..0e4d46c55b 100644 --- a/specs/_features/eip8025/zkevm.md +++ b/specs/_features/eip8025/zkevm.md @@ -9,7 +9,7 @@ - [Types](#types) - [Cryptographic types](#cryptographic-types) - [Containers](#containers) - - [`ExecutionProof`](#zkevmproof) + - [`ExecutionProof`](#executionproof) - [`PrivateInput`](#privateinput) - [`PublicInput`](#publicinput) - [Helpers](#helpers) @@ -50,8 +50,8 @@ for a payload with a maximum gas limit of 30M gas. ## Types -| Name | SSZ equivalent | Description | -| ------------ | -------------- | ------------------------------- | +| Name | SSZ equivalent | Description | +| ---------------- | -------------- | ------------------------------- | | `ExecutionProof` | `Container` | Proof of execution of a program | ## Cryptographic types @@ -156,9 +156,7 @@ def generate_execution_proof_impl( """ Generate a zkEVM execution proof using the proving key, private inputs and public inputs. """ - proof_data = hash( - public_inputs.new_payload_request_root + proof_id.to_bytes(1, "little") - ) + proof_data = hash(public_inputs.new_payload_request_root + proof_id.to_bytes(1, "little")) return ExecutionProof( proof_data=ByteList(proof_data), proof_type=proof_id, public_inputs=public_inputs @@ -204,9 +202,7 @@ def generate_execution_proof( """ proving_key, _ = generate_keys(program_bytecode, proof_id) - public_inputs = PublicInput( - new_payload_request_root=hash_tree_root(new_payload_request) - ) + public_inputs = PublicInput(new_payload_request_root=hash_tree_root(new_payload_request)) private_input = PrivateInput( new_payload_request=new_payload_request, execution_witness=execution_witness ) From e9a8f8e00cf1038371c1fd29c57b1030d93af172 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 01:29:06 +0000 Subject: [PATCH 07/28] refactor: introduce proof engine --- specs/_features/eip8025/beacon-chain.md | 478 +++++++++++++++++------ specs/_features/eip8025/builder.md | 50 +-- specs/_features/eip8025/fork-choice.md | 44 ++- specs/_features/eip8025/p2p-interface.md | 114 +++--- specs/_features/eip8025/prover.md | 52 ++- specs/_features/eip8025/zkevm.md | 211 ---------- 6 files changed, 503 insertions(+), 446 deletions(-) delete mode 100644 specs/_features/eip8025/zkevm.md diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 8473eaa54a..7283b09196 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -10,25 +10,36 @@ - [Introduction](#introduction) - [Constants](#constants) - [Execution](#execution) + - [Custom types](#custom-types) - [Domains](#domains) +>>>>>>> 56faef161 (refactor: introduce proof engine) - [Configuration](#configuration) - [Containers](#containers) - [New containers](#new-containers) - - [`SignedExecutionProof`](#signedexecutionproof) + - [`PublicInput`](#publicinput) + - [`ExecutionProof`](#executionproof) + - [`BuilderSignedExecutionProof`](#buildersignedexecutionproof) + - [`ProverSignedExecutionProof`](#proversignedexecutionproof) - [`NewPayloadRequestHeader`](#newpayloadrequestheader) + - [`ProofAttributes`](#proofattributes) - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) - [`ExecutionProofStoreEntry`](#executionproofstoreentry) - [Extended containers](#extended-containers) - [`BeaconState`](#beaconstate) -- [Helpers](#helpers) - - [Execution proof functions](#execution-proof-functions) - - [`verify_execution_proofs`](#verify_execution_proofs) - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Proof engine](#proof-engine) + - [`verify_execution_proof`](#verify_execution_proof) + - [`verify_new_payload_request_header`](#verify_new_payload_request_header) + - [`request_proofs`](#request_proofs) + - [`get_proofs`](#get_proofs) - [Execution payload processing](#execution-payload-processing) - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [New `verify_execution_payload_header_envelope_signature`](#new-verify_execution_payload_header_envelope_signature) + - [New `process_execution_payload_header`](#new-process_execution_payload_header) - [Execution proof handlers](#execution-proof-handlers) - - [New `process_signed_execution_proof`](#new-process_signed_execution_proof) + - [New `process_builder_signed_execution_proof`](#new-process_builder_signed_execution_proof) + - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) @@ -39,48 +50,72 @@ stateless validation of execution payloads through cryptographic proofs. *Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md). -*Note*: This specification assumes the reader is familiar with the -[public zkEVM methods exposed](./zkevm.md). - ## Constants ### Execution -| Name | Value | -| ---------------------------------- | -------------------------------------- | -| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | -| `PROGRAM` | `ProgramBytecode(b"DEFAULT__PROGRAM")` | +| Name | Value | +| ---------------------------------- | ------------------- | +| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | +| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | + +### Custom types + +| Name | SSZ equivalent | Description | +| ------------ | -------------- | ---------------------------------------- | +| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | +| `ProofType` | `uint8` | Type identifier for proof system | ### Domains | Name | Value | | ------------------------ | -------------------------- | -| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0D000000')` | +| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0E000000')` | ## Configuration *Note*: The configuration values are not definitive. -| Name | Value | -| ------------------------------- | ----------- | -| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | +| Name | Value | +| ------------------------------- | ------------- | +| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | +| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | + +## Containers + +### New containers + +#### `PublicInput` ```python -WHITELISTED_PROVERS: List[BLSPubkey] = [ - # List of allowed prover public keys -] +class PublicInput(Container): + new_payload_request_root: Root ``` -## Containers +#### `ExecutionProof` -### New containers +```python +class ExecutionProof(Container): + proof_data: ByteList[MAX_PROOF_SIZE] + proof_type: ProofType + public_inputs: PublicInput +``` + +#### `BuilderSignedExecutionProof` + +```python +class BuilderSignedExecutionProof(Container): + message: ExecutionProof + builder_index: BuilderIndex + signature: BLSSignature +``` -#### `SignedExecutionProof` +#### `ProverSignedExecutionProof` ```python -class SignedExecutionProof(Container): +class ProverSignedExecutionProof(Container): message: ExecutionProof - prover_id: Union[BuilderIndex, BLSPubkey] + prover_pubkey: BLSPubkey signature: BLSSignature ``` @@ -95,6 +130,14 @@ class NewPayloadRequestHeader(object): execution_requests: ExecutionRequests ``` +#### `ProofAttributes` + +```python +@dataclass +class ProofAttributes(object): + proof_types: List[ProofType] +``` + #### `ExecutionPayloadHeaderEnvelope` ```python @@ -130,69 +173,149 @@ class ExecutionProofStoreEntry(Container): ```python class BeaconState(Container): - # ... existing fields ... + genesis_time: uint64 + genesis_validators_root: Root + slot: Slot + fork: Fork + latest_block_header: BeaconBlockHeader + block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] # [New in EIP-8025] - execution_proof_store: Map[Root, ExecutionProofStoreEntry] + prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] + eth1_deposit_index: uint64 + validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] + balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] + randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] + slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] + previous_justified_checkpoint: Checkpoint + current_justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] + current_sync_committee: SyncCommittee + next_sync_committee: SyncCommittee + # [Modified in Gloas:EIP7732] + # Removed `latest_execution_payload_header` + # [New in Gloas:EIP7732] + latest_execution_payload_bid: ExecutionPayloadBid + next_withdrawal_index: WithdrawalIndex + next_withdrawal_validator_index: ValidatorIndex + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] + deposit_requests_start_index: uint64 + deposit_balance_to_consume: Gwei + exit_balance_to_consume: Gwei + earliest_exit_epoch: Epoch + consolidation_balance_to_consume: Gwei + earliest_consolidation_epoch: Epoch + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] + proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] + # [New in Gloas:EIP7732] + builders: List[Builder, BUILDER_REGISTRY_LIMIT] + # [New in Gloas:EIP7732] + next_withdrawal_builder_index: BuilderIndex + # [New in Gloas:EIP7732] + execution_payload_availability: Bitvector[SLOTS_PER_HISTORICAL_ROOT] + # [New in Gloas:EIP7732] + builder_pending_payments: Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH] + # [New in Gloas:EIP7732] + builder_pending_withdrawals: List[BuilderPendingWithdrawal, BUILDER_PENDING_WITHDRAWALS_LIMIT] + # [New in Gloas:EIP7732] + latest_block_hash: Hash32 + # [New in Gloas:EIP7732] + payload_expected_withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] ``` *Note*: `BeaconBlockBody` remains unchanged. -## Helpers +## Beacon chain state transition function + +### Proof engine + +The implementation-dependent `ProofEngine` protocol encapsulates the proof +sub-system logic via: + +- proof generation and verification functions -### Execution proof functions +The body of these functions are implementation dependent. -#### `verify_execution_proofs` +#### `verify_execution_proof` ```python -def verify_execution_proofs( - state: BeaconState, - new_payload_request_header: NewPayloadRequestHeader, +def verify_execution_proof( + self: ProofEngine, + execution_proof: ExecutionProof, ) -> bool: """ - Verify that execution proofs are available for an execution payload. - Sets the header in the store entry to mark it as received. + Submit and verify an execution proof. + Return ``True`` if proof is valid. """ - new_payload_request_root = hash_tree_root(new_payload_request_header) + ... +``` - if new_payload_request_root not in state.execution_proof_store: - state.execution_proof_store[new_payload_request_root] = ExecutionProofStoreEntry( - header=new_payload_request_header, - proofs=[], - ) - else: - # Set header (proofs were pending) - state.execution_proof_store[new_payload_request_root].header = new_payload_request_header +#### `verify_new_payload_request_header` + +```python +def verify_new_payload_request_header( + self: ProofEngine, + new_payload_request_header: NewPayloadRequestHeader, +) -> bool: + """ + Verify that sufficient valid proofs exist for the given payload request header. + Return ``True`` if proof requirements are satisfied. + """ + ... +``` - # Verify there are sufficient proofs - if ( - len(state.execution_proof_store[new_payload_request_root].proofs) - < MIN_REQUIRED_EXECUTION_PROOFS - ): - return False +#### `request_proofs` - return True +```python +def request_proofs( + self: ProofEngine, + new_payload_request: NewPayloadRequest, + proof_attributes: ProofAttributes, +) -> ProofGenId: + """ + Request proof generation for a payload with specified proof types. + Returns a ``ProofGenId`` to track the generation request. + """ + ... ``` -## Beacon chain state transition function +#### `get_proofs` + +```python +def get_proofs( + self: ProofEngine, + proof_gen_id: ProofGenId, +) -> List[ExecutionProof]: + """ + Retrieve all generated proofs for a generation request. + """ + ... +``` ### Execution payload processing #### Modified `process_execution_payload` +*Note*: `process_execution_payload` is modified in EIP-8025 to require both +`ExecutionEngine` and `ProofEngine` for validation. + ```python def process_execution_payload( state: BeaconState, - # [Modified in EIP-8025] - # Accept either full envelope or header-only envelope - signed_envelope: Union[SignedExecutionPayloadEnvelope, SignedExecutionPayloadHeaderEnvelope], + signed_envelope: SignedExecutionPayloadEnvelope, execution_engine: ExecutionEngine, + proof_engine: ProofEngine, verify: bool = True, ) -> None: - """ - Process an execution payload envelope or header. - When a header is provided, validation uses execution proofs. - """ - envelope = signed_envelope.message payload = envelope.payload @@ -236,26 +359,152 @@ def process_execution_payload( kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments ] requests = envelope.execution_requests + new_payload_request = NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + assert execution_engine.verify_and_notify_new_payload(new_payload_request) + + # [New in EIP-8025] Verify via ProofEngine + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + blob_gas_used=payload.blob_gas_used, + excess_blob_gas=payload.excess_blob_gas, + ), + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + assert proof_engine.verify_new_payload_request_header(new_payload_request_header) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(requests.deposits, process_deposit_request) + for_ops(requests.withdrawals, process_withdrawal_request) + for_ops(requests.consolidations, process_consolidation_request) + + # Queue the builder payment + payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] + amount = payment.withdrawal.amount + if amount > 0: + state.builder_pending_withdrawals.append(payment.withdrawal) + state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( + BuilderPendingPayment() + ) + + # Cache the execution payload hash + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + state.latest_block_hash = payload.block_hash - if isinstance(signed_envelope, SignedExecutionPayloadHeaderEnvelope): - # Header-only validation using execution proofs - new_payload_request_header = NewPayloadRequestHeader( - execution_payload_header=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - assert verify_execution_proofs(state, new_payload_request_header) + # Verify the state root + if verify: + assert envelope.state_root == hash_tree_root(state) +``` + +#### New `verify_execution_payload_header_envelope_signature` + +```python +def verify_execution_payload_header_envelope_signature( + state: BeaconState, signed_envelope: SignedExecutionPayloadHeaderEnvelope +) -> bool: + builder_index = signed_envelope.message.builder_index + if builder_index == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey else: - # Full payload validation via ExecutionEngine - assert execution_engine.verify_and_notify_new_payload( - NewPayloadRequest( - execution_payload=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - ) + pubkey = state.builders[builder_index].pubkey + + signing_root = compute_signing_root( + signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER) + ) + return bls.Verify(pubkey, signing_root, signed_envelope.signature) +``` + +#### New `process_execution_payload_header` + +*Note*: `process_execution_payload_header` is the stateless equivalent of +`process_execution_payload`. It processes execution payload headers using +execution proofs for validation instead of the `ExecutionEngine`. + +```python +def process_execution_payload_header( + state: BeaconState, + signed_envelope: SignedExecutionPayloadHeaderEnvelope, + proof_engine: ProofEngine, + verify: bool = True, +) -> None: + envelope = signed_envelope.message + payload = envelope.payload + + # Verify signature + if verify: + assert verify_execution_payload_header_envelope_signature(state, signed_envelope) + + # Cache latest block header state root + previous_state_root = hash_tree_root(state) + if state.latest_block_header.state_root == Root(): + state.latest_block_header.state_root = previous_state_root + + # Verify consistency with the beacon block + assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) + assert envelope.slot == state.slot + + # Verify consistency with the committed bid + committed_bid = state.latest_execution_payload_bid + assert envelope.builder_index == committed_bid.builder_index + assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) + assert committed_bid.prev_randao == payload.prev_randao + + # Verify consistency with expected withdrawals + assert payload.withdrawals_root == hash_tree_root(state.payload_expected_withdrawals) + + # Verify the gas_limit + assert committed_bid.gas_limit == payload.gas_limit + # Verify the block hash + assert committed_bid.block_hash == payload.block_hash + # Verify consistency of the parent hash with respect to the previous execution payload + assert payload.parent_hash == state.latest_block_hash + # Verify timestamp + assert payload.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(envelope.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + + # [New in EIP-8025] Verify the execution payload request header using execution proofs + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments + ] + requests = envelope.execution_requests + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + + # Verify sufficient proofs exist via ProofEngine + assert proof_engine.verify_new_payload_request_header(new_payload_request_header) def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: for operation in operations: @@ -285,57 +534,60 @@ def process_execution_payload( ### Execution proof handlers -#### New `process_signed_execution_proof` +*Note*: Proof storage and availability is implementation-dependent, managed by +the `ProofEngine`. Implementations may cache proofs that arrive before their +corresponding payload headers and apply them once the header is received. + +#### New `process_builder_signed_execution_proof` ```python -def process_signed_execution_proof( +def process_builder_signed_execution_proof( state: BeaconState, - signed_proof: SignedExecutionProof, + signed_proof: BuilderSignedExecutionProof, + proof_engine: ProofEngine, ) -> None: """ - Handler for SignedExecutionProof. - Supports both builders (via BuilderIndex) and provers (via BLSPubkey). + Handler for BuilderSignedExecutionProof. """ proof_message = signed_proof.message - prover_id = signed_proof.prover_id + builder_index = signed_proof.builder_index - # Determine pubkey based on prover_id type - if isinstance(prover_id, BLSPubkey): - # Prover path - verify whitelist - assert prover_id in WHITELISTED_PROVERS - pubkey = prover_id + # Determine pubkey based on builder_index + if builder_index == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey else: - # Builder path - if prover_id == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey - else: - pubkey = state.builders[prover_id].pubkey + pubkey = state.builders[builder_index].pubkey domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) signing_root = compute_signing_root(proof_message, domain) assert bls.Verify(pubkey, signing_root, signed_proof.signature) # Verify the execution proof - program_bytecode = ProgramBytecode(PROGRAM + proof_message.proof_type.to_bytes(1, "little")) - assert verify_execution_proof(proof_message, program_bytecode) - - # Store proof in execution_proof_store - new_payload_request_root = proof_message.public_inputs.new_payload_request_root - - if new_payload_request_root not in state.execution_proof_store: - # Header not yet received, create entry with header=None - state.execution_proof_store[new_payload_request_root] = ExecutionProofStoreEntry( - header=None, - proofs=[signed_proof], - ) - else: - # Entry exists, append proof - state.execution_proof_store[new_payload_request_root].proofs.append(signed_proof) + assert proof_engine.verify_execution_proof(proof_message) +``` - entry = state.execution_proof_store[new_payload_request_root] +#### New `process_prover_signed_execution_proof` - # Mark payload as available if header received and sufficient proofs gathered - if entry.header is not None and len(entry.proofs) >= MIN_REQUIRED_EXECUTION_PROOFS: - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 +```python +def process_prover_signed_execution_proof( + state: BeaconState, + signed_proof: ProverSignedExecutionProof, + proof_engine: ProofEngine, +) -> None: + """ + Handler for ProverSignedExecutionProof. + """ + proof_message = signed_proof.message + prover_pubkey = signed_proof.prover_pubkey + + # Verify prover is whitelisted + assert prover_pubkey in state.prover_whitelist + + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof_message, domain) + assert bls.Verify(prover_pubkey, signing_root, signed_proof.signature) + + # Verify the execution proof + assert proof_engine.verify_execution_proof(proof_message) ``` diff --git a/specs/_features/eip8025/builder.md b/specs/_features/eip8025/builder.md index 94ab8f9351..dbabcb3fe5 100644 --- a/specs/_features/eip8025/builder.md +++ b/specs/_features/eip8025/builder.md @@ -9,10 +9,9 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) - [Prerequisites](#prerequisites) -- [Configuration](#configuration) - [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) - [Execution proof signature](#execution-proof-signature) -- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) +- [Constructing the `BuilderSignedExecutionProof`](#constructing-the-buildersignedexecutionproof) @@ -28,14 +27,8 @@ definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 -- Beacon Chain](./beacon-chain.md) and -[EIP-8025 -- zkEVM](./zkevm.md) documents are requisite for this document. - -## Configuration - -| Name | Value | -| ------------------------------------ | ------- | -| `EXECUTION_PROOF_GENERATION_ENABLED` | `False` | +[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this +document. ## Constructing the `SignedExecutionPayloadHeaderEnvelope` @@ -58,7 +51,7 @@ To construct the `SignedExecutionPayloadHeaderEnvelope` from an existing 4. Set `signed_header_envelope.signature = signed_envelope.signature`. Then the builder broadcasts `signed_header_envelope` on the -`signed_execution_payload_envelope_header` global gossip topic. +`signed_execution_payload_header_envelope` global gossip topic. ## Execution proof signature @@ -71,27 +64,22 @@ def get_execution_proof_signature( return bls.Sign(privkey, signing_root) ``` -## Constructing the `SignedExecutionProof` +## Constructing the `BuilderSignedExecutionProof` After producing an `ExecutionPayloadEnvelope` the builder constructs a set of -`SignedExecutionProof` as follows: +`BuilderSignedExecutionProof` as follows: 1. Extract the `NewPayloadRequest` from the envelope. -2. Obtain the `ZKExecutionWitness` from the execution layer. -3. Select a `proof_id` corresponding to the proof system being used. -4. Call - `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` - to produce the `ExecutionProof`. -5. Set `signed_proof.message` to the generated `ExecutionProof`. -6. Set `signed_proof.prover_id` to the builder's `BuilderIndex`. -7. Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. - -Then the builder assembles -`signed_execution_proof = SignedExecutionProof(message=proof, prover_id=builder_index, signature=signature)` -and broadcasts it on the `execution_proof_{subnet_id}` gossip topic, where -`subnet_id = proof.proof_type`. - -*Note*: The `proof_id` determines which subnet the proof is broadcast on. Each -proof system has a dedicated subnet to allow validators to subscribe to proofs -from specific proof systems. +2. Select proof types and create `ProofAttributes`. +3. Call + `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` + to initiate proof generation. +4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated + proofs. +5. For each `ExecutionProof` in `proofs`: + - Set `signed_proof.message` to the `ExecutionProof`. + - Set `signed_proof.builder_index` to the builder's `BuilderIndex`. + - Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + - Broadcast the `BuilderSignedExecutionProof` on the `execution_proof` gossip + topic. diff --git a/specs/_features/eip8025/fork-choice.md b/specs/_features/eip8025/fork-choice.md index d86ef77d94..f5caafda27 100644 --- a/specs/_features/eip8025/fork-choice.md +++ b/specs/_features/eip8025/fork-choice.md @@ -7,6 +7,7 @@ - [Introduction](#introduction) - [Handlers](#handlers) - [Modified `on_execution_payload`](#modified-on_execution_payload) + - [New `on_execution_payload_header`](#new-on_execution_payload_header) @@ -22,18 +23,41 @@ proofs. ### Modified `on_execution_payload` -The handler `on_execution_payload` is modified to accept either a full -`SignedExecutionPayloadEnvelope` or a `SignedExecutionPayloadHeaderEnvelope`. +*Note*: `on_execution_payload` is modified in EIP-8025 to include `PROOF_ENGINE` +in the call to `process_execution_payload`. ```python -def on_execution_payload( - store: Store, - # [Modified in EIP-8025] - # Accept either full envelope or header-only envelope - signed_envelope: SignedExecutionPayloadEnvelope | SignedExecutionPayloadHeaderEnvelope, +def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None: + """ + Run ``on_execution_payload`` upon receiving a new execution payload. + """ + envelope = signed_envelope.message + # The corresponding beacon block root needs to be known + assert envelope.beacon_block_root in store.block_states + + # Check if blob data is available + # If not, this payload MAY be queued and subsequently considered when blob data becomes available + assert is_data_available(envelope.beacon_block_root) + + # Make a copy of the state to avoid mutability issues + state = copy(store.block_states[envelope.beacon_block_root]) + + # Process the execution payload + # [Modified in EIP-8025] Added PROOF_ENGINE parameter + process_execution_payload(state, signed_envelope, EXECUTION_ENGINE, PROOF_ENGINE) + + # Add new state for this payload to the store + store.execution_payload_states[envelope.beacon_block_root] = state +``` + +### New `on_execution_payload_header` + +```python +def on_execution_payload_header( + store: Store, signed_envelope: SignedExecutionPayloadHeaderEnvelope ) -> None: """ - Run ``on_execution_payload`` upon receiving a new execution payload or header. + Run ``on_execution_payload_header`` upon receiving a new execution payload header. """ envelope = signed_envelope.message # The corresponding beacon block root needs to be known @@ -46,8 +70,8 @@ def on_execution_payload( # Make a copy of the state to avoid mutability issues state = copy(store.block_states[envelope.beacon_block_root]) - # Process the execution payload (handles both full envelope and header) - process_execution_payload(state, signed_envelope, EXECUTION_ENGINE) + # Process the execution payload header + process_execution_payload_header(state, signed_envelope, PROOF_ENGINE) # Add new state for this payload to the store store.execution_payload_states[envelope.beacon_block_root] = state diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 1cd6d656a8..0247a1d5e0 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -9,29 +9,28 @@ This document contains the networking specifications for EIP-8025. - [Table of contents](#table-of-contents) -- [Constants](#constants) - [Containers](#containers) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) - - [`signed_execution_payload_envelope_header`](#signed_execution_payload_envelope_header) - - [`execution_proof_{subnet_id}`](#execution_proof_subnet_id) + - [`signed_execution_payload_header_envelope`](#signed_execution_payload_header_envelope) + - [`execution_proof`](#execution_proof) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) - [ExecutionProofsByRoot](#executionproofsbyroot) -## Constants +## Containers -*Note*: There are `MAX_EXECUTION_PROOFS_PER_PAYLOAD` (from -[beacon-chain.md](./beacon-chain.md)) execution proof subnets to provide 1-to-1 -mapping with proof systems. Each proof system gets its own dedicated subnet. +*Note*: Execution proofs are broadcast as either `BuilderSignedExecutionProof` +or `ProverSignedExecutionProof` containers on the same topic. -## Containers + -*Note*: Execution proofs are broadcast as `SignedExecutionProof` containers. No -additional message wrapper is needed. +```python +SignedExecutionProof = Union[BuilderSignedExecutionProof, ProverSignedExecutionProof] +``` ## The gossip domain: gossipsub @@ -39,58 +38,67 @@ additional message wrapper is needed. #### Global topics -##### `signed_execution_payload_envelope_header` +##### `signed_execution_payload_header_envelope` This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. -ZK attesters subscribe to this topic to receive execution payload headers for -which they can generate execution proofs. +Provers subscribe to this topic to receive execution payload headers for which +they can generate execution proofs. The following validations MUST pass before forwarding the -`signed_execution_payload_envelope_header` on the network: - -- _[IGNORE]_ The header is the first valid header received for the tuple - `(signed_execution_payload_envelope_header.message.beacon_block_root, signed_execution_payload_envelope_header.message.slot)`. -- _[REJECT]_ The - `signed_execution_payload_envelope_header.message.beacon_block_root` refers to - a known beacon block. -- _[REJECT]_ The - `signed_execution_payload_envelope_header.message.builder_index` is within the - known builder registry. -- _[REJECT]_ The `signed_execution_payload_envelope_header.signature` is valid - with respect to the builder's public key. -- _[REJECT]_ The `signed_execution_payload_envelope_header.message.slot` matches - the slot of the referenced beacon block. +`signed_execution_payload_header_envelope` on the network, assuming the alias +`envelope = signed_execution_payload_header_envelope.message`, +`payload = envelope.payload`: + +- _[IGNORE]_ The envelope's block root `envelope.beacon_block_root` has been + seen (via gossip or non-gossip sources) (a client MAY queue headers for + processing once the block is retrieved). +- _[IGNORE]_ The node has not seen another valid + `SignedExecutionPayloadHeaderEnvelope` for this block root from this builder. +- _[IGNORE]_ The envelope is from a slot greater than or equal to the latest + finalized slot -- i.e. validate that + `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)` + +Let `block` be the block with `envelope.beacon_block_root`. Let `bid` alias +`block.body.signed_execution_payload_bid.message`. + +- _[REJECT]_ `block` passes validation. +- _[REJECT]_ `block.slot` equals `envelope.slot`. +- _[REJECT]_ `envelope.builder_index == bid.builder_index` +- _[REJECT]_ `payload.block_hash == bid.block_hash` +- _[REJECT]_ `signed_execution_payload_header_envelope.signature` is valid with + respect to the builder's public key. + +##### `execution_proof` + +This topic is used to propagate execution proofs. Both +`BuilderSignedExecutionProof` and `ProverSignedExecutionProof` messages are +propagated on this topic. + +The following validations MUST pass before forwarding a proof on the network: + +- _[IGNORE]_ The proof's corresponding new payload request (identified by + `proof.message.public_inputs.new_payload_request_root`) has been seen (via + gossip or non-gossip sources) (a client MAY queue proofs for processing once + the new payload request is retrieved). +- _[IGNORE]_ The proof is the first valid proof received for the tuple + `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, signer)`. -##### `execution_proof_{subnet_id}` +For `BuilderSignedExecutionProof`: -Execution proof subnets are used to propagate execution proofs for specific -proof systems. `SignedExecutionProof` messages from both builders and provers -are propagated on these subnets. +- _[REJECT]_ The `builder_index` is within the known builder registry. +- _[REJECT]_ The signature is valid with respect to the builder's public key. -The execution proof subnet for a given `proof_id` is: +For `ProverSignedExecutionProof`: -```python -def compute_subnet_for_execution_proof(proof_id: ProofID) -> SubnetID: - assert proof_id < MAX_EXECUTION_PROOFS_PER_PAYLOAD - return SubnetID(proof_id) -``` +- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. +- _[REJECT]_ The signature is valid with respect to the prover's public key. -The following validations MUST pass before forwarding a `signed_execution_proof` -on the network: +For both types: -- _[IGNORE]_ The proof is the first valid proof received for the tuple - `(signed_execution_proof.message.public_inputs.new_payload_request_root, subnet_id)`. -- _[REJECT]_ If `signed_execution_proof.prover_id` is a `BuilderIndex`: the - index is within the known builder registry. -- _[REJECT]_ If `signed_execution_proof.prover_id` is a `BLSPubkey`: the pubkey - is in `WHITELISTED_PROVERS`. -- _[REJECT]_ The `signed_execution_proof.signature` is valid with respect to the - prover's public key. -- _[REJECT]_ The `signed_execution_proof.message.proof_data` is non-empty. -- _[REJECT]_ The proof system ID matches the subnet: - `signed_execution_proof.message.proof_type == subnet_id`. -- _[REJECT]_ The execution proof is valid as verified by - `process_signed_execution_proof()`. +- _[REJECT]_ The `proof.message.proof_data` is non-empty. +- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. +- _[REJECT]_ The execution proof is valid as verified by the appropriate + handler. ## The Req/Resp domain @@ -107,7 +115,7 @@ Request Content: ``` ( - Root # new_payload_request_root + Root ) ``` diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index 97fec9f17e..82307593f6 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -10,7 +10,7 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) - [Execution proof signature](#execution-proof-signature) -- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) +- [Constructing the `ProverSignedExecutionProof`](#constructing-the-proversignedexecutionproof) - [Honest Prover Relay](#honest-prover-relay) - [Accepting proofs](#accepting-proofs) - [Signing and broadcasting](#signing-and-broadcasting) @@ -26,8 +26,8 @@ proof phase. ## Prerequisites All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 -- Beacon Chain](./beacon-chain.md) and -[EIP-8025 -- zkEVM](./zkevm.md) documents are requisite for this document. +[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this +document. The prover MUST have their public key included in `WHITELISTED_PROVERS` or alternatively use a whitelisted community proof relay. @@ -43,32 +43,28 @@ def get_execution_proof_signature( return bls.Sign(privkey, signing_root) ``` -## Constructing the `SignedExecutionProof` +## Constructing the `ProverSignedExecutionProof` -Provers subscribe to the `signed_execution_payload_envelope` gossip topic to -receive execution payloads for which they can generate execution proofs. +Provers subscribe to the `signed_execution_payload_envelope` gossip topic +(defined in [Gloas](../../gloas/p2p-interface.md)) to receive execution payloads +for which they can generate execution proofs. -To construct a `SignedExecutionProof`: +To construct a `ProverSignedExecutionProof`: 1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. -2. Obtain the `ZKExecutionWitness` from the execution layer. -3. Select a `proof_id` corresponding to the proof system being used. -4. Call - `generate_execution_proof(new_payload_request, execution_witness, PROGRAM, proof_id)` - to produce the `ExecutionProof`. -5. Set `signed_proof.message` to the generated `ExecutionProof`. -6. Set `signed_proof.prover_id` to the prover's public key. -7. Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. - -Then the prover assembles -`signed_execution_proof = SignedExecutionProof(message=proof, prover_id=pubkey, signature=signature)` -and broadcasts it on the `execution_proof_{subnet_id}` gossip topic, where -`subnet_id = proof.proof_type`. - -*Note*: The `proof_id` determines which subnet the proof is broadcast on. Each -proof system has a dedicated subnet to allow validators to subscribe to proofs -from specific proof systems. +2. Select proof types and create `ProofAttributes`. +3. Call + `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` + to initiate proof generation. +4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated + proofs. +5. For each `ExecutionProof` in `proofs`: + - Set `signed_proof.message` to the `ExecutionProof`. + - Set `signed_proof.prover_pubkey` to the prover's public key. + - Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip + topic. ## Honest Prover Relay @@ -95,11 +91,11 @@ If any validation fails, the relay SHOULD reject the submission. After successful validation, the relay signs and broadcasts the proof: 1. Set `signed_proof.message` to the validated `proof`. -2. Set `signed_proof.prover_id` to the relay's whitelisted public key. +2. Set `signed_proof.prover_pubkey` to the relay's whitelisted public key. 3. Set `signed_proof.signature` to the result of `get_execution_proof_signature(state, proof, relay_privkey)`. -4. Broadcast the `SignedExecutionProof` on the `execution_proof_{subnet_id}` - gossip topic, where `subnet_id = proof.proof_type`. +4. Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip + topic. *Note*: The relay's public key MUST be included in `WHITELISTED_PROVERS`. The relay takes responsibility for the validity of proofs it signs. diff --git a/specs/_features/eip8025/zkevm.md b/specs/_features/eip8025/zkevm.md deleted file mode 100644 index 0e4d46c55b..0000000000 --- a/specs/_features/eip8025/zkevm.md +++ /dev/null @@ -1,211 +0,0 @@ -# EIP-8025 -- zkEVM - -*Note*: This document is a work-in-progress for researchers and implementers. - - - -- [Introduction](#introduction) -- [Constants](#constants) -- [Types](#types) -- [Cryptographic types](#cryptographic-types) -- [Containers](#containers) - - [`ExecutionProof`](#executionproof) - - [`PrivateInput`](#privateinput) - - [`PublicInput`](#publicinput) -- [Helpers](#helpers) - - [Preprocessing](#preprocessing) - - [`generate_keys`](#generate_keys) - - [Proof verification](#proof-verification) - - [`verify_execution_proof_impl`](#verify_execution_proof_impl) - - [`generate_verification_key`](#generate_verification_key) - - [Proof generation](#proof-generation) - - [`generate_execution_proof_impl`](#generate_execution_proof_impl) - - [`generate_proving_key`](#generate_proving_key) - - [`verify_execution_proof`](#verify_execution_proof) - - [`generate_execution_proof`](#generate_execution_proof) - - - -## Introduction - -This document specifies the cryptographic operations for zkEVM based execution -proofs enabling stateless validation of execution payloads. - -*Note*: This specification provides placeholder implementations. Production -implementations should use established zkEVM systems. - -## Constants - -All of the constants below are subject to change and one should not overindex on -them. `MAX_PROOF_SIZE`, `MAX_PROVING_KEY_SIZE`, and `MAX_VERIFICATION_KEY_SIZE` -are all arbitrary. `MAX_WITNESS_SIZE` is the worst case witness size for the MPT -for a payload with a maximum gas limit of 30M gas. - -| Name | Value | -| --------------------------- | ---------------------- | -| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | -| `MAX_PROVING_KEY_SIZE` | `2**28` (= 256MiB) | -| `MAX_VERIFICATION_KEY_SIZE` | `2**20` (= 1MiB) | -| `MAX_WITNESS_SIZE` | `314572800` (= 300MiB) | - -## Types - -| Name | SSZ equivalent | Description | -| ---------------- | -------------- | ------------------------------- | -| `ExecutionProof` | `Container` | Proof of execution of a program | - -## Cryptographic types - -*Note*: `ProgramBytecode` represents the bytecode for a particular execution -layer client. The size depends on the client; `16` is a placeholder. - -| Name | SSZ equivalent | Description | -| -------------------- | ------------------------------------- | ------------------------------------------------------------- | -| `ProgramBytecode` | `ByteList[16]` | Execution-layer program bytecode | -| `ProofID` | `uint8` | Identifier for proof system | -| `ProvingKey` | `ByteList[MAX_PROVING_KEY_SIZE]` | Key used for proof generation | -| `VerificationKey` | `ByteList[MAX_VERIFICATION_KEY_SIZE]` | Key used for proof verification | -| `ZKExecutionWitness` | `ByteList[MAX_WITNESS_SIZE]` | zkEVM execution witness data for stateless program execution | -| `PrivateInput` | `Container` | Private inputs for execution proof generation | -| `PublicInput` | `Container` | Public inputs for execution proof generation and verification | - -## Containers - -### `ExecutionProof` - -```python -class ExecutionProof(Container): - proof_data: ByteList[MAX_PROOF_SIZE] - proof_type: ProofID - public_inputs: PublicInput -``` - -### `PrivateInput` - -```python -class PrivateInput(Container): - new_payload_request: NewPayloadRequest - execution_witness: ZKExecutionWitness -``` - -### `PublicInput` - -```python -class PublicInput(Container): - new_payload_request_root: Root # hash_tree_root(NewPayloadRequest) -``` - -## Helpers - -### Preprocessing - -#### `generate_keys` - -```python -def generate_keys( - program_bytecode: ProgramBytecode, proof_id: ProofID -) -> tuple[ProvingKey, VerificationKey]: - """ - Generate proving and verification keys for the given program bytecode and proof system. - """ - proving_key = generate_proving_key(program_bytecode, proof_id) - verification_key = generate_verification_key(program_bytecode, proof_id) - - return (proving_key, verification_key) -``` - -### Proof verification - -#### `verify_execution_proof_impl` - -```python -def verify_execution_proof_impl(proof: ExecutionProof, verification_key: VerificationKey) -> bool: - """ - Verify a zkEVM execution proof using the verification key. - """ - if len(proof.proof_data) > MAX_PROOF_SIZE: - return False - - return True -``` - -#### `generate_verification_key` - -```python -def generate_verification_key( - program_bytecode: ProgramBytecode, proof_id: ProofID -) -> VerificationKey: - """ - Generate a verification key for the given program bytecode and proof system. - """ - verification_key = VerificationKey(program_bytecode + proof_id.to_bytes(1, "little")) - return verification_key -``` - -### Proof generation - -#### `generate_execution_proof_impl` - -```python -def generate_execution_proof_impl( - private_input: PrivateInput, - proving_key: ProvingKey, - proof_id: ProofID, - public_inputs: PublicInput, -) -> ExecutionProof: - """ - Generate a zkEVM execution proof using the proving key, private inputs and public inputs. - """ - proof_data = hash(public_inputs.new_payload_request_root + proof_id.to_bytes(1, "little")) - - return ExecutionProof( - proof_data=ByteList(proof_data), proof_type=proof_id, public_inputs=public_inputs - ) -``` - -#### `generate_proving_key` - -```python -def generate_proving_key(program_bytecode: ProgramBytecode, proof_id: ProofID) -> ProvingKey: - """ - Generate a proving key for the given program bytecode and proof system. - """ - return ProvingKey(program_bytecode + proof_id.to_bytes(1, "little")) -``` - -### `verify_execution_proof` - -```python -def verify_execution_proof( - proof: ExecutionProof, - program_bytecode: ProgramBytecode, -) -> bool: - """ - Public method to verify an execution proof against NewPayloadRequest root. - """ - _, verification_key = generate_keys(program_bytecode, proof.proof_type) - - return verify_execution_proof_impl(proof, verification_key) -``` - -### `generate_execution_proof` - -```python -def generate_execution_proof( - new_payload_request: NewPayloadRequest, - execution_witness: ZKExecutionWitness, - program_bytecode: ProgramBytecode, - proof_id: ProofID, -) -> ExecutionProof: - """ - Public method to generate an execution proof for a NewPayloadRequest. - """ - proving_key, _ = generate_keys(program_bytecode, proof_id) - - public_inputs = PublicInput(new_payload_request_root=hash_tree_root(new_payload_request)) - private_input = PrivateInput( - new_payload_request=new_payload_request, execution_witness=execution_witness - ) - - return generate_execution_proof_impl(private_input, proving_key, proof_id, public_inputs) -``` From 27514cb46304565356e08ee7ac0cd394950f6cbf Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 18:52:36 +0000 Subject: [PATCH 08/28] split specs by fork base --- specs/_features/eip8025_fulu/beacon-chain.md | 227 +++++++++ specs/_features/eip8025_fulu/fork-choice.md | 80 ++++ specs/_features/eip8025_fulu/p2p-interface.md | 90 ++++ specs/_features/eip8025_fulu/proof-engine.md | 155 ++++++ specs/_features/eip8025_fulu/prover.md | 78 +++ specs/_features/eip8025_fulu/validator.md | 30 ++ specs/_features/eip8025_gloas/beacon-chain.md | 452 ++++++++++++++++++ specs/_features/eip8025_gloas/builder.md | 89 ++++ specs/_features/eip8025_gloas/fork-choice.md | 80 ++++ .../_features/eip8025_gloas/p2p-interface.md | 144 ++++++ specs/_features/eip8025_gloas/prover.md | 104 ++++ specs/_features/eip8025_gloas/validator.md | 31 ++ 12 files changed, 1560 insertions(+) create mode 100644 specs/_features/eip8025_fulu/beacon-chain.md create mode 100644 specs/_features/eip8025_fulu/fork-choice.md create mode 100644 specs/_features/eip8025_fulu/p2p-interface.md create mode 100644 specs/_features/eip8025_fulu/proof-engine.md create mode 100644 specs/_features/eip8025_fulu/prover.md create mode 100644 specs/_features/eip8025_fulu/validator.md create mode 100644 specs/_features/eip8025_gloas/beacon-chain.md create mode 100644 specs/_features/eip8025_gloas/builder.md create mode 100644 specs/_features/eip8025_gloas/fork-choice.md create mode 100644 specs/_features/eip8025_gloas/p2p-interface.md create mode 100644 specs/_features/eip8025_gloas/prover.md create mode 100644 specs/_features/eip8025_gloas/validator.md diff --git a/specs/_features/eip8025_fulu/beacon-chain.md b/specs/_features/eip8025_fulu/beacon-chain.md new file mode 100644 index 0000000000..e8d88e1162 --- /dev/null +++ b/specs/_features/eip8025_fulu/beacon-chain.md @@ -0,0 +1,227 @@ +# EIP-8025 (Fulu) -- The Beacon Chain + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Containers](#containers) + - [New containers](#new-containers) + - [`ProverSignedExecutionProof`](#proversignedexecutionproof) + - [`NewPayloadRequestHeader`](#newpayloadrequestheader) + - [Extended Containers](#extended-containers) + - [`BeaconState`](#beaconstate) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Execution payload processing](#execution-payload-processing) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Execution proof handlers](#execution-proof-handlers) + - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) + + + +## Introduction + +This document contains the Fulu-specific consensus specs for EIP-8025. This +enables stateless validation of execution payloads through cryptographic proofs. + +*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and +imports proof types from [proof-engine.md](./proof-engine.md). + +## Containers + +### New containers + +#### `ProverSignedExecutionProof` + +```python +class ProverSignedExecutionProof(Container): + message: ExecutionProof + prover_pubkey: BLSPubkey + signature: BLSSignature +``` + +#### `NewPayloadRequestHeader` + +```python +@dataclass +class NewPayloadRequestHeader(object): + execution_payload_header: ExecutionPayloadHeader + versioned_hashes: Sequence[VersionedHash] + parent_beacon_block_root: Root + execution_requests: ExecutionRequests +``` + +### Extended Containers + +#### `BeaconState` + +```python +class BeaconState(Container): + genesis_time: uint64 + genesis_validators_root: Root + slot: Slot + fork: Fork + latest_block_header: BeaconBlockHeader + block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + # [New in EIP-8025] + prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] + eth1_deposit_index: uint64 + validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] + balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] + randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] + slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] + previous_justified_checkpoint: Checkpoint + current_justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] + current_sync_committee: SyncCommittee + next_sync_committee: SyncCommittee + latest_execution_payload_header: ExecutionPayloadHeader + next_withdrawal_index: WithdrawalIndex + next_withdrawal_validator_index: ValidatorIndex + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] + deposit_requests_start_index: uint64 + deposit_balance_to_consume: Gwei + exit_balance_to_consume: Gwei + earliest_exit_epoch: Epoch + consolidation_balance_to_consume: Gwei + earliest_consolidation_epoch: Epoch + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] + proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] +``` + +## Beacon chain state transition function + +### Execution payload processing + +#### Modified `process_execution_payload` + +*Note*: `process_execution_payload` is modified in EIP-8025 to require both +`ExecutionEngine` and `ProofEngine` for validation. + +```python +def process_execution_payload( + state: BeaconState, + body: BeaconBlockBody, + execution_engine: ExecutionEngine, + proof_engine: ProofEngine, +) -> None: + payload = body.execution_payload + + # Verify consistency of the parent hash with respect to the previous execution payload header + assert payload.parent_hash == state.latest_execution_payload_header.block_hash + # Verify prev_randao + assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) + # Verify timestamp + assert payload.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(body.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + + # Compute list of versioned hashes + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments + ] + + # Verify the execution payload is valid via ExecutionEngine + new_payload_request = NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=body.execution_requests, + ) + assert execution_engine.verify_and_notify_new_payload(new_payload_request) + + # [New in EIP-8025] Verify via ProofEngine + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + blob_gas_used=payload.blob_gas_used, + excess_blob_gas=payload.excess_blob_gas, + ), + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=body.execution_requests, + ) + assert proof_engine.verify_new_payload_request_header(new_payload_request_header) + + # Cache execution payload header + state.latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + blob_gas_used=payload.blob_gas_used, + excess_blob_gas=payload.excess_blob_gas, + ) +``` + +### Execution proof handlers + +*Note*: Proof storage and availability is implementation-dependent, managed by +the `ProofEngine`. Implementations may cache proofs that arrive before their +corresponding payload headers and apply them once the header is received. + +#### New `process_prover_signed_execution_proof` + +```python +def process_prover_signed_execution_proof( + state: BeaconState, + signed_proof: ProverSignedExecutionProof, + proof_engine: ProofEngine, +) -> None: + """ + Handler for ProverSignedExecutionProof. + """ + proof_message = signed_proof.message + prover_pubkey = signed_proof.prover_pubkey + + # Verify prover is whitelisted + assert prover_pubkey in state.prover_whitelist + + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof_message, domain) + assert bls.Verify(prover_pubkey, signing_root, signed_proof.signature) + + # Verify the execution proof + assert proof_engine.verify_execution_proof(proof_message) +``` diff --git a/specs/_features/eip8025_fulu/fork-choice.md b/specs/_features/eip8025_fulu/fork-choice.md new file mode 100644 index 0000000000..724ce219e2 --- /dev/null +++ b/specs/_features/eip8025_fulu/fork-choice.md @@ -0,0 +1,80 @@ +# EIP-8025 (Fulu) -- Fork Choice + +*Note*: This document is a work-in-progress for researchers and implementers. + + + +- [Introduction](#introduction) +- [Handlers](#handlers) + - [Modified `on_block`](#modified-on_block) + + + +## Introduction + +This is the modification of the fork choice accompanying the EIP-8025 upgrade +for Fulu, enabling stateless validation of execution payloads through +cryptographic proofs. + +*Note*: This specification is built upon [Fulu](../../fulu/fork-choice.md) and +imports proof types from [proof-engine.md](./proof-engine.md). + +## Handlers + +### Modified `on_block` + +*Note*: `on_block` is modified in EIP-8025 to include `PROOF_ENGINE` in the call +to `process_execution_payload`. + +```python +def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: + """ + Run ``on_block`` upon receiving a new block. + """ + block = signed_block.message + # Parent block must be known + assert block.parent_root in store.block_states + # Make a copy of the state to avoid mutability issues + pre_state = copy(store.block_states[block.parent_root]) + # Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past. + assert get_current_slot(store) >= block.slot + + # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) + finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) + assert block.slot > finalized_slot + # Check block is a descendant of the finalized block at the checkpoint finalized slot + finalized_checkpoint_block = get_checkpoint_block( + store, + block.parent_root, + store.finalized_checkpoint.epoch, + ) + assert store.finalized_checkpoint.root == finalized_checkpoint_block + + # Check the block is valid and compute the post-state + state = pre_state.copy() + block_root = hash_tree_root(block) + # [Modified in EIP-8025] Pass PROOF_ENGINE to state_transition + state_transition(state, signed_block, EXECUTION_ENGINE, PROOF_ENGINE, validate_result=True) + + # Add new block to the store + store.blocks[block_root] = block + # Add new state for this block to the store + store.block_states[block_root] = state + + # Add block timeliness to the store + time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT + is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + is_timely = get_current_slot(store) == block.slot and is_before_attesting_interval + store.block_timeliness[block_root] = is_timely + + # Add proposer score boost if the block is timely and not conflicting with an existing block + is_first_block = store.proposer_boost_root == Root() + if is_timely and is_first_block: + store.proposer_boost_root = block_root + + # Update checkpoints in store if necessary + update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint) + + # Eagerly compute unrealized justification and finality. + compute_pulled_up_tip(store, block_root) +``` diff --git a/specs/_features/eip8025_fulu/p2p-interface.md b/specs/_features/eip8025_fulu/p2p-interface.md new file mode 100644 index 0000000000..37ef4ad5c2 --- /dev/null +++ b/specs/_features/eip8025_fulu/p2p-interface.md @@ -0,0 +1,90 @@ +# EIP-8025 (Fulu) -- Networking + +This document contains the networking specification for EIP-8025 on Fulu. + +*Note*: This specification is built upon [Fulu](../../fulu/p2p-interface.md) and +imports proof types from [proof-engine.md](./proof-engine.md). + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Global topics](#global-topics) + - [`execution_proof`](#execution_proof) +- [The Req/Resp domain](#the-reqresp-domain) + - [Messages](#messages) + - [ExecutionProofsByRoot](#executionproofsbyroot) + + + +## The gossip domain: gossipsub + +### Topics and messages + +#### Global topics + +##### `execution_proof` + +This topic is used to propagate `ProverSignedExecutionProof` messages. + +The following validations MUST pass before forwarding a proof on the network: + +- _[IGNORE]_ The proof's corresponding new payload request (identified by + `proof.message.public_inputs.new_payload_request_root`) has been seen (via + gossip or non-gossip sources) (a client MAY queue proofs for processing once + the new payload request is retrieved). +- _[IGNORE]_ The proof is the first valid proof received for the tuple + `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, prover_pubkey)`. + +For `ProverSignedExecutionProof`: + +- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. +- _[REJECT]_ The signature is valid with respect to the prover's public key. +- _[REJECT]_ The `proof.message.proof_data` is non-empty. +- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. +- _[REJECT]_ The execution proof is valid as verified by the appropriate + handler. + +## The Req/Resp domain + +### Messages + +#### ExecutionProofsByRoot + +**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` + +The `` field is calculated as +`context = compute_fork_digest(fork_version, genesis_validators_root)`. + +Request Content: + +``` +( + Root +) +``` + +Response Content: + +``` +( + List[ProverSignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] +) +``` + +Requests execution proofs for the given `new_payload_request_root`. The response +MUST contain all available proofs for the requested root, up to +`MAX_EXECUTION_PROOFS_PER_PAYLOAD`. + +The following validations MUST pass: + +- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. + +The response MUST contain: + +- All available execution proofs for the requested `new_payload_request_root`. +- The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` + proofs. diff --git a/specs/_features/eip8025_fulu/proof-engine.md b/specs/_features/eip8025_fulu/proof-engine.md new file mode 100644 index 0000000000..d86bf6dae3 --- /dev/null +++ b/specs/_features/eip8025_fulu/proof-engine.md @@ -0,0 +1,155 @@ +# EIP-8025 -- Proof Engine + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Constants](#constants) + - [Execution](#execution) + - [Custom types](#custom-types) + - [Domain types](#domain-types) +- [Configuration](#configuration) +- [Containers](#containers) + - [`PublicInput`](#publicinput) + - [`ExecutionProof`](#executionproof) + - [`ProofAttributes`](#proofattributes) +- [Proof engine](#proof-engine) + - [`verify_execution_proof`](#verify_execution_proof) + - [`verify_new_payload_request_header`](#verify_new_payload_request_header) + - [`request_proofs`](#request_proofs) + - [`get_proofs`](#get_proofs) + + + +## Introduction + +This document contains the shared Proof Engine specification for EIP-8025. The +Proof Engine enables stateless validation of execution payloads through +cryptographic proofs. This specification is fork-agnostic and is imported by +both Fulu and Gloas EIP-8025 implementations. + +## Constants + +### Execution + +| Name | Value | +| ---------------------------------- | ------------------- | +| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | +| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | + +### Custom types + +| Name | SSZ equivalent | Description | +| ------------ | -------------- | ---------------------------------------- | +| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | +| `ProofType` | `uint8` | Type identifier for proof system | + +### Domain types + +| Name | Value | +| ------------------------ | -------------------------- | +| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0E000000')` | + +## Configuration + +*Note*: The configuration values are not definitive. + +| Name | Value | +| ------------------------------- | ------------- | +| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | +| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | + +## Containers + +### `PublicInput` + +```python +class PublicInput(Container): + new_payload_request_root: Root +``` + +### `ExecutionProof` + +```python +class ExecutionProof(Container): + proof_data: ByteList[MAX_PROOF_SIZE] + proof_type: ProofType + public_inputs: PublicInput +``` + +### `ProofAttributes` + +```python +@dataclass +class ProofAttributes(object): + proof_types: List[ProofType] +``` + +## Proof engine + +The implementation-dependent `ProofEngine` protocol encapsulates the proof +sub-system logic via: + +- proof generation and verification functions + +The body of these functions are implementation dependent. + +### `verify_execution_proof` + +```python +def verify_execution_proof( + self: ProofEngine, + execution_proof: ExecutionProof, +) -> bool: + """ + Submit and verify an execution proof. + Return ``True`` if proof is valid. + """ + ... +``` + +### `verify_new_payload_request_header` + +```python +def verify_new_payload_request_header( + self: ProofEngine, + new_payload_request_header: NewPayloadRequestHeader, +) -> bool: + """ + Verify that sufficient valid proofs exist for the given payload request header. + Return ``True`` if proof requirements are satisfied. + """ + ... +``` + +### `request_proofs` + +```python +def request_proofs( + self: ProofEngine, + new_payload_request: NewPayloadRequest, + proof_attributes: ProofAttributes, +) -> ProofGenId: + """ + Request proof generation for a payload with specified proof types. + Returns a ``ProofGenId`` to track the generation request. + """ + ... +``` + +### `get_proofs` + +```python +def get_proofs( + self: ProofEngine, + proof_gen_id: ProofGenId, +) -> List[ExecutionProof]: + """ + Retrieve all generated proofs for a generation request. + """ + ... +``` diff --git a/specs/_features/eip8025_fulu/prover.md b/specs/_features/eip8025_fulu/prover.md new file mode 100644 index 0000000000..58f4d49282 --- /dev/null +++ b/specs/_features/eip8025_fulu/prover.md @@ -0,0 +1,78 @@ +# EIP-8025 (Fulu) -- Honest Prover + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Helpers](#helpers) + - [`get_execution_proof_signature`](#get_execution_proof_signature) +- [Constructing `ProverSignedExecutionProof`](#constructing-proversignedexecutionproof) +- [Honest prover relay](#honest-prover-relay) + + + +## Introduction + +This document provides guidance for honest provers and prover relays in the +EIP-8025 network on Fulu. + +*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and +imports proof types from [proof-engine.md](./proof-engine.md). + +## Helpers + +### `get_execution_proof_signature` + +```python +def get_execution_proof_signature( + state: BeaconState, proof: ExecutionProof, privkey: int +) -> BLSSignature: + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof, domain) + return bls.Sign(privkey, signing_root) +``` + +## Constructing `ProverSignedExecutionProof` + +An honest prover who has been whitelisted and wants to generate execution proofs +for a `BeaconBlockBody` performs the following steps: + +1. Extract `NewPayloadRequest` from `BeaconBlockBody`: + - `execution_payload = body.execution_payload` + - `versioned_hashes = [kzg_commitment_to_versioned_hash(c) for c in body.blob_kzg_commitments]` + - `parent_beacon_block_root = state.latest_block_header.parent_root` + - `execution_requests = body.execution_requests` +2. Create `ProofAttributes` with desired proof types. +3. Call `proof_engine.request_proofs(new_payload_request, proof_attributes)` to + initiate proof generation. +4. Call `proof_engine.get_proofs(proof_gen_id)` to retrieve the generated + proofs. +5. For each `ExecutionProof` in the returned list: + - Set `message` to the `ExecutionProof`. + - Set `prover_pubkey` to the prover's public key. + - Sign the proof using + `get_execution_proof_signature(state, proof, prover_privkey)`. + - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip + topic. + +## Honest prover relay + +A prover relay is a trusted intermediary that accepts unsigned execution proofs +from community provers and signs them for broadcast. The relay's public key MUST +be in the prover whitelist. + +When a prover relay receives an unsigned `ExecutionProof`: + +1. Validate that `proof_data` is non-empty. +2. Verify the execution proof is valid using + `proof_engine.verify_execution_proof(proof)`. +3. Check the proof is not a duplicate (same `new_payload_request_root`, + `proof_type`). +4. If valid and not a duplicate: + - Create a `ProverSignedExecutionProof` with the relay's public key and + signature. + - Broadcast on the `execution_proof` gossip topic. diff --git a/specs/_features/eip8025_fulu/validator.md b/specs/_features/eip8025_fulu/validator.md new file mode 100644 index 0000000000..fddde14e55 --- /dev/null +++ b/specs/_features/eip8025_fulu/validator.md @@ -0,0 +1,30 @@ +# EIP-8025 (Fulu) -- Honest Validator + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Validator behavior](#validator-behavior) + + + +## Introduction + +This document provides guidance for validators in the EIP-8025 network on Fulu. + +*Note*: This specification is built upon [Fulu](../../fulu/validator.md) and +imports proof types from [proof-engine.md](./proof-engine.md). + +## Validator behavior + +In EIP-8025 on Fulu, execution proof generation is handled by whitelisted +provers. Validators inherit all behaviors from the Fulu specification except +where overridden by this document. + +Validators are not required to generate execution proofs themselves. Instead, +they rely on provers (either standalone provers or prover relays) to generate +and broadcast execution proofs for the payloads they produce. diff --git a/specs/_features/eip8025_gloas/beacon-chain.md b/specs/_features/eip8025_gloas/beacon-chain.md new file mode 100644 index 0000000000..ce6580d5ec --- /dev/null +++ b/specs/_features/eip8025_gloas/beacon-chain.md @@ -0,0 +1,452 @@ +# EIP-8025 (Gloas) -- The Beacon Chain + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Containers](#containers) + - [New containers](#new-containers) + - [`BuilderSignedExecutionProof`](#buildersignedexecutionproof) + - [`ProverSignedExecutionProof`](#proversignedexecutionproof) + - [`NewPayloadRequestHeader`](#newpayloadrequestheader) + - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) + - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) + - [Extended Containers](#extended-containers) + - [`BeaconState`](#beaconstate) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Execution payload processing](#execution-payload-processing) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [New `verify_execution_payload_header_envelope_signature`](#new-verify_execution_payload_header_envelope_signature) + - [New `process_execution_payload_header`](#new-process_execution_payload_header) + - [Execution proof handlers](#execution-proof-handlers) + - [New `process_builder_signed_execution_proof`](#new-process_builder_signed_execution_proof) + - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) + + + +## Introduction + +This document contains the Gloas-specific consensus specs for EIP-8025. This +enables stateless validation of execution payloads through cryptographic proofs. + +*Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md) +and imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Containers + +### New containers + +#### `BuilderSignedExecutionProof` + +```python +class BuilderSignedExecutionProof(Container): + message: ExecutionProof + builder_index: BuilderIndex + signature: BLSSignature +``` + +#### `ProverSignedExecutionProof` + +```python +class ProverSignedExecutionProof(Container): + message: ExecutionProof + prover_pubkey: BLSPubkey + signature: BLSSignature +``` + +#### `NewPayloadRequestHeader` + +```python +@dataclass +class NewPayloadRequestHeader(object): + execution_payload_header: ExecutionPayloadHeader + versioned_hashes: Sequence[VersionedHash] + parent_beacon_block_root: Root + execution_requests: ExecutionRequests +``` + +#### `ExecutionPayloadHeaderEnvelope` + +```python +class ExecutionPayloadHeaderEnvelope(Container): + payload: ExecutionPayloadHeader + execution_requests: ExecutionRequests + builder_index: BuilderIndex + beacon_block_root: Root + slot: Slot + blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] + state_root: Root +``` + +#### `SignedExecutionPayloadHeaderEnvelope` + +```python +class SignedExecutionPayloadHeaderEnvelope(Container): + message: ExecutionPayloadHeaderEnvelope + signature: BLSSignature +``` + +### Extended Containers + +#### `BeaconState` + +```python +class BeaconState(Container): + genesis_time: uint64 + genesis_validators_root: Root + slot: Slot + fork: Fork + latest_block_header: BeaconBlockHeader + block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] + # [New in EIP-8025] + prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] + historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] + eth1_data: Eth1Data + eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] + eth1_deposit_index: uint64 + validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] + balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] + randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] + slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] + previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] + justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] + previous_justified_checkpoint: Checkpoint + current_justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] + current_sync_committee: SyncCommittee + next_sync_committee: SyncCommittee + # [Modified in Gloas:EIP7732] + # Removed `latest_execution_payload_header` + # [New in Gloas:EIP7732] + latest_execution_payload_bid: ExecutionPayloadBid + next_withdrawal_index: WithdrawalIndex + next_withdrawal_validator_index: ValidatorIndex + historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] + deposit_requests_start_index: uint64 + deposit_balance_to_consume: Gwei + exit_balance_to_consume: Gwei + earliest_exit_epoch: Epoch + consolidation_balance_to_consume: Gwei + earliest_consolidation_epoch: Epoch + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] + proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] + # [New in Gloas:EIP7732] + builders: List[Builder, BUILDER_REGISTRY_LIMIT] + # [New in Gloas:EIP7732] + next_withdrawal_builder_index: BuilderIndex + # [New in Gloas:EIP7732] + execution_payload_availability: Bitvector[SLOTS_PER_HISTORICAL_ROOT] + # [New in Gloas:EIP7732] + builder_pending_payments: Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH] + # [New in Gloas:EIP7732] + builder_pending_withdrawals: List[BuilderPendingWithdrawal, BUILDER_PENDING_WITHDRAWALS_LIMIT] + # [New in Gloas:EIP7732] + latest_block_hash: Hash32 + # [New in Gloas:EIP7732] + payload_expected_withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] +``` + +*Note*: `BeaconBlockBody` remains unchanged. + +## Beacon chain state transition function + +### Execution payload processing + +#### Modified `process_execution_payload` + +*Note*: `process_execution_payload` is modified in EIP-8025 to require both +`ExecutionEngine` and `ProofEngine` for validation. + +```python +def process_execution_payload( + state: BeaconState, + signed_envelope: SignedExecutionPayloadEnvelope, + execution_engine: ExecutionEngine, + proof_engine: ProofEngine, + verify: bool = True, +) -> None: + envelope = signed_envelope.message + payload = envelope.payload + + # Verify signature + if verify: + assert verify_execution_payload_envelope_signature(state, signed_envelope) + + # Cache latest block header state root + previous_state_root = hash_tree_root(state) + if state.latest_block_header.state_root == Root(): + state.latest_block_header.state_root = previous_state_root + + # Verify consistency with the beacon block + assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) + assert envelope.slot == state.slot + + # Verify consistency with the committed bid + committed_bid = state.latest_execution_payload_bid + assert envelope.builder_index == committed_bid.builder_index + assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) + assert committed_bid.prev_randao == payload.prev_randao + + # Verify consistency with expected withdrawals + assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals) + + # Verify the gas_limit + assert committed_bid.gas_limit == payload.gas_limit + # Verify the block hash + assert committed_bid.block_hash == payload.block_hash + # Verify consistency of the parent hash with respect to the previous execution payload + assert payload.parent_hash == state.latest_block_hash + # Verify timestamp + assert payload.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(envelope.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + # Verify the execution payload is valid + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments + ] + requests = envelope.execution_requests + new_payload_request = NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + assert execution_engine.verify_and_notify_new_payload(new_payload_request) + + # [New in EIP-8025] Verify via ProofEngine + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + blob_gas_used=payload.blob_gas_used, + excess_blob_gas=payload.excess_blob_gas, + ), + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + assert proof_engine.verify_new_payload_request_header(new_payload_request_header) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(requests.deposits, process_deposit_request) + for_ops(requests.withdrawals, process_withdrawal_request) + for_ops(requests.consolidations, process_consolidation_request) + + # Queue the builder payment + payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] + amount = payment.withdrawal.amount + if amount > 0: + state.builder_pending_withdrawals.append(payment.withdrawal) + state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( + BuilderPendingPayment() + ) + + # Cache the execution payload hash + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + state.latest_block_hash = payload.block_hash + + # Verify the state root + if verify: + assert envelope.state_root == hash_tree_root(state) +``` + +#### New `verify_execution_payload_header_envelope_signature` + +```python +def verify_execution_payload_header_envelope_signature( + state: BeaconState, signed_envelope: SignedExecutionPayloadHeaderEnvelope +) -> bool: + builder_index = signed_envelope.message.builder_index + if builder_index == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey + else: + pubkey = state.builders[builder_index].pubkey + + signing_root = compute_signing_root( + signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER) + ) + return bls.Verify(pubkey, signing_root, signed_envelope.signature) +``` + +#### New `process_execution_payload_header` + +*Note*: `process_execution_payload_header` is the stateless equivalent of +`process_execution_payload`. It processes execution payload headers using +execution proofs for validation instead of the `ExecutionEngine`. + +```python +def process_execution_payload_header( + state: BeaconState, + signed_envelope: SignedExecutionPayloadHeaderEnvelope, + proof_engine: ProofEngine, + verify: bool = True, +) -> None: + envelope = signed_envelope.message + payload = envelope.payload + + # Verify signature + if verify: + assert verify_execution_payload_header_envelope_signature(state, signed_envelope) + + # Cache latest block header state root + previous_state_root = hash_tree_root(state) + if state.latest_block_header.state_root == Root(): + state.latest_block_header.state_root = previous_state_root + + # Verify consistency with the beacon block + assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) + assert envelope.slot == state.slot + + # Verify consistency with the committed bid + committed_bid = state.latest_execution_payload_bid + assert envelope.builder_index == committed_bid.builder_index + assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) + assert committed_bid.prev_randao == payload.prev_randao + + # Verify consistency with expected withdrawals + assert payload.withdrawals_root == hash_tree_root(state.payload_expected_withdrawals) + + # Verify the gas_limit + assert committed_bid.gas_limit == payload.gas_limit + # Verify the block hash + assert committed_bid.block_hash == payload.block_hash + # Verify consistency of the parent hash with respect to the previous execution payload + assert payload.parent_hash == state.latest_block_hash + # Verify timestamp + assert payload.timestamp == compute_time_at_slot(state, state.slot) + # Verify commitments are under limit + assert ( + len(envelope.blob_kzg_commitments) + <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block + ) + + # [New in EIP-8025] Verify the execution payload request header using execution proofs + versioned_hashes = [ + kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments + ] + requests = envelope.execution_requests + new_payload_request_header = NewPayloadRequestHeader( + execution_payload_header=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=requests, + ) + + # Verify sufficient proofs exist via ProofEngine + assert proof_engine.verify_new_payload_request_header(new_payload_request_header) + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(requests.deposits, process_deposit_request) + for_ops(requests.withdrawals, process_withdrawal_request) + for_ops(requests.consolidations, process_consolidation_request) + + # Queue the builder payment + payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] + amount = payment.withdrawal.amount + if amount > 0: + state.builder_pending_withdrawals.append(payment.withdrawal) + state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( + BuilderPendingPayment() + ) + + # Cache the execution payload hash + state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 + state.latest_block_hash = payload.block_hash + + # Verify the state root + if verify: + assert envelope.state_root == hash_tree_root(state) +``` + +### Execution proof handlers + +*Note*: Proof storage and availability is implementation-dependent, managed by +the `ProofEngine`. Implementations may cache proofs that arrive before their +corresponding payload headers and apply them once the header is received. + +#### New `process_builder_signed_execution_proof` + +```python +def process_builder_signed_execution_proof( + state: BeaconState, + signed_proof: BuilderSignedExecutionProof, + proof_engine: ProofEngine, +) -> None: + """ + Handler for BuilderSignedExecutionProof. + """ + proof_message = signed_proof.message + builder_index = signed_proof.builder_index + + # Determine pubkey based on builder_index + if builder_index == BUILDER_INDEX_SELF_BUILD: + validator_index = state.latest_block_header.proposer_index + pubkey = state.validators[validator_index].pubkey + else: + pubkey = state.builders[builder_index].pubkey + + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof_message, domain) + assert bls.Verify(pubkey, signing_root, signed_proof.signature) + + # Verify the execution proof + assert proof_engine.verify_execution_proof(proof_message) +``` + +#### New `process_prover_signed_execution_proof` + +```python +def process_prover_signed_execution_proof( + state: BeaconState, + signed_proof: ProverSignedExecutionProof, + proof_engine: ProofEngine, +) -> None: + """ + Handler for ProverSignedExecutionProof. + """ + proof_message = signed_proof.message + prover_pubkey = signed_proof.prover_pubkey + + # Verify prover is whitelisted + assert prover_pubkey in state.prover_whitelist + + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof_message, domain) + assert bls.Verify(prover_pubkey, signing_root, signed_proof.signature) + + # Verify the execution proof + assert proof_engine.verify_execution_proof(proof_message) +``` diff --git a/specs/_features/eip8025_gloas/builder.md b/specs/_features/eip8025_gloas/builder.md new file mode 100644 index 0000000000..a859a6b4aa --- /dev/null +++ b/specs/_features/eip8025_gloas/builder.md @@ -0,0 +1,89 @@ +# EIP-8025 (Gloas) -- Honest Builder + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) +- [Execution proof signature](#execution-proof-signature) +- [Constructing the `BuilderSignedExecutionProof`](#constructing-the-buildersignedexecutionproof) + + + +## Introduction + +This document represents the changes to the builder guide accompanying EIP-8025 +on Gloas. + +*Note*: This specification imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Prerequisites + +This document is an extension of the +[Gloas -- Honest Builder](../../gloas/builder.md) guide. All behaviors and +definitions defined in this document, and documents it extends, carry over +unless explicitly noted or overridden. + +All terminology, constants, functions, and protocol mechanics defined in the +[EIP-8025 (Gloas) -- Beacon Chain](./beacon-chain.md) document are requisite for +this document. + +## Constructing the `SignedExecutionPayloadHeaderEnvelope` + +Builders MUST broadcast `SignedExecutionPayloadHeaderEnvelope` messages to allow +ZK attesters to perform proof verification. + +To construct the `SignedExecutionPayloadHeaderEnvelope` from an existing +`SignedExecutionPayloadEnvelope`: + +1. Set `header_envelope.payload` to the `ExecutionPayloadHeader` derived from + `envelope.payload`. +2. Copy all other fields from the original envelope: + - `header_envelope.execution_requests = envelope.execution_requests` + - `header_envelope.builder_index = envelope.builder_index` + - `header_envelope.beacon_block_root = envelope.beacon_block_root` + - `header_envelope.slot = envelope.slot` + - `header_envelope.blob_kzg_commitments = envelope.blob_kzg_commitments` + - `header_envelope.state_root = envelope.state_root` +3. Set `signed_header_envelope.message = header_envelope`. +4. Set `signed_header_envelope.signature = signed_envelope.signature`. + +Then the builder broadcasts `signed_header_envelope` on the +`signed_execution_payload_header_envelope` global gossip topic. + +## Execution proof signature + +```python +def get_execution_proof_signature( + state: BeaconState, proof: ExecutionProof, privkey: int +) -> BLSSignature: + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof, domain) + return bls.Sign(privkey, signing_root) +``` + +## Constructing the `BuilderSignedExecutionProof` + +After producing an `ExecutionPayloadEnvelope` the builder constructs a set of +`BuilderSignedExecutionProof` as follows: + +1. Extract the `NewPayloadRequest` from the envelope. +2. Select proof types and create `ProofAttributes`. +3. Call + `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` + to initiate proof generation. +4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated + proofs. +5. For each `ExecutionProof` in `proofs`: + - Set `signed_proof.message` to the `ExecutionProof`. + - Set `signed_proof.builder_index` to the builder's `BuilderIndex`. + - Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + - Broadcast the `BuilderSignedExecutionProof` on the `execution_proof` gossip + topic. diff --git a/specs/_features/eip8025_gloas/fork-choice.md b/specs/_features/eip8025_gloas/fork-choice.md new file mode 100644 index 0000000000..186987e6e1 --- /dev/null +++ b/specs/_features/eip8025_gloas/fork-choice.md @@ -0,0 +1,80 @@ +# EIP-8025 (Gloas) -- Fork Choice + +*Note*: This document is a work-in-progress for researchers and implementers. + + + +- [Introduction](#introduction) +- [Handlers](#handlers) + - [Modified `on_execution_payload`](#modified-on_execution_payload) + - [New `on_execution_payload_header`](#new-on_execution_payload_header) + + + +## Introduction + +This is the modification of the fork choice accompanying the EIP-8025 upgrade +for Gloas, enabling stateless validation of execution payloads through +cryptographic proofs. + +*Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md) and +imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Handlers + +### Modified `on_execution_payload` + +*Note*: `on_execution_payload` is modified in EIP-8025 to include `PROOF_ENGINE` +in the call to `process_execution_payload`. + +```python +def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None: + """ + Run ``on_execution_payload`` upon receiving a new execution payload. + """ + envelope = signed_envelope.message + # The corresponding beacon block root needs to be known + assert envelope.beacon_block_root in store.block_states + + # Check if blob data is available + # If not, this payload MAY be queued and subsequently considered when blob data becomes available + assert is_data_available(envelope.beacon_block_root) + + # Make a copy of the state to avoid mutability issues + state = copy(store.block_states[envelope.beacon_block_root]) + + # Process the execution payload + # [Modified in EIP-8025] Added PROOF_ENGINE parameter + process_execution_payload(state, signed_envelope, EXECUTION_ENGINE, PROOF_ENGINE) + + # Add new state for this payload to the store + store.execution_payload_states[envelope.beacon_block_root] = state +``` + +### New `on_execution_payload_header` + +```python +def on_execution_payload_header( + store: Store, signed_envelope: SignedExecutionPayloadHeaderEnvelope +) -> None: + """ + Run ``on_execution_payload_header`` upon receiving a new execution payload header. + """ + envelope = signed_envelope.message + # The corresponding beacon block root needs to be known + assert envelope.beacon_block_root in store.block_states + + # Check if blob data is available + # If not, this payload MAY be queued and subsequently considered when blob data becomes available + assert is_data_available(envelope.beacon_block_root) + + # Make a copy of the state to avoid mutability issues + state = copy(store.block_states[envelope.beacon_block_root]) + + # Process the execution payload header + process_execution_payload_header(state, signed_envelope, PROOF_ENGINE) + + # Add new state for this payload to the store + store.execution_payload_states[envelope.beacon_block_root] = state +``` diff --git a/specs/_features/eip8025_gloas/p2p-interface.md b/specs/_features/eip8025_gloas/p2p-interface.md new file mode 100644 index 0000000000..33875ccb84 --- /dev/null +++ b/specs/_features/eip8025_gloas/p2p-interface.md @@ -0,0 +1,144 @@ +# EIP-8025 (Gloas) -- Networking + +This document contains the networking specification for EIP-8025 on Gloas. + +*Note*: This specification is built upon [Gloas](../../gloas/p2p-interface.md) +and imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Containers](#containers) +- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Global topics](#global-topics) + - [`signed_execution_payload_header_envelope`](#signed_execution_payload_header_envelope) + - [`execution_proof`](#execution_proof) +- [The Req/Resp domain](#the-reqresp-domain) + - [Messages](#messages) + - [ExecutionProofsByRoot](#executionproofsbyroot) + + + +## Containers + +*Note*: Execution proofs are broadcast as either `BuilderSignedExecutionProof` +or `ProverSignedExecutionProof` containers on the same topic. + + + +```python +SignedExecutionProof = Union[BuilderSignedExecutionProof, ProverSignedExecutionProof] +``` + +## The gossip domain: gossipsub + +### Topics and messages + +#### Global topics + +##### `signed_execution_payload_header_envelope` + +This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. +Provers subscribe to this topic to receive execution payload headers for which +they can generate execution proofs. + +The following validations MUST pass before forwarding the +`signed_execution_payload_header_envelope` on the network, assuming the alias +`envelope = signed_execution_payload_header_envelope.message`, +`payload = envelope.payload`: + +- _[IGNORE]_ The envelope's block root `envelope.beacon_block_root` has been + seen (via gossip or non-gossip sources) (a client MAY queue headers for + processing once the block is retrieved). +- _[IGNORE]_ The node has not seen another valid + `SignedExecutionPayloadHeaderEnvelope` for this block root from this builder. +- _[IGNORE]_ The envelope is from a slot greater than or equal to the latest + finalized slot -- i.e. validate that + `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)` + +Let `block` be the block with `envelope.beacon_block_root`. Let `bid` alias +`block.body.signed_execution_payload_bid.message`. + +- _[REJECT]_ `block` passes validation. +- _[REJECT]_ `block.slot` equals `envelope.slot`. +- _[REJECT]_ `envelope.builder_index == bid.builder_index` +- _[REJECT]_ `payload.block_hash == bid.block_hash` +- _[REJECT]_ `signed_execution_payload_header_envelope.signature` is valid with + respect to the builder's public key. + +##### `execution_proof` + +This topic is used to propagate execution proofs. Both +`BuilderSignedExecutionProof` and `ProverSignedExecutionProof` messages are +propagated on this topic. + +The following validations MUST pass before forwarding a proof on the network: + +- _[IGNORE]_ The proof's corresponding new payload request (identified by + `proof.message.public_inputs.new_payload_request_root`) has been seen (via + gossip or non-gossip sources) (a client MAY queue proofs for processing once + the new payload request is retrieved). +- _[IGNORE]_ The proof is the first valid proof received for the tuple + `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, signer)`. + +For `BuilderSignedExecutionProof`: + +- _[REJECT]_ The `builder_index` is within the known builder registry. +- _[REJECT]_ The signature is valid with respect to the builder's public key. + +For `ProverSignedExecutionProof`: + +- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. +- _[REJECT]_ The signature is valid with respect to the prover's public key. + +For both types: + +- _[REJECT]_ The `proof.message.proof_data` is non-empty. +- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. +- _[REJECT]_ The execution proof is valid as verified by the appropriate + handler. + +## The Req/Resp domain + +### Messages + +#### ExecutionProofsByRoot + +**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` + +The `` field is calculated as +`context = compute_fork_digest(fork_version, genesis_validators_root)`. + +Request Content: + +``` +( + Root +) +``` + +Response Content: + +``` +( + List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] +) +``` + +Requests execution proofs for the given `new_payload_request_root`. The response +MUST contain all available proofs for the requested root, up to +`MAX_EXECUTION_PROOFS_PER_PAYLOAD`. + +The following validations MUST pass: + +- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. + +The response MUST contain: + +- All available execution proofs for the requested `new_payload_request_root`. +- The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` + proofs. diff --git a/specs/_features/eip8025_gloas/prover.md b/specs/_features/eip8025_gloas/prover.md new file mode 100644 index 0000000000..d4951e75b8 --- /dev/null +++ b/specs/_features/eip8025_gloas/prover.md @@ -0,0 +1,104 @@ +# EIP-8025 (Gloas) -- Honest Prover + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Execution proof signature](#execution-proof-signature) +- [Constructing the `ProverSignedExecutionProof`](#constructing-the-proversignedexecutionproof) +- [Honest Prover Relay](#honest-prover-relay) + - [Accepting proofs](#accepting-proofs) + - [Signing and broadcasting](#signing-and-broadcasting) + + + +## Introduction + +This document represents the prover guide accompanying EIP-8025 on Gloas. +Provers are whitelisted network operators who generate execution proofs during +the optional proof phase. + +*Note*: This specification imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Prerequisites + +All terminology, constants, functions, and protocol mechanics defined in the +[EIP-8025 (Gloas) -- Beacon Chain](./beacon-chain.md) document are requisite for +this document. + +The prover MUST have their public key included in `WHITELISTED_PROVERS` or +alternatively use a whitelisted community proof relay. + +## Execution proof signature + +```python +def get_execution_proof_signature( + state: BeaconState, proof: ExecutionProof, privkey: int +) -> BLSSignature: + domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) + signing_root = compute_signing_root(proof, domain) + return bls.Sign(privkey, signing_root) +``` + +## Constructing the `ProverSignedExecutionProof` + +Provers subscribe to the `signed_execution_payload_envelope` gossip topic +(defined in [Gloas](../../gloas/p2p-interface.md)) to receive execution payloads +for which they can generate execution proofs. + +To construct a `ProverSignedExecutionProof`: + +1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. +2. Select proof types and create `ProofAttributes`. +3. Call + `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` + to initiate proof generation. +4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated + proofs. +5. For each `ExecutionProof` in `proofs`: + - Set `signed_proof.message` to the `ExecutionProof`. + - Set `signed_proof.prover_pubkey` to the prover's public key. + - Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, privkey)`. + - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip + topic. + +## Honest Prover Relay + +A prover relay is a whitelisted service that accepts execution proofs from +community provers, validates them, signs them, and broadcasts them to the +network. This allows any prover to contribute proofs without needing to be +individually whitelisted. + +### Accepting proofs + +The relay exposes an API endpoint that accepts unsigned `ExecutionProof` +submissions from community provers. Upon receiving a proof, the relay MUST: + +1. Verify the `proof.proof_data` is non-empty. +2. Verify the execution proof is valid using + `verify_execution_proof(proof, program_bytecode)`. +3. Verify a proof for the same `(new_payload_request_root, proof_type)` has not + already been signed and broadcast. + +If any validation fails, the relay SHOULD reject the submission. + +### Signing and broadcasting + +After successful validation, the relay signs and broadcasts the proof: + +1. Set `signed_proof.message` to the validated `proof`. +2. Set `signed_proof.prover_pubkey` to the relay's whitelisted public key. +3. Set `signed_proof.signature` to the result of + `get_execution_proof_signature(state, proof, relay_privkey)`. +4. Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip + topic. + +*Note*: The relay's public key MUST be included in `WHITELISTED_PROVERS`. The +relay takes responsibility for the validity of proofs it signs. diff --git a/specs/_features/eip8025_gloas/validator.md b/specs/_features/eip8025_gloas/validator.md new file mode 100644 index 0000000000..19fe7d0ab0 --- /dev/null +++ b/specs/_features/eip8025_gloas/validator.md @@ -0,0 +1,31 @@ +# EIP-8025 (Gloas) -- Honest Validator + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Validator behavior](#validator-behavior) + + + +## Introduction + +This document provides guidance for validators in the EIP-8025 network on Gloas. + +*Note*: This specification is built upon [Gloas](../../gloas/validator.md) and +imports proof types from +[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). + +## Validator behavior + +In EIP-8025 on Gloas, execution proof generation is handled by builders and +whitelisted provers. Validators inherit all behaviors from the Gloas +specification except where overridden by this document. + +Validators are not required to generate execution proofs themselves. Instead, +they rely on builders (who produce the execution payloads) and provers (either +standalone provers or prover relays) to generate and broadcast execution proofs. From d5e64ff9b67a80af144303b84b63623dcfb0f5f1 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 21:52:49 +0000 Subject: [PATCH 09/28] clean up --- specs/_features/eip8025/beacon-chain.md | 471 +++--------------- specs/_features/eip8025/builder.md | 85 ---- specs/_features/eip8025/fork-choice.md | 78 --- specs/_features/eip8025/p2p-interface.md | 64 +-- specs/_features/eip8025/prover.md | 100 ++-- specs/_features/eip8025/validator.md | 28 -- specs/_features/eip8025_fulu/beacon-chain.md | 227 --------- specs/_features/eip8025_fulu/fork-choice.md | 80 --- specs/_features/eip8025_fulu/p2p-interface.md | 90 ---- specs/_features/eip8025_fulu/proof-engine.md | 155 ------ specs/_features/eip8025_fulu/prover.md | 78 --- specs/_features/eip8025_fulu/validator.md | 30 -- specs/_features/eip8025_gloas/beacon-chain.md | 147 +----- specs/_features/eip8025_gloas/builder.md | 20 +- specs/_features/eip8025_gloas/fork-choice.md | 10 +- .../_features/eip8025_gloas/p2p-interface.md | 38 +- specs/_features/eip8025_gloas/prover.md | 104 ---- specs/_features/eip8025_gloas/validator.md | 31 -- 18 files changed, 145 insertions(+), 1691 deletions(-) delete mode 100644 specs/_features/eip8025/builder.md delete mode 100644 specs/_features/eip8025/fork-choice.md delete mode 100644 specs/_features/eip8025/validator.md delete mode 100644 specs/_features/eip8025_fulu/beacon-chain.md delete mode 100644 specs/_features/eip8025_fulu/fork-choice.md delete mode 100644 specs/_features/eip8025_fulu/p2p-interface.md delete mode 100644 specs/_features/eip8025_fulu/proof-engine.md delete mode 100644 specs/_features/eip8025_fulu/prover.md delete mode 100644 specs/_features/eip8025_fulu/validator.md delete mode 100644 specs/_features/eip8025_gloas/prover.md delete mode 100644 specs/_features/eip8025_gloas/validator.md diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 7283b09196..3a70a25627 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -8,108 +8,35 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) -- [Constants](#constants) - - [Execution](#execution) - - [Custom types](#custom-types) - - [Domains](#domains) ->>>>>>> 56faef161 (refactor: introduce proof engine) - [Configuration](#configuration) - [Containers](#containers) - [New containers](#new-containers) - - [`PublicInput`](#publicinput) - - [`ExecutionProof`](#executionproof) - - [`BuilderSignedExecutionProof`](#buildersignedexecutionproof) - [`ProverSignedExecutionProof`](#proversignedexecutionproof) - [`NewPayloadRequestHeader`](#newpayloadrequestheader) - - [`ProofAttributes`](#proofattributes) - - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) - - [`ExecutionProofStoreEntry`](#executionproofstoreentry) - [Extended containers](#extended-containers) - [`BeaconState`](#beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Proof engine](#proof-engine) - - [`verify_execution_proof`](#verify_execution_proof) - - [`verify_new_payload_request_header`](#verify_new_payload_request_header) - - [`request_proofs`](#request_proofs) - - [`get_proofs`](#get_proofs) + - [Block processing](#block-processing) + - [Modified `process_block`](#modified-process_block) - [Execution payload processing](#execution-payload-processing) - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [New `verify_execution_payload_header_envelope_signature`](#new-verify_execution_payload_header_envelope_signature) - - [New `process_execution_payload_header`](#new-process_execution_payload_header) - [Execution proof handlers](#execution-proof-handlers) - - [New `process_builder_signed_execution_proof`](#new-process_builder_signed_execution_proof) - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) ## Introduction -These are the beacon-chain specifications to add EIP-8025. This enables -stateless validation of execution payloads through cryptographic proofs. +This document contains the consensus specs for EIP-8025, enabling stateless +validation of execution payloads through execution proofs. -*Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md). - -## Constants - -### Execution - -| Name | Value | -| ---------------------------------- | ------------------- | -| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | -| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | - -### Custom types - -| Name | SSZ equivalent | Description | -| ------------ | -------------- | ---------------------------------------- | -| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | -| `ProofType` | `uint8` | Type identifier for proof system | - -### Domains - -| Name | Value | -| ------------------------ | -------------------------- | -| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0E000000')` | - -## Configuration - -*Note*: The configuration values are not definitive. - -| Name | Value | -| ------------------------------- | ------------- | -| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | -| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | +*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and +imports proof types from [proof-engine.md](./proof-engine.md). ## Containers ### New containers -#### `PublicInput` - -```python -class PublicInput(Container): - new_payload_request_root: Root -``` - -#### `ExecutionProof` - -```python -class ExecutionProof(Container): - proof_data: ByteList[MAX_PROOF_SIZE] - proof_type: ProofType - public_inputs: PublicInput -``` - -#### `BuilderSignedExecutionProof` - -```python -class BuilderSignedExecutionProof(Container): - message: ExecutionProof - builder_index: BuilderIndex - signature: BLSSignature -``` - #### `ProverSignedExecutionProof` ```python @@ -130,43 +57,6 @@ class NewPayloadRequestHeader(object): execution_requests: ExecutionRequests ``` -#### `ProofAttributes` - -```python -@dataclass -class ProofAttributes(object): - proof_types: List[ProofType] -``` - -#### `ExecutionPayloadHeaderEnvelope` - -```python -class ExecutionPayloadHeaderEnvelope(Container): - payload: ExecutionPayloadHeader - execution_requests: ExecutionRequests - builder_index: BuilderIndex - beacon_block_root: Root - slot: Slot - blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] - state_root: Root -``` - -#### `SignedExecutionPayloadHeaderEnvelope` - -```python -class SignedExecutionPayloadHeaderEnvelope(Container): - message: ExecutionPayloadHeaderEnvelope - signature: BLSSignature -``` - -#### `ExecutionProofStoreEntry` - -```python -class ExecutionProofStoreEntry(Container): - header: Optional[NewPayloadRequestHeader] - proofs: List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] -``` - ### Extended containers #### `BeaconState` @@ -180,8 +70,6 @@ class BeaconState(Container): latest_block_header: BeaconBlockHeader block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - # [New in EIP-8025] - prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] eth1_data: Eth1Data eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] @@ -199,10 +87,7 @@ class BeaconState(Container): inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] current_sync_committee: SyncCommittee next_sync_committee: SyncCommittee - # [Modified in Gloas:EIP7732] - # Removed `latest_execution_payload_header` - # [New in Gloas:EIP7732] - latest_execution_payload_bid: ExecutionPayloadBid + latest_execution_payload_header: ExecutionPayloadHeader next_withdrawal_index: WithdrawalIndex next_withdrawal_validator_index: ValidatorIndex historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] @@ -216,89 +101,29 @@ class BeaconState(Container): pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] - # [New in Gloas:EIP7732] - builders: List[Builder, BUILDER_REGISTRY_LIMIT] - # [New in Gloas:EIP7732] - next_withdrawal_builder_index: BuilderIndex - # [New in Gloas:EIP7732] - execution_payload_availability: Bitvector[SLOTS_PER_HISTORICAL_ROOT] - # [New in Gloas:EIP7732] - builder_pending_payments: Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH] - # [New in Gloas:EIP7732] - builder_pending_withdrawals: List[BuilderPendingWithdrawal, BUILDER_PENDING_WITHDRAWALS_LIMIT] - # [New in Gloas:EIP7732] - latest_block_hash: Hash32 - # [New in Gloas:EIP7732] - payload_expected_withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] + # [New in EIP-8025] + prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] ``` -*Note*: `BeaconBlockBody` remains unchanged. - ## Beacon chain state transition function -### Proof engine - -The implementation-dependent `ProofEngine` protocol encapsulates the proof -sub-system logic via: - -- proof generation and verification functions - -The body of these functions are implementation dependent. +### Block processing -#### `verify_execution_proof` - -```python -def verify_execution_proof( - self: ProofEngine, - execution_proof: ExecutionProof, -) -> bool: - """ - Submit and verify an execution proof. - Return ``True`` if proof is valid. - """ - ... -``` +#### Modified `process_block` -#### `verify_new_payload_request_header` +*Note*: `process_block` is modified in EIP-8025 to pass `PROOF_ENGINE` to +`process_execution_payload`. ```python -def verify_new_payload_request_header( - self: ProofEngine, - new_payload_request_header: NewPayloadRequestHeader, -) -> bool: - """ - Verify that sufficient valid proofs exist for the given payload request header. - Return ``True`` if proof requirements are satisfied. - """ - ... -``` - -#### `request_proofs` - -```python -def request_proofs( - self: ProofEngine, - new_payload_request: NewPayloadRequest, - proof_attributes: ProofAttributes, -) -> ProofGenId: - """ - Request proof generation for a payload with specified proof types. - Returns a ``ProofGenId`` to track the generation request. - """ - ... -``` - -#### `get_proofs` - -```python -def get_proofs( - self: ProofEngine, - proof_gen_id: ProofGenId, -) -> List[ExecutionProof]: - """ - Retrieve all generated proofs for a generation request. - """ - ... +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_block_header(state, block) + process_withdrawals(state, block.body.execution_payload) + # [Modified in EIP-8025] + process_execution_payload(state, block.body, EXECUTION_ENGINE, PROOF_ENGINE) + process_randao(state, block.body) + process_eth1_data(state, block.body) + process_operations(state, block.body) + process_sync_aggregate(state, block.body.sync_aggregate) ``` ### Execution payload processing @@ -311,61 +136,38 @@ def get_proofs( ```python def process_execution_payload( state: BeaconState, - signed_envelope: SignedExecutionPayloadEnvelope, + body: BeaconBlockBody, execution_engine: ExecutionEngine, proof_engine: ProofEngine, - verify: bool = True, ) -> None: - envelope = signed_envelope.message - payload = envelope.payload - - # Verify signature - if verify: - assert verify_execution_payload_envelope_signature(state, signed_envelope) - - # Cache latest block header state root - previous_state_root = hash_tree_root(state) - if state.latest_block_header.state_root == Root(): - state.latest_block_header.state_root = previous_state_root - - # Verify consistency with the beacon block - assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) - assert envelope.slot == state.slot - - # Verify consistency with the committed bid - committed_bid = state.latest_execution_payload_bid - assert envelope.builder_index == committed_bid.builder_index - assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) - assert committed_bid.prev_randao == payload.prev_randao - - # Verify consistency with expected withdrawals - assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals) - - # Verify the gas_limit - assert committed_bid.gas_limit == payload.gas_limit - # Verify the block hash - assert committed_bid.block_hash == payload.block_hash - # Verify consistency of the parent hash with respect to the previous execution payload - assert payload.parent_hash == state.latest_block_hash + payload = body.execution_payload + + # Verify consistency of the parent hash with respect to the previous execution payload header + assert payload.parent_hash == state.latest_execution_payload_header.block_hash + # Verify prev_randao + assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) # Verify timestamp assert payload.timestamp == compute_time_at_slot(state, state.slot) # Verify commitments are under limit assert ( - len(envelope.blob_kzg_commitments) + len(body.blob_kzg_commitments) <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block ) - # Verify the execution payload is valid + + # Compute list of versioned hashes versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments + kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments ] - requests = envelope.execution_requests - new_payload_request = NewPayloadRequest( - execution_payload=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, + + # Verify the execution payload is valid via ExecutionEngine + assert execution_engine.verify_and_notify_new_payload( + NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + execution_requests=body.execution_requests, + ) ) - assert execution_engine.verify_and_notify_new_payload(new_payload_request) # [New in EIP-8025] Verify via ProofEngine new_payload_request_header = NewPayloadRequestHeader( @@ -390,182 +192,35 @@ def process_execution_payload( ), versioned_hashes=versioned_hashes, parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, + execution_requests=body.execution_requests, ) assert proof_engine.verify_new_payload_request_header(new_payload_request_header) - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(requests.deposits, process_deposit_request) - for_ops(requests.withdrawals, process_withdrawal_request) - for_ops(requests.consolidations, process_consolidation_request) - - # Queue the builder payment - payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] - amount = payment.withdrawal.amount - if amount > 0: - state.builder_pending_withdrawals.append(payment.withdrawal) - state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( - BuilderPendingPayment() - ) - - # Cache the execution payload hash - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 - state.latest_block_hash = payload.block_hash - - # Verify the state root - if verify: - assert envelope.state_root == hash_tree_root(state) -``` - -#### New `verify_execution_payload_header_envelope_signature` - -```python -def verify_execution_payload_header_envelope_signature( - state: BeaconState, signed_envelope: SignedExecutionPayloadHeaderEnvelope -) -> bool: - builder_index = signed_envelope.message.builder_index - if builder_index == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey - else: - pubkey = state.builders[builder_index].pubkey - - signing_root = compute_signing_root( - signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER) + # Cache execution payload header + state.latest_execution_payload_header = ExecutionPayloadHeader( + parent_hash=payload.parent_hash, + fee_recipient=payload.fee_recipient, + state_root=payload.state_root, + receipts_root=payload.receipts_root, + logs_bloom=payload.logs_bloom, + prev_randao=payload.prev_randao, + block_number=payload.block_number, + gas_limit=payload.gas_limit, + gas_used=payload.gas_used, + timestamp=payload.timestamp, + extra_data=payload.extra_data, + base_fee_per_gas=payload.base_fee_per_gas, + block_hash=payload.block_hash, + transactions_root=hash_tree_root(payload.transactions), + withdrawals_root=hash_tree_root(payload.withdrawals), + blob_gas_used=payload.blob_gas_used, + excess_blob_gas=payload.excess_blob_gas, ) - return bls.Verify(pubkey, signing_root, signed_envelope.signature) -``` - -#### New `process_execution_payload_header` - -*Note*: `process_execution_payload_header` is the stateless equivalent of -`process_execution_payload`. It processes execution payload headers using -execution proofs for validation instead of the `ExecutionEngine`. - -```python -def process_execution_payload_header( - state: BeaconState, - signed_envelope: SignedExecutionPayloadHeaderEnvelope, - proof_engine: ProofEngine, - verify: bool = True, -) -> None: - envelope = signed_envelope.message - payload = envelope.payload - - # Verify signature - if verify: - assert verify_execution_payload_header_envelope_signature(state, signed_envelope) - - # Cache latest block header state root - previous_state_root = hash_tree_root(state) - if state.latest_block_header.state_root == Root(): - state.latest_block_header.state_root = previous_state_root - - # Verify consistency with the beacon block - assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) - assert envelope.slot == state.slot - - # Verify consistency with the committed bid - committed_bid = state.latest_execution_payload_bid - assert envelope.builder_index == committed_bid.builder_index - assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) - assert committed_bid.prev_randao == payload.prev_randao - - # Verify consistency with expected withdrawals - assert payload.withdrawals_root == hash_tree_root(state.payload_expected_withdrawals) - - # Verify the gas_limit - assert committed_bid.gas_limit == payload.gas_limit - # Verify the block hash - assert committed_bid.block_hash == payload.block_hash - # Verify consistency of the parent hash with respect to the previous execution payload - assert payload.parent_hash == state.latest_block_hash - # Verify timestamp - assert payload.timestamp == compute_time_at_slot(state, state.slot) - # Verify commitments are under limit - assert ( - len(envelope.blob_kzg_commitments) - <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block - ) - - # [New in EIP-8025] Verify the execution payload request header using execution proofs - versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments - ] - requests = envelope.execution_requests - new_payload_request_header = NewPayloadRequestHeader( - execution_payload_header=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - - # Verify sufficient proofs exist via ProofEngine - assert proof_engine.verify_new_payload_request_header(new_payload_request_header) - - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(requests.deposits, process_deposit_request) - for_ops(requests.withdrawals, process_withdrawal_request) - for_ops(requests.consolidations, process_consolidation_request) - - # Queue the builder payment - payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] - amount = payment.withdrawal.amount - if amount > 0: - state.builder_pending_withdrawals.append(payment.withdrawal) - state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( - BuilderPendingPayment() - ) - - # Cache the execution payload hash - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 - state.latest_block_hash = payload.block_hash - - # Verify the state root - if verify: - assert envelope.state_root == hash_tree_root(state) ``` ### Execution proof handlers -*Note*: Proof storage and availability is implementation-dependent, managed by -the `ProofEngine`. Implementations may cache proofs that arrive before their -corresponding payload headers and apply them once the header is received. - -#### New `process_builder_signed_execution_proof` - -```python -def process_builder_signed_execution_proof( - state: BeaconState, - signed_proof: BuilderSignedExecutionProof, - proof_engine: ProofEngine, -) -> None: - """ - Handler for BuilderSignedExecutionProof. - """ - proof_message = signed_proof.message - builder_index = signed_proof.builder_index - - # Determine pubkey based on builder_index - if builder_index == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey - else: - pubkey = state.builders[builder_index].pubkey - - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof_message, domain) - assert bls.Verify(pubkey, signing_root, signed_proof.signature) - - # Verify the execution proof - assert proof_engine.verify_execution_proof(proof_message) -``` +*Note*: Proof storage is implementation-dependent, managed by the `ProofEngine`. #### New `process_prover_signed_execution_proof` diff --git a/specs/_features/eip8025/builder.md b/specs/_features/eip8025/builder.md deleted file mode 100644 index dbabcb3fe5..0000000000 --- a/specs/_features/eip8025/builder.md +++ /dev/null @@ -1,85 +0,0 @@ -# EIP-8025 -- Honest Builder - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Prerequisites](#prerequisites) -- [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) -- [Execution proof signature](#execution-proof-signature) -- [Constructing the `BuilderSignedExecutionProof`](#constructing-the-buildersignedexecutionproof) - - - -## Introduction - -This document represents the changes to the builder guide accompanying EIP-8025. - -## Prerequisites - -This document is an extension of the -[Gloas -- Honest Builder](../../gloas/builder.md) guide. All behaviors and -definitions defined in this document, and documents it extends, carry over -unless explicitly noted or overridden. - -All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this -document. - -## Constructing the `SignedExecutionPayloadHeaderEnvelope` - -Builders MUST broadcast `SignedExecutionPayloadHeaderEnvelope` messages to allow -ZK attesters to perform proof verification. - -To construct the `SignedExecutionPayloadHeaderEnvelope` from an existing -`SignedExecutionPayloadEnvelope`: - -1. Set `header_envelope.payload` to the `ExecutionPayloadHeader` derived from - `envelope.payload`. -2. Copy all other fields from the original envelope: - - `header_envelope.execution_requests = envelope.execution_requests` - - `header_envelope.builder_index = envelope.builder_index` - - `header_envelope.beacon_block_root = envelope.beacon_block_root` - - `header_envelope.slot = envelope.slot` - - `header_envelope.blob_kzg_commitments = envelope.blob_kzg_commitments` - - `header_envelope.state_root = envelope.state_root` -3. Set `signed_header_envelope.message = header_envelope`. -4. Set `signed_header_envelope.signature = signed_envelope.signature`. - -Then the builder broadcasts `signed_header_envelope` on the -`signed_execution_payload_header_envelope` global gossip topic. - -## Execution proof signature - -```python -def get_execution_proof_signature( - state: BeaconState, proof: ExecutionProof, privkey: int -) -> BLSSignature: - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof, domain) - return bls.Sign(privkey, signing_root) -``` - -## Constructing the `BuilderSignedExecutionProof` - -After producing an `ExecutionPayloadEnvelope` the builder constructs a set of -`BuilderSignedExecutionProof` as follows: - -1. Extract the `NewPayloadRequest` from the envelope. -2. Select proof types and create `ProofAttributes`. -3. Call - `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` - to initiate proof generation. -4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated - proofs. -5. For each `ExecutionProof` in `proofs`: - - Set `signed_proof.message` to the `ExecutionProof`. - - Set `signed_proof.builder_index` to the builder's `BuilderIndex`. - - Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. - - Broadcast the `BuilderSignedExecutionProof` on the `execution_proof` gossip - topic. diff --git a/specs/_features/eip8025/fork-choice.md b/specs/_features/eip8025/fork-choice.md deleted file mode 100644 index f5caafda27..0000000000 --- a/specs/_features/eip8025/fork-choice.md +++ /dev/null @@ -1,78 +0,0 @@ -# EIP-8025 -- Fork Choice - -*Note*: This document is a work-in-progress for researchers and implementers. - - - -- [Introduction](#introduction) -- [Handlers](#handlers) - - [Modified `on_execution_payload`](#modified-on_execution_payload) - - [New `on_execution_payload_header`](#new-on_execution_payload_header) - - - -## Introduction - -This is the modification of the fork choice accompanying the EIP-8025 upgrade, -enabling stateless validation of execution payloads through cryptographic -proofs. - -*Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md). - -## Handlers - -### Modified `on_execution_payload` - -*Note*: `on_execution_payload` is modified in EIP-8025 to include `PROOF_ENGINE` -in the call to `process_execution_payload`. - -```python -def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None: - """ - Run ``on_execution_payload`` upon receiving a new execution payload. - """ - envelope = signed_envelope.message - # The corresponding beacon block root needs to be known - assert envelope.beacon_block_root in store.block_states - - # Check if blob data is available - # If not, this payload MAY be queued and subsequently considered when blob data becomes available - assert is_data_available(envelope.beacon_block_root) - - # Make a copy of the state to avoid mutability issues - state = copy(store.block_states[envelope.beacon_block_root]) - - # Process the execution payload - # [Modified in EIP-8025] Added PROOF_ENGINE parameter - process_execution_payload(state, signed_envelope, EXECUTION_ENGINE, PROOF_ENGINE) - - # Add new state for this payload to the store - store.execution_payload_states[envelope.beacon_block_root] = state -``` - -### New `on_execution_payload_header` - -```python -def on_execution_payload_header( - store: Store, signed_envelope: SignedExecutionPayloadHeaderEnvelope -) -> None: - """ - Run ``on_execution_payload_header`` upon receiving a new execution payload header. - """ - envelope = signed_envelope.message - # The corresponding beacon block root needs to be known - assert envelope.beacon_block_root in store.block_states - - # Check if blob data is available - # If not, this payload MAY be queued and subsequently considered when blob data becomes available - assert is_data_available(envelope.beacon_block_root) - - # Make a copy of the state to avoid mutability issues - state = copy(store.block_states[envelope.beacon_block_root]) - - # Process the execution payload header - process_execution_payload_header(state, signed_envelope, PROOF_ENGINE) - - # Add new state for this payload to the store - store.execution_payload_states[envelope.beacon_block_root] = state -``` diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 0247a1d5e0..c04f692b05 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -2,18 +2,17 @@ This document contains the networking specifications for EIP-8025. -*Note*: This specification is built upon [Gloas](../../gloas/p2p-interface.md). +*Note*: This specification is built upon [Fulu](../../fulu/p2p-interface.md) and +imports proof types from [proof-engine.md](./proof-engine.md). ## Table of contents - [Table of contents](#table-of-contents) -- [Containers](#containers) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) - - [`signed_execution_payload_header_envelope`](#signed_execution_payload_header_envelope) - [`execution_proof`](#execution_proof) - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) @@ -21,80 +20,29 @@ This document contains the networking specifications for EIP-8025. -## Containers - -*Note*: Execution proofs are broadcast as either `BuilderSignedExecutionProof` -or `ProverSignedExecutionProof` containers on the same topic. - - - -```python -SignedExecutionProof = Union[BuilderSignedExecutionProof, ProverSignedExecutionProof] -``` - ## The gossip domain: gossipsub ### Topics and messages #### Global topics -##### `signed_execution_payload_header_envelope` - -This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. -Provers subscribe to this topic to receive execution payload headers for which -they can generate execution proofs. - -The following validations MUST pass before forwarding the -`signed_execution_payload_header_envelope` on the network, assuming the alias -`envelope = signed_execution_payload_header_envelope.message`, -`payload = envelope.payload`: - -- _[IGNORE]_ The envelope's block root `envelope.beacon_block_root` has been - seen (via gossip or non-gossip sources) (a client MAY queue headers for - processing once the block is retrieved). -- _[IGNORE]_ The node has not seen another valid - `SignedExecutionPayloadHeaderEnvelope` for this block root from this builder. -- _[IGNORE]_ The envelope is from a slot greater than or equal to the latest - finalized slot -- i.e. validate that - `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)` - -Let `block` be the block with `envelope.beacon_block_root`. Let `bid` alias -`block.body.signed_execution_payload_bid.message`. - -- _[REJECT]_ `block` passes validation. -- _[REJECT]_ `block.slot` equals `envelope.slot`. -- _[REJECT]_ `envelope.builder_index == bid.builder_index` -- _[REJECT]_ `payload.block_hash == bid.block_hash` -- _[REJECT]_ `signed_execution_payload_header_envelope.signature` is valid with - respect to the builder's public key. - ##### `execution_proof` -This topic is used to propagate execution proofs. Both -`BuilderSignedExecutionProof` and `ProverSignedExecutionProof` messages are -propagated on this topic. +This topic is used to propagate `ProverSignedExecutionProof` messages. The following validations MUST pass before forwarding a proof on the network: - _[IGNORE]_ The proof's corresponding new payload request (identified by - `proof.message.public_inputs.new_payload_request_root`) has been seen (via + `proof.message.public_input.new_payload_request_root`) has been seen (via gossip or non-gossip sources) (a client MAY queue proofs for processing once the new payload request is retrieved). - _[IGNORE]_ The proof is the first valid proof received for the tuple - `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, signer)`. - -For `BuilderSignedExecutionProof`: - -- _[REJECT]_ The `builder_index` is within the known builder registry. -- _[REJECT]_ The signature is valid with respect to the builder's public key. + `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, prover_pubkey)`. For `ProverSignedExecutionProof`: - _[REJECT]_ The `prover_pubkey` is in the prover whitelist. - _[REJECT]_ The signature is valid with respect to the prover's public key. - -For both types: - - _[REJECT]_ The `proof.message.proof_data` is non-empty. - _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. - _[REJECT]_ The execution proof is valid as verified by the appropriate @@ -123,7 +71,7 @@ Response Content: ``` ( - List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] + List[ProverSignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] ) ``` diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index 82307593f6..ac4fa3b770 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -8,31 +8,30 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) -- [Prerequisites](#prerequisites) -- [Execution proof signature](#execution-proof-signature) -- [Constructing the `ProverSignedExecutionProof`](#constructing-the-proversignedexecutionproof) -- [Honest Prover Relay](#honest-prover-relay) - - [Accepting proofs](#accepting-proofs) - - [Signing and broadcasting](#signing-and-broadcasting) +- [Helpers](#helpers) + - [`get_execution_proof_signature`](#get_execution_proof_signature) +- [Constructing `ProverSignedExecutionProof`](#constructing-proversignedexecutionproof) +- [Honest prover relay](#honest-prover-relay) ## Introduction This document represents the prover guide accompanying EIP-8025. Provers are -whitelisted network operators who generate execution proofs during the optional -proof phase. +whitelisted network participants who voluntarily generate and submit execution +proofs without direct protocol-level compensation. They provide a public good by +enabling stateless validation during the optional proof phase. -## Prerequisites +*Note*: Provers are a transitional mechanism. In future mandatory proof forks, +builders will be required to produce and gossip execution proofs as part of +their block production duties, and the prover role will be deprecated. -All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this -document. +*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and +imports proof types from [proof-engine.md](./proof-engine.md). -The prover MUST have their public key included in `WHITELISTED_PROVERS` or -alternatively use a whitelisted community proof relay. +## Helpers -## Execution proof signature +### `get_execution_proof_signature` ```python def get_execution_proof_signature( @@ -43,59 +42,44 @@ def get_execution_proof_signature( return bls.Sign(privkey, signing_root) ``` -## Constructing the `ProverSignedExecutionProof` +## Constructing `ProverSignedExecutionProof` -Provers subscribe to the `signed_execution_payload_envelope` gossip topic -(defined in [Gloas](../../gloas/p2p-interface.md)) to receive execution payloads -for which they can generate execution proofs. +An honest prover who has been whitelisted and wants to generate execution proofs +for a `BeaconBlockBody` performs the following steps: -To construct a `ProverSignedExecutionProof`: - -1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. -2. Select proof types and create `ProofAttributes`. +1. Extract `NewPayloadRequest` from `BeaconBlockBody`: + - `execution_payload = body.execution_payload` + - `versioned_hashes = [kzg_commitment_to_versioned_hash(c) for c in body.blob_kzg_commitments]` + - `parent_beacon_block_root = state.latest_block_header.parent_root` + - `execution_requests = body.execution_requests` +2. Create `ProofAttributes` with desired proof types. 3. Call `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` to initiate proof generation. -4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated +4. Call `proof_engine.get_proofs(proof_gen_id)` to retrieve the generated proofs. -5. For each `ExecutionProof` in `proofs`: - - Set `signed_proof.message` to the `ExecutionProof`. - - Set `signed_proof.prover_pubkey` to the prover's public key. - - Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. +5. For each `ExecutionProof` in the returned list: + - Set `message` to the `ExecutionProof`. + - Set `prover_pubkey` to the prover's public key. + - Sign the proof using + `get_execution_proof_signature(state, proof, prover_privkey)`. - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip topic. -## Honest Prover Relay - -A prover relay is a whitelisted service that accepts execution proofs from -community provers, validates them, signs them, and broadcasts them to the -network. This allows any prover to contribute proofs without needing to be -individually whitelisted. +## Honest prover relay -### Accepting proofs +A prover relay is a trusted intermediary that accepts unsigned execution proofs +from community provers and signs them for broadcast. The relay's public key MUST +be in the prover whitelist. -The relay exposes an API endpoint that accepts unsigned `ExecutionProof` -submissions from community provers. Upon receiving a proof, the relay MUST: +When a prover relay receives an unsigned `ExecutionProof`: -1. Verify the `proof.proof_data` is non-empty. +1. Validate that `proof_data` is non-empty. 2. Verify the execution proof is valid using - `verify_execution_proof(proof, program_bytecode)`. -3. Verify a proof for the same `(new_payload_request_root, proof_type)` has not - already been signed and broadcast. - -If any validation fails, the relay SHOULD reject the submission. - -### Signing and broadcasting - -After successful validation, the relay signs and broadcasts the proof: - -1. Set `signed_proof.message` to the validated `proof`. -2. Set `signed_proof.prover_pubkey` to the relay's whitelisted public key. -3. Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, relay_privkey)`. -4. Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip - topic. - -*Note*: The relay's public key MUST be included in `WHITELISTED_PROVERS`. The -relay takes responsibility for the validity of proofs it signs. + `proof_engine.verify_execution_proof(proof)`. +3. Check the proof is not a duplicate (same `new_payload_request_root`, + `proof_type`). +4. If valid and not a duplicate: + - Create a `ProverSignedExecutionProof` with the relay's public key and + signature. + - Broadcast on the `execution_proof` gossip topic. diff --git a/specs/_features/eip8025/validator.md b/specs/_features/eip8025/validator.md deleted file mode 100644 index 400e153401..0000000000 --- a/specs/_features/eip8025/validator.md +++ /dev/null @@ -1,28 +0,0 @@ -# EIP-8025 -- Honest Validator - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Prerequisites](#prerequisites) - - - -## Introduction - -This document represents the changes to the validator guide accompanying -EIP-8025. - -## Prerequisites - -This document is an extension of the -[Gloas -- Honest Validator](../../gloas/validator.md) guide. All behaviors and -definitions defined in this document, and documents it extends, carry over -unless explicitly noted or overridden. - -*Note*: Execution proof generation is handled by builders and provers. See -[builder.md](./builder.md) and [prover.md](./prover.md). diff --git a/specs/_features/eip8025_fulu/beacon-chain.md b/specs/_features/eip8025_fulu/beacon-chain.md deleted file mode 100644 index e8d88e1162..0000000000 --- a/specs/_features/eip8025_fulu/beacon-chain.md +++ /dev/null @@ -1,227 +0,0 @@ -# EIP-8025 (Fulu) -- The Beacon Chain - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Containers](#containers) - - [New containers](#new-containers) - - [`ProverSignedExecutionProof`](#proversignedexecutionproof) - - [`NewPayloadRequestHeader`](#newpayloadrequestheader) - - [Extended Containers](#extended-containers) - - [`BeaconState`](#beaconstate) -- [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Execution payload processing](#execution-payload-processing) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [Execution proof handlers](#execution-proof-handlers) - - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) - - - -## Introduction - -This document contains the Fulu-specific consensus specs for EIP-8025. This -enables stateless validation of execution payloads through cryptographic proofs. - -*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and -imports proof types from [proof-engine.md](./proof-engine.md). - -## Containers - -### New containers - -#### `ProverSignedExecutionProof` - -```python -class ProverSignedExecutionProof(Container): - message: ExecutionProof - prover_pubkey: BLSPubkey - signature: BLSSignature -``` - -#### `NewPayloadRequestHeader` - -```python -@dataclass -class NewPayloadRequestHeader(object): - execution_payload_header: ExecutionPayloadHeader - versioned_hashes: Sequence[VersionedHash] - parent_beacon_block_root: Root - execution_requests: ExecutionRequests -``` - -### Extended Containers - -#### `BeaconState` - -```python -class BeaconState(Container): - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - # [New in EIP-8025] - prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - latest_execution_payload_header: ExecutionPayloadHeader - next_withdrawal_index: WithdrawalIndex - next_withdrawal_validator_index: ValidatorIndex - historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] - deposit_requests_start_index: uint64 - deposit_balance_to_consume: Gwei - exit_balance_to_consume: Gwei - earliest_exit_epoch: Epoch - consolidation_balance_to_consume: Gwei - earliest_consolidation_epoch: Epoch - pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] - pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] - pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] - proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] -``` - -## Beacon chain state transition function - -### Execution payload processing - -#### Modified `process_execution_payload` - -*Note*: `process_execution_payload` is modified in EIP-8025 to require both -`ExecutionEngine` and `ProofEngine` for validation. - -```python -def process_execution_payload( - state: BeaconState, - body: BeaconBlockBody, - execution_engine: ExecutionEngine, - proof_engine: ProofEngine, -) -> None: - payload = body.execution_payload - - # Verify consistency of the parent hash with respect to the previous execution payload header - assert payload.parent_hash == state.latest_execution_payload_header.block_hash - # Verify prev_randao - assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) - # Verify timestamp - assert payload.timestamp == compute_time_at_slot(state, state.slot) - # Verify commitments are under limit - assert ( - len(body.blob_kzg_commitments) - <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block - ) - - # Compute list of versioned hashes - versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments - ] - - # Verify the execution payload is valid via ExecutionEngine - new_payload_request = NewPayloadRequest( - execution_payload=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=body.execution_requests, - ) - assert execution_engine.verify_and_notify_new_payload(new_payload_request) - - # [New in EIP-8025] Verify via ProofEngine - new_payload_request_header = NewPayloadRequestHeader( - execution_payload_header=ExecutionPayloadHeader( - parent_hash=payload.parent_hash, - fee_recipient=payload.fee_recipient, - state_root=payload.state_root, - receipts_root=payload.receipts_root, - logs_bloom=payload.logs_bloom, - prev_randao=payload.prev_randao, - block_number=payload.block_number, - gas_limit=payload.gas_limit, - gas_used=payload.gas_used, - timestamp=payload.timestamp, - extra_data=payload.extra_data, - base_fee_per_gas=payload.base_fee_per_gas, - block_hash=payload.block_hash, - transactions_root=hash_tree_root(payload.transactions), - withdrawals_root=hash_tree_root(payload.withdrawals), - blob_gas_used=payload.blob_gas_used, - excess_blob_gas=payload.excess_blob_gas, - ), - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=body.execution_requests, - ) - assert proof_engine.verify_new_payload_request_header(new_payload_request_header) - - # Cache execution payload header - state.latest_execution_payload_header = ExecutionPayloadHeader( - parent_hash=payload.parent_hash, - fee_recipient=payload.fee_recipient, - state_root=payload.state_root, - receipts_root=payload.receipts_root, - logs_bloom=payload.logs_bloom, - prev_randao=payload.prev_randao, - block_number=payload.block_number, - gas_limit=payload.gas_limit, - gas_used=payload.gas_used, - timestamp=payload.timestamp, - extra_data=payload.extra_data, - base_fee_per_gas=payload.base_fee_per_gas, - block_hash=payload.block_hash, - transactions_root=hash_tree_root(payload.transactions), - withdrawals_root=hash_tree_root(payload.withdrawals), - blob_gas_used=payload.blob_gas_used, - excess_blob_gas=payload.excess_blob_gas, - ) -``` - -### Execution proof handlers - -*Note*: Proof storage and availability is implementation-dependent, managed by -the `ProofEngine`. Implementations may cache proofs that arrive before their -corresponding payload headers and apply them once the header is received. - -#### New `process_prover_signed_execution_proof` - -```python -def process_prover_signed_execution_proof( - state: BeaconState, - signed_proof: ProverSignedExecutionProof, - proof_engine: ProofEngine, -) -> None: - """ - Handler for ProverSignedExecutionProof. - """ - proof_message = signed_proof.message - prover_pubkey = signed_proof.prover_pubkey - - # Verify prover is whitelisted - assert prover_pubkey in state.prover_whitelist - - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof_message, domain) - assert bls.Verify(prover_pubkey, signing_root, signed_proof.signature) - - # Verify the execution proof - assert proof_engine.verify_execution_proof(proof_message) -``` diff --git a/specs/_features/eip8025_fulu/fork-choice.md b/specs/_features/eip8025_fulu/fork-choice.md deleted file mode 100644 index 724ce219e2..0000000000 --- a/specs/_features/eip8025_fulu/fork-choice.md +++ /dev/null @@ -1,80 +0,0 @@ -# EIP-8025 (Fulu) -- Fork Choice - -*Note*: This document is a work-in-progress for researchers and implementers. - - - -- [Introduction](#introduction) -- [Handlers](#handlers) - - [Modified `on_block`](#modified-on_block) - - - -## Introduction - -This is the modification of the fork choice accompanying the EIP-8025 upgrade -for Fulu, enabling stateless validation of execution payloads through -cryptographic proofs. - -*Note*: This specification is built upon [Fulu](../../fulu/fork-choice.md) and -imports proof types from [proof-engine.md](./proof-engine.md). - -## Handlers - -### Modified `on_block` - -*Note*: `on_block` is modified in EIP-8025 to include `PROOF_ENGINE` in the call -to `process_execution_payload`. - -```python -def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: - """ - Run ``on_block`` upon receiving a new block. - """ - block = signed_block.message - # Parent block must be known - assert block.parent_root in store.block_states - # Make a copy of the state to avoid mutability issues - pre_state = copy(store.block_states[block.parent_root]) - # Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past. - assert get_current_slot(store) >= block.slot - - # Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor) - finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch) - assert block.slot > finalized_slot - # Check block is a descendant of the finalized block at the checkpoint finalized slot - finalized_checkpoint_block = get_checkpoint_block( - store, - block.parent_root, - store.finalized_checkpoint.epoch, - ) - assert store.finalized_checkpoint.root == finalized_checkpoint_block - - # Check the block is valid and compute the post-state - state = pre_state.copy() - block_root = hash_tree_root(block) - # [Modified in EIP-8025] Pass PROOF_ENGINE to state_transition - state_transition(state, signed_block, EXECUTION_ENGINE, PROOF_ENGINE, validate_result=True) - - # Add new block to the store - store.blocks[block_root] = block - # Add new state for this block to the store - store.block_states[block_root] = state - - # Add block timeliness to the store - time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT - is_timely = get_current_slot(store) == block.slot and is_before_attesting_interval - store.block_timeliness[block_root] = is_timely - - # Add proposer score boost if the block is timely and not conflicting with an existing block - is_first_block = store.proposer_boost_root == Root() - if is_timely and is_first_block: - store.proposer_boost_root = block_root - - # Update checkpoints in store if necessary - update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint) - - # Eagerly compute unrealized justification and finality. - compute_pulled_up_tip(store, block_root) -``` diff --git a/specs/_features/eip8025_fulu/p2p-interface.md b/specs/_features/eip8025_fulu/p2p-interface.md deleted file mode 100644 index 37ef4ad5c2..0000000000 --- a/specs/_features/eip8025_fulu/p2p-interface.md +++ /dev/null @@ -1,90 +0,0 @@ -# EIP-8025 (Fulu) -- Networking - -This document contains the networking specification for EIP-8025 on Fulu. - -*Note*: This specification is built upon [Fulu](../../fulu/p2p-interface.md) and -imports proof types from [proof-engine.md](./proof-engine.md). - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - - [Topics and messages](#topics-and-messages) - - [Global topics](#global-topics) - - [`execution_proof`](#execution_proof) -- [The Req/Resp domain](#the-reqresp-domain) - - [Messages](#messages) - - [ExecutionProofsByRoot](#executionproofsbyroot) - - - -## The gossip domain: gossipsub - -### Topics and messages - -#### Global topics - -##### `execution_proof` - -This topic is used to propagate `ProverSignedExecutionProof` messages. - -The following validations MUST pass before forwarding a proof on the network: - -- _[IGNORE]_ The proof's corresponding new payload request (identified by - `proof.message.public_inputs.new_payload_request_root`) has been seen (via - gossip or non-gossip sources) (a client MAY queue proofs for processing once - the new payload request is retrieved). -- _[IGNORE]_ The proof is the first valid proof received for the tuple - `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, prover_pubkey)`. - -For `ProverSignedExecutionProof`: - -- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. -- _[REJECT]_ The signature is valid with respect to the prover's public key. -- _[REJECT]_ The `proof.message.proof_data` is non-empty. -- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. -- _[REJECT]_ The execution proof is valid as verified by the appropriate - handler. - -## The Req/Resp domain - -### Messages - -#### ExecutionProofsByRoot - -**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` - -The `` field is calculated as -`context = compute_fork_digest(fork_version, genesis_validators_root)`. - -Request Content: - -``` -( - Root -) -``` - -Response Content: - -``` -( - List[ProverSignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] -) -``` - -Requests execution proofs for the given `new_payload_request_root`. The response -MUST contain all available proofs for the requested root, up to -`MAX_EXECUTION_PROOFS_PER_PAYLOAD`. - -The following validations MUST pass: - -- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. - -The response MUST contain: - -- All available execution proofs for the requested `new_payload_request_root`. -- The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` - proofs. diff --git a/specs/_features/eip8025_fulu/proof-engine.md b/specs/_features/eip8025_fulu/proof-engine.md deleted file mode 100644 index d86bf6dae3..0000000000 --- a/specs/_features/eip8025_fulu/proof-engine.md +++ /dev/null @@ -1,155 +0,0 @@ -# EIP-8025 -- Proof Engine - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Constants](#constants) - - [Execution](#execution) - - [Custom types](#custom-types) - - [Domain types](#domain-types) -- [Configuration](#configuration) -- [Containers](#containers) - - [`PublicInput`](#publicinput) - - [`ExecutionProof`](#executionproof) - - [`ProofAttributes`](#proofattributes) -- [Proof engine](#proof-engine) - - [`verify_execution_proof`](#verify_execution_proof) - - [`verify_new_payload_request_header`](#verify_new_payload_request_header) - - [`request_proofs`](#request_proofs) - - [`get_proofs`](#get_proofs) - - - -## Introduction - -This document contains the shared Proof Engine specification for EIP-8025. The -Proof Engine enables stateless validation of execution payloads through -cryptographic proofs. This specification is fork-agnostic and is imported by -both Fulu and Gloas EIP-8025 implementations. - -## Constants - -### Execution - -| Name | Value | -| ---------------------------------- | ------------------- | -| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | -| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | - -### Custom types - -| Name | SSZ equivalent | Description | -| ------------ | -------------- | ---------------------------------------- | -| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | -| `ProofType` | `uint8` | Type identifier for proof system | - -### Domain types - -| Name | Value | -| ------------------------ | -------------------------- | -| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0E000000')` | - -## Configuration - -*Note*: The configuration values are not definitive. - -| Name | Value | -| ------------------------------- | ------------- | -| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | -| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | - -## Containers - -### `PublicInput` - -```python -class PublicInput(Container): - new_payload_request_root: Root -``` - -### `ExecutionProof` - -```python -class ExecutionProof(Container): - proof_data: ByteList[MAX_PROOF_SIZE] - proof_type: ProofType - public_inputs: PublicInput -``` - -### `ProofAttributes` - -```python -@dataclass -class ProofAttributes(object): - proof_types: List[ProofType] -``` - -## Proof engine - -The implementation-dependent `ProofEngine` protocol encapsulates the proof -sub-system logic via: - -- proof generation and verification functions - -The body of these functions are implementation dependent. - -### `verify_execution_proof` - -```python -def verify_execution_proof( - self: ProofEngine, - execution_proof: ExecutionProof, -) -> bool: - """ - Submit and verify an execution proof. - Return ``True`` if proof is valid. - """ - ... -``` - -### `verify_new_payload_request_header` - -```python -def verify_new_payload_request_header( - self: ProofEngine, - new_payload_request_header: NewPayloadRequestHeader, -) -> bool: - """ - Verify that sufficient valid proofs exist for the given payload request header. - Return ``True`` if proof requirements are satisfied. - """ - ... -``` - -### `request_proofs` - -```python -def request_proofs( - self: ProofEngine, - new_payload_request: NewPayloadRequest, - proof_attributes: ProofAttributes, -) -> ProofGenId: - """ - Request proof generation for a payload with specified proof types. - Returns a ``ProofGenId`` to track the generation request. - """ - ... -``` - -### `get_proofs` - -```python -def get_proofs( - self: ProofEngine, - proof_gen_id: ProofGenId, -) -> List[ExecutionProof]: - """ - Retrieve all generated proofs for a generation request. - """ - ... -``` diff --git a/specs/_features/eip8025_fulu/prover.md b/specs/_features/eip8025_fulu/prover.md deleted file mode 100644 index 58f4d49282..0000000000 --- a/specs/_features/eip8025_fulu/prover.md +++ /dev/null @@ -1,78 +0,0 @@ -# EIP-8025 (Fulu) -- Honest Prover - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Helpers](#helpers) - - [`get_execution_proof_signature`](#get_execution_proof_signature) -- [Constructing `ProverSignedExecutionProof`](#constructing-proversignedexecutionproof) -- [Honest prover relay](#honest-prover-relay) - - - -## Introduction - -This document provides guidance for honest provers and prover relays in the -EIP-8025 network on Fulu. - -*Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and -imports proof types from [proof-engine.md](./proof-engine.md). - -## Helpers - -### `get_execution_proof_signature` - -```python -def get_execution_proof_signature( - state: BeaconState, proof: ExecutionProof, privkey: int -) -> BLSSignature: - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof, domain) - return bls.Sign(privkey, signing_root) -``` - -## Constructing `ProverSignedExecutionProof` - -An honest prover who has been whitelisted and wants to generate execution proofs -for a `BeaconBlockBody` performs the following steps: - -1. Extract `NewPayloadRequest` from `BeaconBlockBody`: - - `execution_payload = body.execution_payload` - - `versioned_hashes = [kzg_commitment_to_versioned_hash(c) for c in body.blob_kzg_commitments]` - - `parent_beacon_block_root = state.latest_block_header.parent_root` - - `execution_requests = body.execution_requests` -2. Create `ProofAttributes` with desired proof types. -3. Call `proof_engine.request_proofs(new_payload_request, proof_attributes)` to - initiate proof generation. -4. Call `proof_engine.get_proofs(proof_gen_id)` to retrieve the generated - proofs. -5. For each `ExecutionProof` in the returned list: - - Set `message` to the `ExecutionProof`. - - Set `prover_pubkey` to the prover's public key. - - Sign the proof using - `get_execution_proof_signature(state, proof, prover_privkey)`. - - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip - topic. - -## Honest prover relay - -A prover relay is a trusted intermediary that accepts unsigned execution proofs -from community provers and signs them for broadcast. The relay's public key MUST -be in the prover whitelist. - -When a prover relay receives an unsigned `ExecutionProof`: - -1. Validate that `proof_data` is non-empty. -2. Verify the execution proof is valid using - `proof_engine.verify_execution_proof(proof)`. -3. Check the proof is not a duplicate (same `new_payload_request_root`, - `proof_type`). -4. If valid and not a duplicate: - - Create a `ProverSignedExecutionProof` with the relay's public key and - signature. - - Broadcast on the `execution_proof` gossip topic. diff --git a/specs/_features/eip8025_fulu/validator.md b/specs/_features/eip8025_fulu/validator.md deleted file mode 100644 index fddde14e55..0000000000 --- a/specs/_features/eip8025_fulu/validator.md +++ /dev/null @@ -1,30 +0,0 @@ -# EIP-8025 (Fulu) -- Honest Validator - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Validator behavior](#validator-behavior) - - - -## Introduction - -This document provides guidance for validators in the EIP-8025 network on Fulu. - -*Note*: This specification is built upon [Fulu](../../fulu/validator.md) and -imports proof types from [proof-engine.md](./proof-engine.md). - -## Validator behavior - -In EIP-8025 on Fulu, execution proof generation is handled by whitelisted -provers. Validators inherit all behaviors from the Fulu specification except -where overridden by this document. - -Validators are not required to generate execution proofs themselves. Instead, -they rely on provers (either standalone provers or prover relays) to generate -and broadcast execution proofs for the payloads they produce. diff --git a/specs/_features/eip8025_gloas/beacon-chain.md b/specs/_features/eip8025_gloas/beacon-chain.md index ce6580d5ec..0220acf44c 100644 --- a/specs/_features/eip8025_gloas/beacon-chain.md +++ b/specs/_features/eip8025_gloas/beacon-chain.md @@ -1,4 +1,4 @@ -# EIP-8025 (Gloas) -- The Beacon Chain +# EIP-8025 -- The Beacon Chain *Note*: This document is a work-in-progress for researchers and implementers. @@ -10,66 +10,41 @@ - [Introduction](#introduction) - [Containers](#containers) - [New containers](#new-containers) - - [`BuilderSignedExecutionProof`](#buildersignedexecutionproof) - - [`ProverSignedExecutionProof`](#proversignedexecutionproof) - - [`NewPayloadRequestHeader`](#newpayloadrequestheader) + - [`SignedExecutionProof`](#signedexecutionproof) - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) - - [Extended Containers](#extended-containers) - - [`BeaconState`](#beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution payload processing](#execution-payload-processing) - [Modified `process_execution_payload`](#modified-process_execution_payload) - [New `verify_execution_payload_header_envelope_signature`](#new-verify_execution_payload_header_envelope_signature) - [New `process_execution_payload_header`](#new-process_execution_payload_header) - [Execution proof handlers](#execution-proof-handlers) - - [New `process_builder_signed_execution_proof`](#new-process_builder_signed_execution_proof) - - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) + - [New `process_signed_execution_proof`](#new-process_signed_execution_proof) ## Introduction -This document contains the Gloas-specific consensus specs for EIP-8025. This -enables stateless validation of execution payloads through cryptographic proofs. +This document contains the consensus specs for EIP-8025, enabling stateless +validation of execution payloads through execution proofs. *Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md) and imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). +[eip8025/proof-engine.md](../eip8025/proof-engine.md). ## Containers ### New containers -#### `BuilderSignedExecutionProof` +#### `SignedExecutionProof` ```python -class BuilderSignedExecutionProof(Container): +class SignedExecutionProof(Container): message: ExecutionProof builder_index: BuilderIndex signature: BLSSignature ``` -#### `ProverSignedExecutionProof` - -```python -class ProverSignedExecutionProof(Container): - message: ExecutionProof - prover_pubkey: BLSPubkey - signature: BLSSignature -``` - -#### `NewPayloadRequestHeader` - -```python -@dataclass -class NewPayloadRequestHeader(object): - execution_payload_header: ExecutionPayloadHeader - versioned_hashes: Sequence[VersionedHash] - parent_beacon_block_root: Root - execution_requests: ExecutionRequests -``` - #### `ExecutionPayloadHeaderEnvelope` ```python @@ -91,73 +66,6 @@ class SignedExecutionPayloadHeaderEnvelope(Container): signature: BLSSignature ``` -### Extended Containers - -#### `BeaconState` - -```python -class BeaconState(Container): - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - # [New in EIP-8025] - prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - # [Modified in Gloas:EIP7732] - # Removed `latest_execution_payload_header` - # [New in Gloas:EIP7732] - latest_execution_payload_bid: ExecutionPayloadBid - next_withdrawal_index: WithdrawalIndex - next_withdrawal_validator_index: ValidatorIndex - historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] - deposit_requests_start_index: uint64 - deposit_balance_to_consume: Gwei - exit_balance_to_consume: Gwei - earliest_exit_epoch: Epoch - consolidation_balance_to_consume: Gwei - earliest_consolidation_epoch: Epoch - pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] - pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] - pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] - proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] - # [New in Gloas:EIP7732] - builders: List[Builder, BUILDER_REGISTRY_LIMIT] - # [New in Gloas:EIP7732] - next_withdrawal_builder_index: BuilderIndex - # [New in Gloas:EIP7732] - execution_payload_availability: Bitvector[SLOTS_PER_HISTORICAL_ROOT] - # [New in Gloas:EIP7732] - builder_pending_payments: Vector[BuilderPendingPayment, 2 * SLOTS_PER_EPOCH] - # [New in Gloas:EIP7732] - builder_pending_withdrawals: List[BuilderPendingWithdrawal, BUILDER_PENDING_WITHDRAWALS_LIMIT] - # [New in Gloas:EIP7732] - latest_block_hash: Hash32 - # [New in Gloas:EIP7732] - payload_expected_withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] -``` - -*Note*: `BeaconBlockBody` remains unchanged. - ## Beacon chain state transition function ### Execution payload processing @@ -213,7 +121,7 @@ def process_execution_payload( len(envelope.blob_kzg_commitments) <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block ) - # Verify the execution payload is valid + # Verify the execution payload is valid via ExecutionEngine versioned_hashes = [ kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments ] @@ -393,20 +301,18 @@ def process_execution_payload_header( ### Execution proof handlers -*Note*: Proof storage and availability is implementation-dependent, managed by -the `ProofEngine`. Implementations may cache proofs that arrive before their -corresponding payload headers and apply them once the header is received. +*Note*: Proof storage is implementation-dependent, managed by the `ProofEngine`. -#### New `process_builder_signed_execution_proof` +#### New `process_signed_execution_proof` ```python -def process_builder_signed_execution_proof( +def process_signed_execution_proof( state: BeaconState, - signed_proof: BuilderSignedExecutionProof, + signed_proof: SignedExecutionProof, proof_engine: ProofEngine, ) -> None: """ - Handler for BuilderSignedExecutionProof. + Handler for SignedExecutionProof. """ proof_message = signed_proof.message builder_index = signed_proof.builder_index @@ -425,28 +331,3 @@ def process_builder_signed_execution_proof( # Verify the execution proof assert proof_engine.verify_execution_proof(proof_message) ``` - -#### New `process_prover_signed_execution_proof` - -```python -def process_prover_signed_execution_proof( - state: BeaconState, - signed_proof: ProverSignedExecutionProof, - proof_engine: ProofEngine, -) -> None: - """ - Handler for ProverSignedExecutionProof. - """ - proof_message = signed_proof.message - prover_pubkey = signed_proof.prover_pubkey - - # Verify prover is whitelisted - assert prover_pubkey in state.prover_whitelist - - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof_message, domain) - assert bls.Verify(prover_pubkey, signing_root, signed_proof.signature) - - # Verify the execution proof - assert proof_engine.verify_execution_proof(proof_message) -``` diff --git a/specs/_features/eip8025_gloas/builder.md b/specs/_features/eip8025_gloas/builder.md index a859a6b4aa..eea8624f31 100644 --- a/specs/_features/eip8025_gloas/builder.md +++ b/specs/_features/eip8025_gloas/builder.md @@ -1,4 +1,4 @@ -# EIP-8025 (Gloas) -- Honest Builder +# EIP-8025 -- Honest Builder *Note*: This document is a work-in-progress for researchers and implementers. @@ -11,17 +11,16 @@ - [Prerequisites](#prerequisites) - [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) - [Execution proof signature](#execution-proof-signature) -- [Constructing the `BuilderSignedExecutionProof`](#constructing-the-buildersignedexecutionproof) +- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) ## Introduction -This document represents the changes to the builder guide accompanying EIP-8025 -on Gloas. +This document represents the changes to the builder guide accompanying EIP-8025. *Note*: This specification imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). +[eip8025/proof-engine.md](../eip8025/proof-engine.md). ## Prerequisites @@ -31,8 +30,8 @@ definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 (Gloas) -- Beacon Chain](./beacon-chain.md) document are requisite for -this document. +[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this +document. ## Constructing the `SignedExecutionPayloadHeaderEnvelope` @@ -68,10 +67,10 @@ def get_execution_proof_signature( return bls.Sign(privkey, signing_root) ``` -## Constructing the `BuilderSignedExecutionProof` +## Constructing the `SignedExecutionProof` After producing an `ExecutionPayloadEnvelope` the builder constructs a set of -`BuilderSignedExecutionProof` as follows: +`SignedExecutionProof` as follows: 1. Extract the `NewPayloadRequest` from the envelope. 2. Select proof types and create `ProofAttributes`. @@ -85,5 +84,4 @@ After producing an `ExecutionPayloadEnvelope` the builder constructs a set of - Set `signed_proof.builder_index` to the builder's `BuilderIndex`. - Set `signed_proof.signature` to the result of `get_execution_proof_signature(state, proof, privkey)`. - - Broadcast the `BuilderSignedExecutionProof` on the `execution_proof` gossip - topic. + - Broadcast the `SignedExecutionProof` on the `execution_proof` gossip topic. diff --git a/specs/_features/eip8025_gloas/fork-choice.md b/specs/_features/eip8025_gloas/fork-choice.md index 186987e6e1..7de935bdb7 100644 --- a/specs/_features/eip8025_gloas/fork-choice.md +++ b/specs/_features/eip8025_gloas/fork-choice.md @@ -1,4 +1,4 @@ -# EIP-8025 (Gloas) -- Fork Choice +# EIP-8025 -- Fork Choice *Note*: This document is a work-in-progress for researchers and implementers. @@ -13,13 +13,11 @@ ## Introduction -This is the modification of the fork choice accompanying the EIP-8025 upgrade -for Gloas, enabling stateless validation of execution payloads through -cryptographic proofs. +This is the modification of the fork choice accompanying the EIP-8025 upgrade, +enabling stateless validation of execution payloads through execution proofs. *Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md) and -imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). +imports proof types from [eip8025/proof-engine.md](../eip8025/proof-engine.md). ## Handlers diff --git a/specs/_features/eip8025_gloas/p2p-interface.md b/specs/_features/eip8025_gloas/p2p-interface.md index 33875ccb84..5051b3b0bb 100644 --- a/specs/_features/eip8025_gloas/p2p-interface.md +++ b/specs/_features/eip8025_gloas/p2p-interface.md @@ -1,17 +1,16 @@ -# EIP-8025 (Gloas) -- Networking +# EIP-8025 -- Networking -This document contains the networking specification for EIP-8025 on Gloas. +This document contains the networking specification for EIP-8025. *Note*: This specification is built upon [Gloas](../../gloas/p2p-interface.md) and imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). +[eip8025/proof-engine.md](../eip8025/proof-engine.md). ## Table of contents - [Table of contents](#table-of-contents) -- [Containers](#containers) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) @@ -23,17 +22,6 @@ and imports proof types from -## Containers - -*Note*: Execution proofs are broadcast as either `BuilderSignedExecutionProof` -or `ProverSignedExecutionProof` containers on the same topic. - - - -```python -SignedExecutionProof = Union[BuilderSignedExecutionProof, ProverSignedExecutionProof] -``` - ## The gossip domain: gossipsub ### Topics and messages @@ -43,8 +31,6 @@ SignedExecutionProof = Union[BuilderSignedExecutionProof, ProverSignedExecutionP ##### `signed_execution_payload_header_envelope` This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. -Provers subscribe to this topic to receive execution payload headers for which -they can generate execution proofs. The following validations MUST pass before forwarding the `signed_execution_payload_header_envelope` on the network, assuming the alias @@ -72,31 +58,21 @@ Let `block` be the block with `envelope.beacon_block_root`. Let `bid` alias ##### `execution_proof` -This topic is used to propagate execution proofs. Both -`BuilderSignedExecutionProof` and `ProverSignedExecutionProof` messages are -propagated on this topic. +This topic is used to propagate `SignedExecutionProof` messages. The following validations MUST pass before forwarding a proof on the network: - _[IGNORE]_ The proof's corresponding new payload request (identified by - `proof.message.public_inputs.new_payload_request_root`) has been seen (via + `proof.message.public_input.new_payload_request_root`) has been seen (via gossip or non-gossip sources) (a client MAY queue proofs for processing once the new payload request is retrieved). - _[IGNORE]_ The proof is the first valid proof received for the tuple - `(proof.message.public_inputs.new_payload_request_root, proof.message.proof_type, signer)`. + `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, builder_index)`. -For `BuilderSignedExecutionProof`: +For `SignedExecutionProof`: - _[REJECT]_ The `builder_index` is within the known builder registry. - _[REJECT]_ The signature is valid with respect to the builder's public key. - -For `ProverSignedExecutionProof`: - -- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. -- _[REJECT]_ The signature is valid with respect to the prover's public key. - -For both types: - - _[REJECT]_ The `proof.message.proof_data` is non-empty. - _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. - _[REJECT]_ The execution proof is valid as verified by the appropriate diff --git a/specs/_features/eip8025_gloas/prover.md b/specs/_features/eip8025_gloas/prover.md deleted file mode 100644 index d4951e75b8..0000000000 --- a/specs/_features/eip8025_gloas/prover.md +++ /dev/null @@ -1,104 +0,0 @@ -# EIP-8025 (Gloas) -- Honest Prover - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Prerequisites](#prerequisites) -- [Execution proof signature](#execution-proof-signature) -- [Constructing the `ProverSignedExecutionProof`](#constructing-the-proversignedexecutionproof) -- [Honest Prover Relay](#honest-prover-relay) - - [Accepting proofs](#accepting-proofs) - - [Signing and broadcasting](#signing-and-broadcasting) - - - -## Introduction - -This document represents the prover guide accompanying EIP-8025 on Gloas. -Provers are whitelisted network operators who generate execution proofs during -the optional proof phase. - -*Note*: This specification imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). - -## Prerequisites - -All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 (Gloas) -- Beacon Chain](./beacon-chain.md) document are requisite for -this document. - -The prover MUST have their public key included in `WHITELISTED_PROVERS` or -alternatively use a whitelisted community proof relay. - -## Execution proof signature - -```python -def get_execution_proof_signature( - state: BeaconState, proof: ExecutionProof, privkey: int -) -> BLSSignature: - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof, domain) - return bls.Sign(privkey, signing_root) -``` - -## Constructing the `ProverSignedExecutionProof` - -Provers subscribe to the `signed_execution_payload_envelope` gossip topic -(defined in [Gloas](../../gloas/p2p-interface.md)) to receive execution payloads -for which they can generate execution proofs. - -To construct a `ProverSignedExecutionProof`: - -1. Extract the `NewPayloadRequest` from the `SignedExecutionPayloadEnvelope`. -2. Select proof types and create `ProofAttributes`. -3. Call - `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` - to initiate proof generation. -4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated - proofs. -5. For each `ExecutionProof` in `proofs`: - - Set `signed_proof.message` to the `ExecutionProof`. - - Set `signed_proof.prover_pubkey` to the prover's public key. - - Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. - - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip - topic. - -## Honest Prover Relay - -A prover relay is a whitelisted service that accepts execution proofs from -community provers, validates them, signs them, and broadcasts them to the -network. This allows any prover to contribute proofs without needing to be -individually whitelisted. - -### Accepting proofs - -The relay exposes an API endpoint that accepts unsigned `ExecutionProof` -submissions from community provers. Upon receiving a proof, the relay MUST: - -1. Verify the `proof.proof_data` is non-empty. -2. Verify the execution proof is valid using - `verify_execution_proof(proof, program_bytecode)`. -3. Verify a proof for the same `(new_payload_request_root, proof_type)` has not - already been signed and broadcast. - -If any validation fails, the relay SHOULD reject the submission. - -### Signing and broadcasting - -After successful validation, the relay signs and broadcasts the proof: - -1. Set `signed_proof.message` to the validated `proof`. -2. Set `signed_proof.prover_pubkey` to the relay's whitelisted public key. -3. Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, relay_privkey)`. -4. Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip - topic. - -*Note*: The relay's public key MUST be included in `WHITELISTED_PROVERS`. The -relay takes responsibility for the validity of proofs it signs. diff --git a/specs/_features/eip8025_gloas/validator.md b/specs/_features/eip8025_gloas/validator.md deleted file mode 100644 index 19fe7d0ab0..0000000000 --- a/specs/_features/eip8025_gloas/validator.md +++ /dev/null @@ -1,31 +0,0 @@ -# EIP-8025 (Gloas) -- Honest Validator - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Validator behavior](#validator-behavior) - - - -## Introduction - -This document provides guidance for validators in the EIP-8025 network on Gloas. - -*Note*: This specification is built upon [Gloas](../../gloas/validator.md) and -imports proof types from -[eip8025_fulu/proof-engine.md](../eip8025_fulu/proof-engine.md). - -## Validator behavior - -In EIP-8025 on Gloas, execution proof generation is handled by builders and -whitelisted provers. Validators inherit all behaviors from the Gloas -specification except where overridden by this document. - -Validators are not required to generate execution proofs themselves. Instead, -they rely on builders (who produce the execution payloads) and provers (either -standalone provers or prover relays) to generate and broadcast execution proofs. From 089b17d96eba09bee6d4b3bb13f5529b04530bbb Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 21:53:12 +0000 Subject: [PATCH 10/28] add proof engine --- specs/_features/eip8025/proof-engine.md | 151 ++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 specs/_features/eip8025/proof-engine.md diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md new file mode 100644 index 0000000000..7da0c26c80 --- /dev/null +++ b/specs/_features/eip8025/proof-engine.md @@ -0,0 +1,151 @@ +# EIP-8025 -- Proof Engine + +*Note*: This document is a work-in-progress for researchers and implementers. + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Introduction](#introduction) +- [Constants](#constants) + - [Execution](#execution) + - [Custom types](#custom-types) + - [Domain types](#domain-types) +- [Configuration](#configuration) +- [Containers](#containers) + - [`PublicInput`](#publicinput) + - [`ExecutionProof`](#executionproof) + - [`ProofAttributes`](#proofattributes) +- [Proof engine](#proof-engine) + - [`verify_execution_proof`](#verify_execution_proof) + - [`verify_new_payload_request_header`](#verify_new_payload_request_header) + - [`request_proofs`](#request_proofs) + - [`get_proofs`](#get_proofs) + + + +## Introduction + +This document contains the Proof Engine specification. The Proof Engine enables +stateless validation of execution payloads through execution proofs. + +## Constants + +### Execution + +| Name | Value | +| ---------------------------------- | ------------------- | +| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | +| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | + +### Custom types + +| Name | SSZ equivalent | Description | +| ------------ | -------------- | ---------------------------------------- | +| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | +| `ProofType` | `uint8` | Type type identifier for the proof | + +### Domain types + +| Name | Value | +| ------------------------ | -------------------------- | +| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0D000000')` | + +## Configuration + +*Note*: The configuration values are not definitive. + +| Name | Value | +| ------------------------------- | ------------- | +| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | +| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | + +## Containers + +### `PublicInput` + +```python +class PublicInput(Container): + new_payload_request_root: Root +``` + +### `ExecutionProof` + +```python +class ExecutionProof(Container): + proof_data: ByteList[MAX_PROOF_SIZE] + proof_type: ProofType + public_input: PublicInput +``` + +### `ProofAttributes` + +```python +@dataclass +class ProofAttributes(object): + proof_types: List[ProofType] +``` + +## Proof engine + +The implementation-dependent `ProofEngine` protocol encapsulates the proof +sub-system logic via proof verification and generation functions. + +The body of these functions are implementation dependent. + +### `verify_execution_proof` + +```python +def verify_execution_proof( + self: ProofEngine, + execution_proof: ExecutionProof, +) -> bool: + """ + Verify an execution proof. + Return ``True`` if proof is valid. + """ + ... +``` + +### `verify_new_payload_request_header` + +```python +def verify_new_payload_request_header( + self: ProofEngine, + new_payload_request_header: NewPayloadRequestHeader, +) -> bool: + """ + Verify the given payload request header. + Return ``True`` if proof requirements are satisfied. + """ + ... +``` + +### `request_proofs` + +```python +def request_proofs( + self: ProofEngine, + new_payload_request: NewPayloadRequest, + proof_attributes: ProofAttributes, +) -> ProofGenId: + """ + Request proof generation for a new payload request with specified proof attributes. + Returns a ``ProofGenId`` to track the generation request. + """ + ... +``` + +### `get_proofs` + +```python +def get_proofs( + self: ProofEngine, + proof_gen_id: ProofGenId, +) -> List[ExecutionProof]: + """ + Retrieve all generated proofs for a generation request. + """ + ... +``` From 401b25f7315bd6c18dab132823dbe6385343df4b Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 21:56:03 +0000 Subject: [PATCH 11/28] remove eip8025 gloas --- specs/_features/eip8025_gloas/beacon-chain.md | 333 ------------------ specs/_features/eip8025_gloas/builder.md | 87 ----- specs/_features/eip8025_gloas/fork-choice.md | 78 ---- .../_features/eip8025_gloas/p2p-interface.md | 120 ------- 4 files changed, 618 deletions(-) delete mode 100644 specs/_features/eip8025_gloas/beacon-chain.md delete mode 100644 specs/_features/eip8025_gloas/builder.md delete mode 100644 specs/_features/eip8025_gloas/fork-choice.md delete mode 100644 specs/_features/eip8025_gloas/p2p-interface.md diff --git a/specs/_features/eip8025_gloas/beacon-chain.md b/specs/_features/eip8025_gloas/beacon-chain.md deleted file mode 100644 index 0220acf44c..0000000000 --- a/specs/_features/eip8025_gloas/beacon-chain.md +++ /dev/null @@ -1,333 +0,0 @@ -# EIP-8025 -- The Beacon Chain - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Containers](#containers) - - [New containers](#new-containers) - - [`SignedExecutionProof`](#signedexecutionproof) - - [`ExecutionPayloadHeaderEnvelope`](#executionpayloadheaderenvelope) - - [`SignedExecutionPayloadHeaderEnvelope`](#signedexecutionpayloadheaderenvelope) -- [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Execution payload processing](#execution-payload-processing) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [New `verify_execution_payload_header_envelope_signature`](#new-verify_execution_payload_header_envelope_signature) - - [New `process_execution_payload_header`](#new-process_execution_payload_header) - - [Execution proof handlers](#execution-proof-handlers) - - [New `process_signed_execution_proof`](#new-process_signed_execution_proof) - - - -## Introduction - -This document contains the consensus specs for EIP-8025, enabling stateless -validation of execution payloads through execution proofs. - -*Note*: This specification is built upon [Gloas](../../gloas/beacon-chain.md) -and imports proof types from -[eip8025/proof-engine.md](../eip8025/proof-engine.md). - -## Containers - -### New containers - -#### `SignedExecutionProof` - -```python -class SignedExecutionProof(Container): - message: ExecutionProof - builder_index: BuilderIndex - signature: BLSSignature -``` - -#### `ExecutionPayloadHeaderEnvelope` - -```python -class ExecutionPayloadHeaderEnvelope(Container): - payload: ExecutionPayloadHeader - execution_requests: ExecutionRequests - builder_index: BuilderIndex - beacon_block_root: Root - slot: Slot - blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK] - state_root: Root -``` - -#### `SignedExecutionPayloadHeaderEnvelope` - -```python -class SignedExecutionPayloadHeaderEnvelope(Container): - message: ExecutionPayloadHeaderEnvelope - signature: BLSSignature -``` - -## Beacon chain state transition function - -### Execution payload processing - -#### Modified `process_execution_payload` - -*Note*: `process_execution_payload` is modified in EIP-8025 to require both -`ExecutionEngine` and `ProofEngine` for validation. - -```python -def process_execution_payload( - state: BeaconState, - signed_envelope: SignedExecutionPayloadEnvelope, - execution_engine: ExecutionEngine, - proof_engine: ProofEngine, - verify: bool = True, -) -> None: - envelope = signed_envelope.message - payload = envelope.payload - - # Verify signature - if verify: - assert verify_execution_payload_envelope_signature(state, signed_envelope) - - # Cache latest block header state root - previous_state_root = hash_tree_root(state) - if state.latest_block_header.state_root == Root(): - state.latest_block_header.state_root = previous_state_root - - # Verify consistency with the beacon block - assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) - assert envelope.slot == state.slot - - # Verify consistency with the committed bid - committed_bid = state.latest_execution_payload_bid - assert envelope.builder_index == committed_bid.builder_index - assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) - assert committed_bid.prev_randao == payload.prev_randao - - # Verify consistency with expected withdrawals - assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals) - - # Verify the gas_limit - assert committed_bid.gas_limit == payload.gas_limit - # Verify the block hash - assert committed_bid.block_hash == payload.block_hash - # Verify consistency of the parent hash with respect to the previous execution payload - assert payload.parent_hash == state.latest_block_hash - # Verify timestamp - assert payload.timestamp == compute_time_at_slot(state, state.slot) - # Verify commitments are under limit - assert ( - len(envelope.blob_kzg_commitments) - <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block - ) - # Verify the execution payload is valid via ExecutionEngine - versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments - ] - requests = envelope.execution_requests - new_payload_request = NewPayloadRequest( - execution_payload=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - assert execution_engine.verify_and_notify_new_payload(new_payload_request) - - # [New in EIP-8025] Verify via ProofEngine - new_payload_request_header = NewPayloadRequestHeader( - execution_payload_header=ExecutionPayloadHeader( - parent_hash=payload.parent_hash, - fee_recipient=payload.fee_recipient, - state_root=payload.state_root, - receipts_root=payload.receipts_root, - logs_bloom=payload.logs_bloom, - prev_randao=payload.prev_randao, - block_number=payload.block_number, - gas_limit=payload.gas_limit, - gas_used=payload.gas_used, - timestamp=payload.timestamp, - extra_data=payload.extra_data, - base_fee_per_gas=payload.base_fee_per_gas, - block_hash=payload.block_hash, - transactions_root=hash_tree_root(payload.transactions), - withdrawals_root=hash_tree_root(payload.withdrawals), - blob_gas_used=payload.blob_gas_used, - excess_blob_gas=payload.excess_blob_gas, - ), - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - assert proof_engine.verify_new_payload_request_header(new_payload_request_header) - - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(requests.deposits, process_deposit_request) - for_ops(requests.withdrawals, process_withdrawal_request) - for_ops(requests.consolidations, process_consolidation_request) - - # Queue the builder payment - payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] - amount = payment.withdrawal.amount - if amount > 0: - state.builder_pending_withdrawals.append(payment.withdrawal) - state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( - BuilderPendingPayment() - ) - - # Cache the execution payload hash - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 - state.latest_block_hash = payload.block_hash - - # Verify the state root - if verify: - assert envelope.state_root == hash_tree_root(state) -``` - -#### New `verify_execution_payload_header_envelope_signature` - -```python -def verify_execution_payload_header_envelope_signature( - state: BeaconState, signed_envelope: SignedExecutionPayloadHeaderEnvelope -) -> bool: - builder_index = signed_envelope.message.builder_index - if builder_index == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey - else: - pubkey = state.builders[builder_index].pubkey - - signing_root = compute_signing_root( - signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER) - ) - return bls.Verify(pubkey, signing_root, signed_envelope.signature) -``` - -#### New `process_execution_payload_header` - -*Note*: `process_execution_payload_header` is the stateless equivalent of -`process_execution_payload`. It processes execution payload headers using -execution proofs for validation instead of the `ExecutionEngine`. - -```python -def process_execution_payload_header( - state: BeaconState, - signed_envelope: SignedExecutionPayloadHeaderEnvelope, - proof_engine: ProofEngine, - verify: bool = True, -) -> None: - envelope = signed_envelope.message - payload = envelope.payload - - # Verify signature - if verify: - assert verify_execution_payload_header_envelope_signature(state, signed_envelope) - - # Cache latest block header state root - previous_state_root = hash_tree_root(state) - if state.latest_block_header.state_root == Root(): - state.latest_block_header.state_root = previous_state_root - - # Verify consistency with the beacon block - assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header) - assert envelope.slot == state.slot - - # Verify consistency with the committed bid - committed_bid = state.latest_execution_payload_bid - assert envelope.builder_index == committed_bid.builder_index - assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments) - assert committed_bid.prev_randao == payload.prev_randao - - # Verify consistency with expected withdrawals - assert payload.withdrawals_root == hash_tree_root(state.payload_expected_withdrawals) - - # Verify the gas_limit - assert committed_bid.gas_limit == payload.gas_limit - # Verify the block hash - assert committed_bid.block_hash == payload.block_hash - # Verify consistency of the parent hash with respect to the previous execution payload - assert payload.parent_hash == state.latest_block_hash - # Verify timestamp - assert payload.timestamp == compute_time_at_slot(state, state.slot) - # Verify commitments are under limit - assert ( - len(envelope.blob_kzg_commitments) - <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block - ) - - # [New in EIP-8025] Verify the execution payload request header using execution proofs - versioned_hashes = [ - kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments - ] - requests = envelope.execution_requests - new_payload_request_header = NewPayloadRequestHeader( - execution_payload_header=payload, - versioned_hashes=versioned_hashes, - parent_beacon_block_root=state.latest_block_header.parent_root, - execution_requests=requests, - ) - - # Verify sufficient proofs exist via ProofEngine - assert proof_engine.verify_new_payload_request_header(new_payload_request_header) - - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(requests.deposits, process_deposit_request) - for_ops(requests.withdrawals, process_withdrawal_request) - for_ops(requests.consolidations, process_consolidation_request) - - # Queue the builder payment - payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] - amount = payment.withdrawal.amount - if amount > 0: - state.builder_pending_withdrawals.append(payment.withdrawal) - state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = ( - BuilderPendingPayment() - ) - - # Cache the execution payload hash - state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1 - state.latest_block_hash = payload.block_hash - - # Verify the state root - if verify: - assert envelope.state_root == hash_tree_root(state) -``` - -### Execution proof handlers - -*Note*: Proof storage is implementation-dependent, managed by the `ProofEngine`. - -#### New `process_signed_execution_proof` - -```python -def process_signed_execution_proof( - state: BeaconState, - signed_proof: SignedExecutionProof, - proof_engine: ProofEngine, -) -> None: - """ - Handler for SignedExecutionProof. - """ - proof_message = signed_proof.message - builder_index = signed_proof.builder_index - - # Determine pubkey based on builder_index - if builder_index == BUILDER_INDEX_SELF_BUILD: - validator_index = state.latest_block_header.proposer_index - pubkey = state.validators[validator_index].pubkey - else: - pubkey = state.builders[builder_index].pubkey - - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof_message, domain) - assert bls.Verify(pubkey, signing_root, signed_proof.signature) - - # Verify the execution proof - assert proof_engine.verify_execution_proof(proof_message) -``` diff --git a/specs/_features/eip8025_gloas/builder.md b/specs/_features/eip8025_gloas/builder.md deleted file mode 100644 index eea8624f31..0000000000 --- a/specs/_features/eip8025_gloas/builder.md +++ /dev/null @@ -1,87 +0,0 @@ -# EIP-8025 -- Honest Builder - -*Note*: This document is a work-in-progress for researchers and implementers. - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [Introduction](#introduction) -- [Prerequisites](#prerequisites) -- [Constructing the `SignedExecutionPayloadHeaderEnvelope`](#constructing-the-signedexecutionpayloadheaderenvelope) -- [Execution proof signature](#execution-proof-signature) -- [Constructing the `SignedExecutionProof`](#constructing-the-signedexecutionproof) - - - -## Introduction - -This document represents the changes to the builder guide accompanying EIP-8025. - -*Note*: This specification imports proof types from -[eip8025/proof-engine.md](../eip8025/proof-engine.md). - -## Prerequisites - -This document is an extension of the -[Gloas -- Honest Builder](../../gloas/builder.md) guide. All behaviors and -definitions defined in this document, and documents it extends, carry over -unless explicitly noted or overridden. - -All terminology, constants, functions, and protocol mechanics defined in the -[EIP-8025 -- Beacon Chain](./beacon-chain.md) document are requisite for this -document. - -## Constructing the `SignedExecutionPayloadHeaderEnvelope` - -Builders MUST broadcast `SignedExecutionPayloadHeaderEnvelope` messages to allow -ZK attesters to perform proof verification. - -To construct the `SignedExecutionPayloadHeaderEnvelope` from an existing -`SignedExecutionPayloadEnvelope`: - -1. Set `header_envelope.payload` to the `ExecutionPayloadHeader` derived from - `envelope.payload`. -2. Copy all other fields from the original envelope: - - `header_envelope.execution_requests = envelope.execution_requests` - - `header_envelope.builder_index = envelope.builder_index` - - `header_envelope.beacon_block_root = envelope.beacon_block_root` - - `header_envelope.slot = envelope.slot` - - `header_envelope.blob_kzg_commitments = envelope.blob_kzg_commitments` - - `header_envelope.state_root = envelope.state_root` -3. Set `signed_header_envelope.message = header_envelope`. -4. Set `signed_header_envelope.signature = signed_envelope.signature`. - -Then the builder broadcasts `signed_header_envelope` on the -`signed_execution_payload_header_envelope` global gossip topic. - -## Execution proof signature - -```python -def get_execution_proof_signature( - state: BeaconState, proof: ExecutionProof, privkey: int -) -> BLSSignature: - domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) - signing_root = compute_signing_root(proof, domain) - return bls.Sign(privkey, signing_root) -``` - -## Constructing the `SignedExecutionProof` - -After producing an `ExecutionPayloadEnvelope` the builder constructs a set of -`SignedExecutionProof` as follows: - -1. Extract the `NewPayloadRequest` from the envelope. -2. Select proof types and create `ProofAttributes`. -3. Call - `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` - to initiate proof generation. -4. Call `proofs = proof_engine.get_proofs(proof_gen_id)` to retrieve generated - proofs. -5. For each `ExecutionProof` in `proofs`: - - Set `signed_proof.message` to the `ExecutionProof`. - - Set `signed_proof.builder_index` to the builder's `BuilderIndex`. - - Set `signed_proof.signature` to the result of - `get_execution_proof_signature(state, proof, privkey)`. - - Broadcast the `SignedExecutionProof` on the `execution_proof` gossip topic. diff --git a/specs/_features/eip8025_gloas/fork-choice.md b/specs/_features/eip8025_gloas/fork-choice.md deleted file mode 100644 index 7de935bdb7..0000000000 --- a/specs/_features/eip8025_gloas/fork-choice.md +++ /dev/null @@ -1,78 +0,0 @@ -# EIP-8025 -- Fork Choice - -*Note*: This document is a work-in-progress for researchers and implementers. - - - -- [Introduction](#introduction) -- [Handlers](#handlers) - - [Modified `on_execution_payload`](#modified-on_execution_payload) - - [New `on_execution_payload_header`](#new-on_execution_payload_header) - - - -## Introduction - -This is the modification of the fork choice accompanying the EIP-8025 upgrade, -enabling stateless validation of execution payloads through execution proofs. - -*Note*: This specification is built upon [Gloas](../../gloas/fork-choice.md) and -imports proof types from [eip8025/proof-engine.md](../eip8025/proof-engine.md). - -## Handlers - -### Modified `on_execution_payload` - -*Note*: `on_execution_payload` is modified in EIP-8025 to include `PROOF_ENGINE` -in the call to `process_execution_payload`. - -```python -def on_execution_payload(store: Store, signed_envelope: SignedExecutionPayloadEnvelope) -> None: - """ - Run ``on_execution_payload`` upon receiving a new execution payload. - """ - envelope = signed_envelope.message - # The corresponding beacon block root needs to be known - assert envelope.beacon_block_root in store.block_states - - # Check if blob data is available - # If not, this payload MAY be queued and subsequently considered when blob data becomes available - assert is_data_available(envelope.beacon_block_root) - - # Make a copy of the state to avoid mutability issues - state = copy(store.block_states[envelope.beacon_block_root]) - - # Process the execution payload - # [Modified in EIP-8025] Added PROOF_ENGINE parameter - process_execution_payload(state, signed_envelope, EXECUTION_ENGINE, PROOF_ENGINE) - - # Add new state for this payload to the store - store.execution_payload_states[envelope.beacon_block_root] = state -``` - -### New `on_execution_payload_header` - -```python -def on_execution_payload_header( - store: Store, signed_envelope: SignedExecutionPayloadHeaderEnvelope -) -> None: - """ - Run ``on_execution_payload_header`` upon receiving a new execution payload header. - """ - envelope = signed_envelope.message - # The corresponding beacon block root needs to be known - assert envelope.beacon_block_root in store.block_states - - # Check if blob data is available - # If not, this payload MAY be queued and subsequently considered when blob data becomes available - assert is_data_available(envelope.beacon_block_root) - - # Make a copy of the state to avoid mutability issues - state = copy(store.block_states[envelope.beacon_block_root]) - - # Process the execution payload header - process_execution_payload_header(state, signed_envelope, PROOF_ENGINE) - - # Add new state for this payload to the store - store.execution_payload_states[envelope.beacon_block_root] = state -``` diff --git a/specs/_features/eip8025_gloas/p2p-interface.md b/specs/_features/eip8025_gloas/p2p-interface.md deleted file mode 100644 index 5051b3b0bb..0000000000 --- a/specs/_features/eip8025_gloas/p2p-interface.md +++ /dev/null @@ -1,120 +0,0 @@ -# EIP-8025 -- Networking - -This document contains the networking specification for EIP-8025. - -*Note*: This specification is built upon [Gloas](../../gloas/p2p-interface.md) -and imports proof types from -[eip8025/proof-engine.md](../eip8025/proof-engine.md). - -## Table of contents - - - -- [Table of contents](#table-of-contents) -- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - - [Topics and messages](#topics-and-messages) - - [Global topics](#global-topics) - - [`signed_execution_payload_header_envelope`](#signed_execution_payload_header_envelope) - - [`execution_proof`](#execution_proof) -- [The Req/Resp domain](#the-reqresp-domain) - - [Messages](#messages) - - [ExecutionProofsByRoot](#executionproofsbyroot) - - - -## The gossip domain: gossipsub - -### Topics and messages - -#### Global topics - -##### `signed_execution_payload_header_envelope` - -This topic is used to propagate `SignedExecutionPayloadHeaderEnvelope` messages. - -The following validations MUST pass before forwarding the -`signed_execution_payload_header_envelope` on the network, assuming the alias -`envelope = signed_execution_payload_header_envelope.message`, -`payload = envelope.payload`: - -- _[IGNORE]_ The envelope's block root `envelope.beacon_block_root` has been - seen (via gossip or non-gossip sources) (a client MAY queue headers for - processing once the block is retrieved). -- _[IGNORE]_ The node has not seen another valid - `SignedExecutionPayloadHeaderEnvelope` for this block root from this builder. -- _[IGNORE]_ The envelope is from a slot greater than or equal to the latest - finalized slot -- i.e. validate that - `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)` - -Let `block` be the block with `envelope.beacon_block_root`. Let `bid` alias -`block.body.signed_execution_payload_bid.message`. - -- _[REJECT]_ `block` passes validation. -- _[REJECT]_ `block.slot` equals `envelope.slot`. -- _[REJECT]_ `envelope.builder_index == bid.builder_index` -- _[REJECT]_ `payload.block_hash == bid.block_hash` -- _[REJECT]_ `signed_execution_payload_header_envelope.signature` is valid with - respect to the builder's public key. - -##### `execution_proof` - -This topic is used to propagate `SignedExecutionProof` messages. - -The following validations MUST pass before forwarding a proof on the network: - -- _[IGNORE]_ The proof's corresponding new payload request (identified by - `proof.message.public_input.new_payload_request_root`) has been seen (via - gossip or non-gossip sources) (a client MAY queue proofs for processing once - the new payload request is retrieved). -- _[IGNORE]_ The proof is the first valid proof received for the tuple - `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, builder_index)`. - -For `SignedExecutionProof`: - -- _[REJECT]_ The `builder_index` is within the known builder registry. -- _[REJECT]_ The signature is valid with respect to the builder's public key. -- _[REJECT]_ The `proof.message.proof_data` is non-empty. -- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. -- _[REJECT]_ The execution proof is valid as verified by the appropriate - handler. - -## The Req/Resp domain - -### Messages - -#### ExecutionProofsByRoot - -**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` - -The `` field is calculated as -`context = compute_fork_digest(fork_version, genesis_validators_root)`. - -Request Content: - -``` -( - Root -) -``` - -Response Content: - -``` -( - List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] -) -``` - -Requests execution proofs for the given `new_payload_request_root`. The response -MUST contain all available proofs for the requested root, up to -`MAX_EXECUTION_PROOFS_PER_PAYLOAD`. - -The following validations MUST pass: - -- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. - -The response MUST contain: - -- All available execution proofs for the requested `new_payload_request_root`. -- The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` - proofs. From b11df6006707703dcb1189506f0e307507ba31d2 Mon Sep 17 00:00:00 2001 From: frisitano Date: Tue, 13 Jan 2026 22:16:40 +0000 Subject: [PATCH 12/28] fix typo --- specs/_features/eip8025/proof-engine.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md index 7da0c26c80..329ee3b415 100644 --- a/specs/_features/eip8025/proof-engine.md +++ b/specs/_features/eip8025/proof-engine.md @@ -44,7 +44,7 @@ stateless validation of execution payloads through execution proofs. | Name | SSZ equivalent | Description | | ------------ | -------------- | ---------------------------------------- | | `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | -| `ProofType` | `uint8` | Type type identifier for the proof | +| `ProofType` | `uint8` | The type identifier for the proof | ### Domain types From aacb0e1497ae6eb9a71ae357edd7bdd71e2c2f81 Mon Sep 17 00:00:00 2001 From: frisitano Date: Wed, 14 Jan 2026 11:48:54 +0000 Subject: [PATCH 13/28] lint --- specs/_features/eip8025/beacon-chain.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 3a70a25627..1a035a31bc 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -101,7 +101,7 @@ class BeaconState(Container): pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] - # [New in EIP-8025] + # [New in EIP8025] prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] ``` @@ -118,7 +118,7 @@ class BeaconState(Container): def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) process_withdrawals(state, block.body.execution_payload) - # [Modified in EIP-8025] + # [Modified in EIP8025] process_execution_payload(state, block.body, EXECUTION_ENGINE, PROOF_ENGINE) process_randao(state, block.body) process_eth1_data(state, block.body) @@ -169,7 +169,8 @@ def process_execution_payload( ) ) - # [New in EIP-8025] Verify via ProofEngine + # [New in EIP8025] + # Verify via ProofEngine new_payload_request_header = NewPayloadRequestHeader( execution_payload_header=ExecutionPayloadHeader( parent_hash=payload.parent_hash, From 35bf6647a3503a402e5cdd12edbf52dc3865f959 Mon Sep 17 00:00:00 2001 From: frisitano Date: Thu, 15 Jan 2026 11:25:22 +0000 Subject: [PATCH 14/28] lint --- specs/_features/eip8025/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 1a035a31bc..3b90805d68 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -169,7 +169,7 @@ def process_execution_payload( ) ) - # [New in EIP8025] + # [New in EIP8025] # Verify via ProofEngine new_payload_request_header = NewPayloadRequestHeader( execution_payload_header=ExecutionPayloadHeader( From 30efa028dd75d3934a69fb466463baf626837702 Mon Sep 17 00:00:00 2001 From: frisitano Date: Thu, 15 Jan 2026 23:10:40 +0000 Subject: [PATCH 15/28] remove get_proofs proof engine method --- specs/_features/eip8025/proof-engine.md | 15 ++------------- specs/_features/eip8025/prover.md | 12 +++++++----- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md index 329ee3b415..4202c6c114 100644 --- a/specs/_features/eip8025/proof-engine.md +++ b/specs/_features/eip8025/proof-engine.md @@ -21,7 +21,6 @@ - [`verify_execution_proof`](#verify_execution_proof) - [`verify_new_payload_request_header`](#verify_new_payload_request_header) - [`request_proofs`](#request_proofs) - - [`get_proofs`](#get_proofs) @@ -133,19 +132,9 @@ def request_proofs( """ Request proof generation for a new payload request with specified proof attributes. Returns a ``ProofGenId`` to track the generation request. - """ - ... -``` - -### `get_proofs` -```python -def get_proofs( - self: ProofEngine, - proof_gen_id: ProofGenId, -) -> List[ExecutionProof]: - """ - Retrieve all generated proofs for a generation request. + Generated proofs are delivered asynchronously via the beacon API endpoint + ``POST /eth/v1/prover/execution_proofs``. """ ... ``` diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index ac4fa3b770..b2f06bafa8 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -56,9 +56,10 @@ for a `BeaconBlockBody` performs the following steps: 3. Call `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` to initiate proof generation. -4. Call `proof_engine.get_proofs(proof_gen_id)` to retrieve the generated - proofs. -5. For each `ExecutionProof` in the returned list: +4. The proof engine generates proofs asynchronously and delivers them to the + prover via `POST /eth/v1/prover/execution_proofs`. +5. Upon receiving each `ExecutionProof`: + - Validate the proof matches a pending `proof_gen_id`. - Set `message` to the `ExecutionProof`. - Set `prover_pubkey` to the prover's public key. - Sign the proof using @@ -69,10 +70,11 @@ for a `BeaconBlockBody` performs the following steps: ## Honest prover relay A prover relay is a trusted intermediary that accepts unsigned execution proofs -from community provers and signs them for broadcast. The relay's public key MUST +from proof engines and signs them for broadcast. The relay's public key MUST be in the prover whitelist. -When a prover relay receives an unsigned `ExecutionProof`: +When a prover relay receives an unsigned `ExecutionProof` via +`POST /eth/v1/prover/execution_proofs`: 1. Validate that `proof_data` is non-empty. 2. Verify the execution proof is valid using From 4d45977f2553381e70207f4b5d57615b09b07827 Mon Sep 17 00:00:00 2001 From: frisitano Date: Thu, 15 Jan 2026 23:21:26 +0000 Subject: [PATCH 16/28] lint --- specs/_features/eip8025/prover.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index b2f06bafa8..1008e6242b 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -70,8 +70,8 @@ for a `BeaconBlockBody` performs the following steps: ## Honest prover relay A prover relay is a trusted intermediary that accepts unsigned execution proofs -from proof engines and signs them for broadcast. The relay's public key MUST -be in the prover whitelist. +from proof engines and signs them for broadcast. The relay's public key MUST be +in the prover whitelist. When a prover relay receives an unsigned `ExecutionProof` via `POST /eth/v1/prover/execution_proofs`: From 4885119079f823453c54abadb8c4e9cfc8323d75 Mon Sep 17 00:00:00 2001 From: frisitano Date: Sat, 17 Jan 2026 17:10:31 +0000 Subject: [PATCH 17/28] address PR comments --- specs/_features/eip8025/p2p-interface.md | 12 +++++------ specs/_features/eip8025/proof-engine.md | 26 +++++++++++++++++------- specs/_features/eip8025/prover.md | 5 +++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index c04f692b05..e7a835f813 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -54,7 +54,7 @@ For `ProverSignedExecutionProof`: #### ExecutionProofsByRoot -**Protocol ID:** `/eth2/beacon/req/execution_proofs_by_root/1/` +**Protocol ID:** `/eth2/beacon_chain/req/execution_proofs_by_root/1/` The `` field is calculated as `context = compute_fork_digest(fork_version, genesis_validators_root)`. @@ -63,7 +63,7 @@ Request Content: ``` ( - Root + block_root: Root ) ``` @@ -75,16 +75,16 @@ Response Content: ) ``` -Requests execution proofs for the given `new_payload_request_root`. The response -MUST contain all available proofs for the requested root, up to +Requests execution proofs for the given `block_root`. The response MUST contain +all available proofs for the requested beacon block, up to `MAX_EXECUTION_PROOFS_PER_PAYLOAD`. The following validations MUST pass: -- _[REJECT]_ The `new_payload_request_root` is a 32-byte value. +- _[REJECT]_ The `block_root` is a 32-byte value. The response MUST contain: -- All available execution proofs for the requested `new_payload_request_root`. +- All available execution proofs for the requested `block_root`. - The response MUST NOT contain more than `MAX_EXECUTION_PROOFS_PER_PAYLOAD` proofs. diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md index 4202c6c114..711333df70 100644 --- a/specs/_features/eip8025/proof-engine.md +++ b/specs/_features/eip8025/proof-engine.md @@ -33,6 +33,8 @@ stateless validation of execution payloads through execution proofs. ### Execution +*Note*: The execution values are not definitive. + | Name | Value | | ---------------------------------- | ------------------- | | `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | @@ -55,10 +57,9 @@ stateless validation of execution payloads through execution proofs. *Note*: The configuration values are not definitive. -| Name | Value | -| ------------------------------- | ------------- | -| `MIN_REQUIRED_EXECUTION_PROOFS` | `uint64(1)` | -| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | +| Name | Value | +| ------------------------- | ------------- | +| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | ## Containers @@ -89,9 +90,20 @@ class ProofAttributes(object): ## Proof engine The implementation-dependent `ProofEngine` protocol encapsulates the proof -sub-system logic via proof verification and generation functions. +sub-system logic via: + +- a state object `self.proof_state` of type `ProofState` containing stored + proofs +- a verification function `self.verify_execution_proof` to verify individual + proofs +- a verification function `self.verify_new_payload_request_header` to verify new + payload request headers using stored proofs +- a generation function `self.request_proofs` to initiate asynchronous proof + generation -The body of these functions are implementation dependent. +The body of these functions are implementation dependent. The Engine API may be +used to implement this and similarly defined functions via an external proof +engine. ### `verify_execution_proof` @@ -115,7 +127,7 @@ def verify_new_payload_request_header( new_payload_request_header: NewPayloadRequestHeader, ) -> bool: """ - Verify the given payload request header. + Verify the corresponding new payload request execution is valid. Return ``True`` if proof requirements are satisfied. """ ... diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index 1008e6242b..e11c156fdc 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -57,8 +57,9 @@ for a `BeaconBlockBody` performs the following steps: `proof_gen_id = proof_engine.request_proofs(new_payload_request, proof_attributes)` to initiate proof generation. 4. The proof engine generates proofs asynchronously and delivers them to the - prover via `POST /eth/v1/prover/execution_proofs`. -5. Upon receiving each `ExecutionProof`: + prover via `POST /eth/v1/prover/execution_proofs`. Each proof is delivered + with its associated `proof_gen_id` to link it to the original request. +5. Upon receiving each `ExecutionProof` with its `proof_gen_id`: - Validate the proof matches a pending `proof_gen_id`. - Set `message` to the `ExecutionProof`. - Set `prover_pubkey` to the prover's public key. From 9bf6076dbc815a41e9cf2c10604e157e24dcce20 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 22 Jan 2026 12:03:05 -0600 Subject: [PATCH 18/28] Fix table of contents (my mistake) --- specs/_features/eip8025/beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 3b90805d68..b8a20e3f00 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -8,7 +8,6 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) -- [Configuration](#configuration) - [Containers](#containers) - [New containers](#new-containers) - [`ProverSignedExecutionProof`](#proversignedexecutionproof) From b08d0cefa8cf31dcdc922f359f34a9c561ea3fb3 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 22 Jan 2026 12:09:48 -0600 Subject: [PATCH 19/28] Fix various nits --- specs/_features/eip8025/beacon-chain.md | 27 +++++++------ specs/_features/eip8025/proof-engine.md | 53 ++++++++++--------------- specs/_features/eip8025/prover.md | 4 +- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index b8a20e3f00..1e9ce00a2e 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -8,12 +8,11 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) +- [Configuration](#configuration) - [Containers](#containers) - - [New containers](#new-containers) - - [`ProverSignedExecutionProof`](#proversignedexecutionproof) - - [`NewPayloadRequestHeader`](#newpayloadrequestheader) - - [Extended containers](#extended-containers) - - [`BeaconState`](#beaconstate) + - [New `ProverSignedExecutionProof`](#new-proversignedexecutionproof) + - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) + - [Modified `BeaconState`](#modified-beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_block`](#modified-process_block) @@ -32,11 +31,17 @@ validation of execution payloads through execution proofs. *Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and imports proof types from [proof-engine.md](./proof-engine.md). -## Containers +## Configuration + +*Note*: The configuration values are not definitive. -### New containers +| Name | Value | +| ------------------------- | ------------- | +| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | -#### `ProverSignedExecutionProof` +## Containers + +### New `ProverSignedExecutionProof` ```python class ProverSignedExecutionProof(Container): @@ -45,7 +50,7 @@ class ProverSignedExecutionProof(Container): signature: BLSSignature ``` -#### `NewPayloadRequestHeader` +### New `NewPayloadRequestHeader` ```python @dataclass @@ -56,9 +61,7 @@ class NewPayloadRequestHeader(object): execution_requests: ExecutionRequests ``` -### Extended containers - -#### `BeaconState` +#### Modified `BeaconState` ```python class BeaconState(Container): diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md index 711333df70..f9f0c4e69b 100644 --- a/specs/_features/eip8025/proof-engine.md +++ b/specs/_features/eip8025/proof-engine.md @@ -8,19 +8,18 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) +- [Types](#types) - [Constants](#constants) - [Execution](#execution) - - [Custom types](#custom-types) - - [Domain types](#domain-types) -- [Configuration](#configuration) + - [Domains](#domains) - [Containers](#containers) - - [`PublicInput`](#publicinput) - - [`ExecutionProof`](#executionproof) - - [`ProofAttributes`](#proofattributes) + - [New `PublicInput`](#new-publicinput) + - [New `ExecutionProof`](#new-executionproof) + - [New `ProofAttributes`](#new-proofattributes) - [Proof engine](#proof-engine) - - [`verify_execution_proof`](#verify_execution_proof) - - [`verify_new_payload_request_header`](#verify_new_payload_request_header) - - [`request_proofs`](#request_proofs) + - [New `verify_execution_proof`](#new-verify_execution_proof) + - [New `verify_new_payload_request_header`](#new-verify_new_payload_request_header) + - [New `request_proofs`](#new-request_proofs) @@ -29,6 +28,13 @@ This document contains the Proof Engine specification. The Proof Engine enables stateless validation of execution payloads through execution proofs. +## Types + +| Name | SSZ equivalent | Description | +| ------------ | -------------- | ---------------------------------------- | +| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | +| `ProofType` | `uint8` | The type identifier for the proof | + ## Constants ### Execution @@ -40,37 +46,22 @@ stateless validation of execution payloads through execution proofs. | `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | | `MAX_PROOF_SIZE` | `307200` (= 300KiB) | -### Custom types - -| Name | SSZ equivalent | Description | -| ------------ | -------------- | ---------------------------------------- | -| `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | -| `ProofType` | `uint8` | The type identifier for the proof | - -### Domain types +### Domains | Name | Value | | ------------------------ | -------------------------- | | `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0D000000')` | -## Configuration - -*Note*: The configuration values are not definitive. - -| Name | Value | -| ------------------------- | ------------- | -| `MAX_WHITELISTED_PROVERS` | `uint64(256)` | - ## Containers -### `PublicInput` +### New `PublicInput` ```python class PublicInput(Container): new_payload_request_root: Root ``` -### `ExecutionProof` +### New `ExecutionProof` ```python class ExecutionProof(Container): @@ -79,7 +70,7 @@ class ExecutionProof(Container): public_input: PublicInput ``` -### `ProofAttributes` +### New `ProofAttributes` ```python @dataclass @@ -105,7 +96,7 @@ The body of these functions are implementation dependent. The Engine API may be used to implement this and similarly defined functions via an external proof engine. -### `verify_execution_proof` +### New `verify_execution_proof` ```python def verify_execution_proof( @@ -119,7 +110,7 @@ def verify_execution_proof( ... ``` -### `verify_new_payload_request_header` +### New `verify_new_payload_request_header` ```python def verify_new_payload_request_header( @@ -133,7 +124,7 @@ def verify_new_payload_request_header( ... ``` -### `request_proofs` +### New `request_proofs` ```python def request_proofs( diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index e11c156fdc..761cb21e28 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -9,7 +9,7 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) - [Helpers](#helpers) - - [`get_execution_proof_signature`](#get_execution_proof_signature) + - [New `get_execution_proof_signature`](#new-get_execution_proof_signature) - [Constructing `ProverSignedExecutionProof`](#constructing-proversignedexecutionproof) - [Honest prover relay](#honest-prover-relay) @@ -31,7 +31,7 @@ imports proof types from [proof-engine.md](./proof-engine.md). ## Helpers -### `get_execution_proof_signature` +### New `get_execution_proof_signature` ```python def get_execution_proof_signature( From 1d8e7e30b3e3ce02fd44b9729a693e7daeb8ad00 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 22 Jan 2026 12:13:27 -0600 Subject: [PATCH 20/28] Rename ProverSignedExecutionProof to SignedExecutionProof --- specs/_features/eip8025/beacon-chain.md | 10 +++++----- specs/_features/eip8025/p2p-interface.md | 6 +++--- specs/_features/eip8025/prover.md | 10 ++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 1e9ce00a2e..d561cb25db 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -10,7 +10,7 @@ - [Introduction](#introduction) - [Configuration](#configuration) - [Containers](#containers) - - [New `ProverSignedExecutionProof`](#new-proversignedexecutionproof) + - [New `SignedExecutionProof`](#new-signedexecutionproof) - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) - [Modified `BeaconState`](#modified-beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) @@ -41,10 +41,10 @@ imports proof types from [proof-engine.md](./proof-engine.md). ## Containers -### New `ProverSignedExecutionProof` +### New `SignedExecutionProof` ```python -class ProverSignedExecutionProof(Container): +class SignedExecutionProof(Container): message: ExecutionProof prover_pubkey: BLSPubkey signature: BLSSignature @@ -230,11 +230,11 @@ def process_execution_payload( ```python def process_prover_signed_execution_proof( state: BeaconState, - signed_proof: ProverSignedExecutionProof, + signed_proof: SignedExecutionProof, proof_engine: ProofEngine, ) -> None: """ - Handler for ProverSignedExecutionProof. + Handler for SignedExecutionProof. """ proof_message = signed_proof.message prover_pubkey = signed_proof.prover_pubkey diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index e7a835f813..6f078eeba1 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -28,7 +28,7 @@ imports proof types from [proof-engine.md](./proof-engine.md). ##### `execution_proof` -This topic is used to propagate `ProverSignedExecutionProof` messages. +This topic is used to propagate `SignedExecutionProof` messages. The following validations MUST pass before forwarding a proof on the network: @@ -39,7 +39,7 @@ The following validations MUST pass before forwarding a proof on the network: - _[IGNORE]_ The proof is the first valid proof received for the tuple `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, prover_pubkey)`. -For `ProverSignedExecutionProof`: +For `SignedExecutionProof`: - _[REJECT]_ The `prover_pubkey` is in the prover whitelist. - _[REJECT]_ The signature is valid with respect to the prover's public key. @@ -71,7 +71,7 @@ Response Content: ``` ( - List[ProverSignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] + List[SignedExecutionProof, MAX_EXECUTION_PROOFS_PER_PAYLOAD] ) ``` diff --git a/specs/_features/eip8025/prover.md b/specs/_features/eip8025/prover.md index 761cb21e28..f4a9fadd93 100644 --- a/specs/_features/eip8025/prover.md +++ b/specs/_features/eip8025/prover.md @@ -10,7 +10,7 @@ - [Introduction](#introduction) - [Helpers](#helpers) - [New `get_execution_proof_signature`](#new-get_execution_proof_signature) -- [Constructing `ProverSignedExecutionProof`](#constructing-proversignedexecutionproof) +- [Constructing `SignedExecutionProof`](#constructing-signedexecutionproof) - [Honest prover relay](#honest-prover-relay) @@ -42,7 +42,7 @@ def get_execution_proof_signature( return bls.Sign(privkey, signing_root) ``` -## Constructing `ProverSignedExecutionProof` +## Constructing `SignedExecutionProof` An honest prover who has been whitelisted and wants to generate execution proofs for a `BeaconBlockBody` performs the following steps: @@ -65,8 +65,7 @@ for a `BeaconBlockBody` performs the following steps: - Set `prover_pubkey` to the prover's public key. - Sign the proof using `get_execution_proof_signature(state, proof, prover_privkey)`. - - Broadcast the `ProverSignedExecutionProof` on the `execution_proof` gossip - topic. + - Broadcast the `SignedExecutionProof` on the `execution_proof` gossip topic. ## Honest prover relay @@ -83,6 +82,5 @@ When a prover relay receives an unsigned `ExecutionProof` via 3. Check the proof is not a duplicate (same `new_payload_request_root`, `proof_type`). 4. If valid and not a duplicate: - - Create a `ProverSignedExecutionProof` with the relay's public key and - signature. + - Create a `SignedExecutionProof` with the relay's public key and signature. - Broadcast on the `execution_proof` gossip topic. From 23b033457f948be913aba3e11ad6d528a56ae01a Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Thu, 22 Jan 2026 12:21:44 -0600 Subject: [PATCH 21/28] Reorganize a bit --- specs/_features/eip8025/beacon-chain.md | 68 +++++++++++++++++++----- specs/_features/eip8025/p2p-interface.md | 12 +++++ specs/_features/eip8025/proof-engine.md | 60 ++++----------------- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index d561cb25db..0acfceac6f 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -8,15 +8,21 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) +- [Types](#types) +- [Constants](#constants) + - [Execution](#execution) + - [Domains](#domains) - [Configuration](#configuration) - [Containers](#containers) + - [New `PublicInput`](#new-publicinput) + - [New `ExecutionProof`](#new-executionproof) - [New `SignedExecutionProof`](#new-signedexecutionproof) - - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) - [Modified `BeaconState`](#modified-beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_block`](#modified-process_block) - [Execution payload processing](#execution-payload-processing) + - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) - [Modified `process_execution_payload`](#modified-process_execution_payload) - [Execution proof handlers](#execution-proof-handlers) - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) @@ -31,6 +37,28 @@ validation of execution payloads through execution proofs. *Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and imports proof types from [proof-engine.md](./proof-engine.md). +## Types + +| Name | SSZ equivalent | Description | +| ----------- | -------------- | --------------------------------- | +| `ProofType` | `uint8` | The type identifier for the proof | + +## Constants + +### Execution + +*Note*: The execution values are not definitive. + +| Name | Value | +| ---------------- | ------------------- | +| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | + +### Domains + +| Name | Value | +| ------------------------ | -------------------------- | +| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0D000000')` | + ## Configuration *Note*: The configuration values are not definitive. @@ -41,6 +69,22 @@ imports proof types from [proof-engine.md](./proof-engine.md). ## Containers +### New `PublicInput` + +```python +class PublicInput(Container): + new_payload_request_root: Root +``` + +### New `ExecutionProof` + +```python +class ExecutionProof(Container): + proof_data: ByteList[MAX_PROOF_SIZE] + proof_type: ProofType + public_input: PublicInput +``` + ### New `SignedExecutionProof` ```python @@ -50,17 +94,6 @@ class SignedExecutionProof(Container): signature: BLSSignature ``` -### New `NewPayloadRequestHeader` - -```python -@dataclass -class NewPayloadRequestHeader(object): - execution_payload_header: ExecutionPayloadHeader - versioned_hashes: Sequence[VersionedHash] - parent_beacon_block_root: Root - execution_requests: ExecutionRequests -``` - #### Modified `BeaconState` ```python @@ -130,6 +163,17 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ### Execution payload processing +#### New `NewPayloadRequestHeader` + +```python +@dataclass +class NewPayloadRequestHeader(object): + execution_payload_header: ExecutionPayloadHeader + versioned_hashes: Sequence[VersionedHash] + parent_beacon_block_root: Root + execution_requests: ExecutionRequests +``` + #### Modified `process_execution_payload` *Note*: `process_execution_payload` is modified in EIP-8025 to require both diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 6f078eeba1..28125a07ca 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -10,6 +10,8 @@ imports proof types from [proof-engine.md](./proof-engine.md). - [Table of contents](#table-of-contents) +- [Constants](#constants) + - [Execution](#execution) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) @@ -20,6 +22,16 @@ imports proof types from [proof-engine.md](./proof-engine.md). +## Constants + +### Execution + +*Note*: The execution values are not definitive. + +| Name | Value | +| ---------------------------------- | ----------- | +| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | + ## The gossip domain: gossipsub ### Topics and messages diff --git a/specs/_features/eip8025/proof-engine.md b/specs/_features/eip8025/proof-engine.md index f9f0c4e69b..911c6125dc 100644 --- a/specs/_features/eip8025/proof-engine.md +++ b/specs/_features/eip8025/proof-engine.md @@ -9,16 +9,10 @@ - [Table of contents](#table-of-contents) - [Introduction](#introduction) - [Types](#types) -- [Constants](#constants) - - [Execution](#execution) - - [Domains](#domains) -- [Containers](#containers) - - [New `PublicInput`](#new-publicinput) - - [New `ExecutionProof`](#new-executionproof) - - [New `ProofAttributes`](#new-proofattributes) - [Proof engine](#proof-engine) - [New `verify_execution_proof`](#new-verify_execution_proof) - [New `verify_new_payload_request_header`](#new-verify_new_payload_request_header) + - [New `ProofAttributes`](#new-proofattributes) - [New `request_proofs`](#new-request_proofs) @@ -33,50 +27,6 @@ stateless validation of execution payloads through execution proofs. | Name | SSZ equivalent | Description | | ------------ | -------------- | ---------------------------------------- | | `ProofGenId` | `Bytes8` | Identifier for tracking proof generation | -| `ProofType` | `uint8` | The type identifier for the proof | - -## Constants - -### Execution - -*Note*: The execution values are not definitive. - -| Name | Value | -| ---------------------------------- | ------------------- | -| `MAX_EXECUTION_PROOFS_PER_PAYLOAD` | `uint64(4)` | -| `MAX_PROOF_SIZE` | `307200` (= 300KiB) | - -### Domains - -| Name | Value | -| ------------------------ | -------------------------- | -| `DOMAIN_EXECUTION_PROOF` | `DomainType('0x0D000000')` | - -## Containers - -### New `PublicInput` - -```python -class PublicInput(Container): - new_payload_request_root: Root -``` - -### New `ExecutionProof` - -```python -class ExecutionProof(Container): - proof_data: ByteList[MAX_PROOF_SIZE] - proof_type: ProofType - public_input: PublicInput -``` - -### New `ProofAttributes` - -```python -@dataclass -class ProofAttributes(object): - proof_types: List[ProofType] -``` ## Proof engine @@ -124,6 +74,14 @@ def verify_new_payload_request_header( ... ``` +### New `ProofAttributes` + +```python +@dataclass +class ProofAttributes(object): + proof_types: List[ProofType] +``` + ### New `request_proofs` ```python From 6bdb7d0a687d72d40a239b7d9c3de17e368d8f95 Mon Sep 17 00:00:00 2001 From: Jihoon Song Date: Fri, 23 Jan 2026 21:28:17 +0900 Subject: [PATCH 22/28] Nits --- specs/_features/eip8025/beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 0acfceac6f..3cffeed65a 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -17,7 +17,7 @@ - [New `PublicInput`](#new-publicinput) - [New `ExecutionProof`](#new-executionproof) - [New `SignedExecutionProof`](#new-signedexecutionproof) - - [Modified `BeaconState`](#modified-beaconstate) + - [Modified `BeaconState`](#modified-beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_block`](#modified-process_block) @@ -31,7 +31,7 @@ ## Introduction -This document contains the consensus specs for EIP-8025, enabling stateless +These are the beacon-chain specifications to add EIP-8025, enabling stateless validation of execution payloads through execution proofs. *Note*: This specification is built upon [Fulu](../../fulu/beacon-chain.md) and @@ -94,7 +94,7 @@ class SignedExecutionProof(Container): signature: BLSSignature ``` -#### Modified `BeaconState` +### Modified `BeaconState` ```python class BeaconState(Container): From 7fd6b516b82fe31a7592a67aed377e70f00d9c0a Mon Sep 17 00:00:00 2001 From: frisitano <35734660+frisitano@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:38:51 +0000 Subject: [PATCH 23/28] Update specs/_features/eip8025/beacon-chain.md Co-authored-by: JihoonSong --- specs/_features/eip8025/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 3cffeed65a..26dcb065e6 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -272,7 +272,7 @@ def process_execution_payload( #### New `process_prover_signed_execution_proof` ```python -def process_prover_signed_execution_proof( +def process_execution_proof( state: BeaconState, signed_proof: SignedExecutionProof, proof_engine: ProofEngine, From eaa9c23bf5e1b3c2f7b45b7399f66a159ae46350 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 23 Jan 2026 11:50:29 -0600 Subject: [PATCH 24/28] Remove prover whitelist --- specs/_features/eip8025/beacon-chain.md | 50 +----------------------- specs/_features/eip8025/p2p-interface.md | 22 +++++------ 2 files changed, 13 insertions(+), 59 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 26dcb065e6..51406cdd98 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -17,7 +17,6 @@ - [New `PublicInput`](#new-publicinput) - [New `ExecutionProof`](#new-executionproof) - [New `SignedExecutionProof`](#new-signedexecutionproof) - - [Modified `BeaconState`](#modified-beaconstate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_block`](#modified-process_block) @@ -94,52 +93,6 @@ class SignedExecutionProof(Container): signature: BLSSignature ``` -### Modified `BeaconState` - -```python -class BeaconState(Container): - genesis_time: uint64 - genesis_validators_root: Root - slot: Slot - fork: Fork - latest_block_header: BeaconBlockHeader - block_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - state_roots: Vector[Root, SLOTS_PER_HISTORICAL_ROOT] - historical_roots: List[Root, HISTORICAL_ROOTS_LIMIT] - eth1_data: Eth1Data - eth1_data_votes: List[Eth1Data, EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH] - eth1_deposit_index: uint64 - validators: List[Validator, VALIDATOR_REGISTRY_LIMIT] - balances: List[Gwei, VALIDATOR_REGISTRY_LIMIT] - randao_mixes: Vector[Bytes32, EPOCHS_PER_HISTORICAL_VECTOR] - slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] - previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] - justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] - previous_justified_checkpoint: Checkpoint - current_justified_checkpoint: Checkpoint - finalized_checkpoint: Checkpoint - inactivity_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] - current_sync_committee: SyncCommittee - next_sync_committee: SyncCommittee - latest_execution_payload_header: ExecutionPayloadHeader - next_withdrawal_index: WithdrawalIndex - next_withdrawal_validator_index: ValidatorIndex - historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT] - deposit_requests_start_index: uint64 - deposit_balance_to_consume: Gwei - exit_balance_to_consume: Gwei - earliest_exit_epoch: Epoch - consolidation_balance_to_consume: Gwei - earliest_consolidation_epoch: Epoch - pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT] - pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT] - pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] - proposer_lookahead: Vector[ValidatorIndex, (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH] - # [New in EIP8025] - prover_whitelist: List[BLSPubkey, MAX_WHITELISTED_PROVERS] -``` - ## Beacon chain state transition function ### Block processing @@ -284,7 +237,8 @@ def process_execution_proof( prover_pubkey = signed_proof.prover_pubkey # Verify prover is whitelisted - assert prover_pubkey in state.prover_whitelist + validator_pubkeys = [v.pubkey for v in state.validators] + assert prover_pubkey in validator_pubkeys domain = get_domain(state, DOMAIN_EXECUTION_PROOF, compute_epoch_at_slot(state.slot)) signing_root = compute_signing_root(proof_message, domain) diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 28125a07ca..f1c9e12716 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -48,17 +48,17 @@ The following validations MUST pass before forwarding a proof on the network: `proof.message.public_input.new_payload_request_root`) has been seen (via gossip or non-gossip sources) (a client MAY queue proofs for processing once the new payload request is retrieved). -- _[IGNORE]_ The proof is the first valid proof received for the tuple - `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, prover_pubkey)`. - -For `SignedExecutionProof`: - -- _[REJECT]_ The `prover_pubkey` is in the prover whitelist. -- _[REJECT]_ The signature is valid with respect to the prover's public key. -- _[REJECT]_ The `proof.message.proof_data` is non-empty. -- _[REJECT]_ The `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. -- _[REJECT]_ The execution proof is valid as verified by the appropriate - handler. +- _[IGNORE]_ The proof is the first proof received for the tuple + `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, prover_pubkey)` + -- i.e. the first proof from that prover. +- _[REJECT]_ `proof.prover_pubkey` is associated with an active validator. +- _[REJECT]_ `proof.signature` is valid with respect to the prover's public key. +- _[REJECT]_ `proof.message.proof_data` is non-empty. +- _[REJECT]_ `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. +- _[REJECT]_ `proof.message` is a valid execution proof. +- _[IGNORE]_ The proof is the first *valid* proof received for the tuple + `(proof.message.public_input.new_payload_request_root, proof.message.proof_type)` + -- i.e. the first valid proof for that proof type. ## The Req/Resp domain From aae1f788916058500de280e9fbb3bcabf3596bc5 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 23 Jan 2026 11:55:23 -0600 Subject: [PATCH 25/28] Fix nit --- specs/_features/eip8025/p2p-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index f1c9e12716..a81c3409ec 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -49,7 +49,7 @@ The following validations MUST pass before forwarding a proof on the network: gossip or non-gossip sources) (a client MAY queue proofs for processing once the new payload request is retrieved). - _[IGNORE]_ The proof is the first proof received for the tuple - `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, prover_pubkey)` + `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, proof.prover_pubkey)` -- i.e. the first proof from that prover. - _[REJECT]_ `proof.prover_pubkey` is associated with an active validator. - _[REJECT]_ `proof.signature` is valid with respect to the prover's public key. From 614679c7be90b48d2ae4898520ce2ca5f5303279 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 23 Jan 2026 12:08:09 -0600 Subject: [PATCH 26/28] Clarify gossip conditions --- specs/_features/eip8025/p2p-interface.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index a81c3409ec..535b9a0261 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -50,15 +50,17 @@ The following validations MUST pass before forwarding a proof on the network: the new payload request is retrieved). - _[IGNORE]_ The proof is the first proof received for the tuple `(proof.message.public_input.new_payload_request_root, proof.message.proof_type, proof.prover_pubkey)` - -- i.e. the first proof from that prover. + -- i.e. the first *valid or invalid* proof for `proof.message.proof_type` from + `proof.prover_pubkey`. - _[REJECT]_ `proof.prover_pubkey` is associated with an active validator. - _[REJECT]_ `proof.signature` is valid with respect to the prover's public key. - _[REJECT]_ `proof.message.proof_data` is non-empty. - _[REJECT]_ `proof.message.proof_data` is not larger than `MAX_PROOF_SIZE`. - _[REJECT]_ `proof.message` is a valid execution proof. -- _[IGNORE]_ The proof is the first *valid* proof received for the tuple +- _[IGNORE]_ The proof is the first proof received for the tuple `(proof.message.public_input.new_payload_request_root, proof.message.proof_type)` - -- i.e. the first valid proof for that proof type. + -- i.e. the first *valid* proof for `proof.message.proof_type` from any + prover. ## The Req/Resp domain From b746a88b9f34d494fa25b7ced2139b0846fd4996 Mon Sep 17 00:00:00 2001 From: Jihoon Song Date: Sat, 24 Jan 2026 13:30:14 +0900 Subject: [PATCH 27/28] Trim ToC --- specs/_features/eip8025/beacon-chain.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/specs/_features/eip8025/beacon-chain.md b/specs/_features/eip8025/beacon-chain.md index 51406cdd98..5336442a5b 100644 --- a/specs/_features/eip8025/beacon-chain.md +++ b/specs/_features/eip8025/beacon-chain.md @@ -20,11 +20,11 @@ - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Block processing](#block-processing) - [Modified `process_block`](#modified-process_block) - - [Execution payload processing](#execution-payload-processing) - - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) - - [Modified `process_execution_payload`](#modified-process_execution_payload) - - [Execution proof handlers](#execution-proof-handlers) - - [New `process_prover_signed_execution_proof`](#new-process_prover_signed_execution_proof) + - [Execution payload](#execution-payload) + - [New `NewPayloadRequestHeader`](#new-newpayloadrequestheader) + - [Modified `process_execution_payload`](#modified-process_execution_payload) + - [Execution proof](#execution-proof) + - [New `process_execution_proof`](#new-process_execution_proof) @@ -114,9 +114,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_sync_aggregate(state, block.body.sync_aggregate) ``` -### Execution payload processing +#### Execution payload -#### New `NewPayloadRequestHeader` +##### New `NewPayloadRequestHeader` ```python @dataclass @@ -127,7 +127,7 @@ class NewPayloadRequestHeader(object): execution_requests: ExecutionRequests ``` -#### Modified `process_execution_payload` +##### Modified `process_execution_payload` *Note*: `process_execution_payload` is modified in EIP-8025 to require both `ExecutionEngine` and `ProofEngine` for validation. @@ -218,11 +218,11 @@ def process_execution_payload( ) ``` -### Execution proof handlers +### Execution proof *Note*: Proof storage is implementation-dependent, managed by the `ProofEngine`. -#### New `process_prover_signed_execution_proof` +#### New `process_execution_proof` ```python def process_execution_proof( @@ -230,9 +230,6 @@ def process_execution_proof( signed_proof: SignedExecutionProof, proof_engine: ProofEngine, ) -> None: - """ - Handler for SignedExecutionProof. - """ proof_message = signed_proof.message prover_pubkey = signed_proof.prover_pubkey From be1a7e04dfd02c97b67313c81e5b9e6e3e8ddbd3 Mon Sep 17 00:00:00 2001 From: Justin Traglia Date: Fri, 30 Jan 2026 13:09:08 -0600 Subject: [PATCH 28/28] Run make lint --- specs/_features/eip8025/p2p-interface.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/_features/eip8025/p2p-interface.md b/specs/_features/eip8025/p2p-interface.md index 3953b3af43..c71eee8834 100644 --- a/specs/_features/eip8025/p2p-interface.md +++ b/specs/_features/eip8025/p2p-interface.md @@ -20,7 +20,6 @@ imports proof types from [proof-engine.md](./proof-engine.md). - [The Req/Resp domain](#the-reqresp-domain) - [Messages](#messages) - [ExecutionProofsByRoot](#executionproofsbyroot) - - [ExecutionProofsByHash](#executionproofsbyhash) - [GetMetaData v4](#getmetadata-v4) - [The discovery domain: discv5](#the-discovery-domain-discv5) - [ENR structure](#enr-structure)