---
# System prepended metadata

title: Implementing Optional Execution Proofs (EIP-8025) in Prysm
tags: [Optional Proofs]

---

> [!Note]
> Current version: https://github.com/developeruche/prysm/tree/ca228fca440f72da329f0d5d0488d105a633118c

> [!Tip]
> Legend
> - ✅: Complete!
> - 🚧: Under construction.
> - 🔘: Nothing done.

## ✅ Flags

- **`--activate-zkvm`**: If specified, set `cfg.EnableZkvm` to `true`. We can use `features.Get().EnableZkvm` to access this value in the codebase.
- **`--zkvm-generation-proof-types`**: If specified, set `cfg.ProofGenerationTypes` to the slice of `ExecutionProofId`. We can use `flags.Get().ProofGenerationTypes` to access this value in the codebase.

## ✅ Data types

```go
// ExecutionProofId identifies which zkVM/proof system a proof belongs to.
type ExecutionProofId uint8
```
- **`ExecutionProofId`**: Alias for `uint8`. Implemented in `consensus-types/primitives/execution_proof_id.go` ([Link](https://github.com/syjn99/prysm/blob/2d9e6ad2c8302b0045a4c6020f52e997c3f20bdf/consensus-types/primitives/execution_proof_id.go)).

```protobuf
message ExecutionProof {
    // Which proof type (zkVM+EL combination) this proof belongs to
    // Examples: 0=SP1+Reth, 1=Risc0+Geth, 2=SP1+Geth, etc.
    uint64 proof_id = 1 [
        (ethereum.eth.ext.cast_type) =
            "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.ExecutionProofId"
    ];

    // The slot of the beacon block this proof validates
    uint64 slot = 2 [
      (ethereum.eth.ext.cast_type) =
          "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Slot"
    ];

    // The block hash of the execution payload this proof validates
    bytes block_hash = 3 [ (ethereum.eth.ext.ssz_size) = "32" ];

    // The beacon block root corresponding to the beacon block
    // with the execution payload, that this proof attests to.
    bytes block_root = 4 [ (ethereum.eth.ext.ssz_size) = "32" ];

    // The actual proof data
    bytes proof_data = 5 [ (ethereum.eth.ext.ssz_max) = "1048576" ];
}
```
- **`ExecutionProof`**: A core SSZ Container that is used in P2P, Proof Generation Service, etc. Defined as a proto message at `proto/prysm/v1alpha1/execution_proof.proto` ([Link](https://github.com/syjn99/prysm/blob/2d9e6ad2c8302b0045a4c6020f52e997c3f20bdf/proto/prysm/v1alpha1/execution_proof.proto)) and implemented corresponding SSZ-related methods (e.g., `MarshalSSZ`, `HashTreeRoot`) under the same directory.

## ✅ Cache

- [x] Currently implemented in `zkvm-execution-layer/proof_cache.go` ([Link](https://github.com/syjn99/prysm/blob/2d9e6ad2c8302b0045a4c6020f52e997c3f20bdf/zkvm-execution-layer/proof_cache.go)) 
- [x] **would be better** to re-implement and move this under `beacon-chain/cache` ([Link](https://github.com/OffchainLabs/prysm/tree/develop/beacon-chain/cache)). See other cache examples under the directory.

## ✅ P2P

> [!Note]
> We assume all functionalities are enabled **after** Fulu.

### ✅ ENR: `"zkvm"`

When a node activates zkVM mode by `--activate-zkvm`, it *explicitly* advertises by adding an entry for `zkvm`.

- [x] Add `ZkvmKey` (= `zkvm`) under `NetworkConfig` in `config/params/network_config.go`
- [x] Set zkVM entry in `updateSubnetRecordWithMetadataV3`.

### ✅ Gossip

A node [subscribes](https://github.com/OffchainLabs/prysm/blob/2773bdef89ab460aea4353d4d98833a4eeaa4918/beacon-chain/sync/fork_watcher.go#L19) to each pubsub topic *after* chain starts. 

Note: Unlike the [specification](https://github.com/ethereum/consensus-specs/blob/master/specs/_features/eip8025/p2p-interface.md#topics-and-messages), current implementation has **one single global** topic `/eth2/{fork_digest}/execution_proof`.

- [x] Add `ExecutionProofTopicFormat`(`/eth2/{fork_digest}/execution_proof`) under `beacon-chain/p2p/topics.go`
- [x] Add `defaultExecutionProofTopicParams()` under `beacon-chain/p2p/gossip_scoring_params.go`
- [x] Add `seenExecutionProofCache` and lock for the Sync service
- [x] Add validation function (`validateExecutionProof()`)
- [x] Add subscriber (`executionProofSubscriber`)
- [x] Register the subscriber (`registerSubscribers`)

### ✅ RPC (Req/Resp)

Handlers for P2P RPC are [registered](https://github.com/OffchainLabs/prysm/blob/2773bdef89ab460aea4353d4d98833a4eeaa4918/beacon-chain/sync/service.go#L383) when the Sync service starts.

- [x] Add `RPCExecutionProofsByRootTopicV1` (`/eth2/beacon_chain/req/execution_proofs_by_root/1/`) under `beacon-chain/p2p/rpc_topic_mappings.go`
- [x] Add an entry for `RPCTopicMappings`
- [x] Add rate limiter for this (modification in `newRateLimiter`)
- [x] Add handler
- [x] Send request by block hash 

## ✅ DA Check

```rust!
// Check if this node needs execution proofs to validate blocks.
// Nodes that have EL and generate proofs validate via EL execution.
// Nodes that have EL but DON'T generate proofs are lightweight verifiers and wait for proofs.
// TODO(zkproofs): This is a technicality mainly because we cannot remove the EL on kurtosis
// ie each CL is coupled with an EL
let needs_execution_proofs =
    spec.zkvm_min_proofs_required().is_some() && !has_execution_layer_and_proof_gen;

if needs_execution_proofs {
    let min_proofs = spec.zkvm_min_proofs_required().unwrap();
    let num_proofs = self.execution_proof_subnet_count();
    if num_proofs < min_proofs {
        // Not enough execution proofs yet
        return Ok(None);
    }
}
```

We need to add whether we've seen enough proofs. `ReceiveBlock` in `beacon-chain/blockchain/receive_block.go` is the core function when a block is received from gossip/rpc.

```go!
func (s *Service) handleDA(ctx context.Context, avs das.AvailabilityStore, block blocks.ROBlock) (time.Duration, error) {
	var err error
	start := time.Now()
	if avs != nil {
		err = avs.IsDataAvailable(ctx, s.CurrentSlot(), block)
	} else {
		err = s.isDataAvailable(ctx, block)
	}
	elapsed := time.Since(start)
	if err == nil {
		dataAvailWaitedTime.Observe(float64(elapsed.Milliseconds()))
	}
	return elapsed, err
}
```

For now, we can ignore `das.AvailabilityStore` and add proof availability logic in `isDataAvaiable`.


## ✅ Proof Generation Service

- [x] Add skeleton under `beacon-chain/proof-generation` and register when zkVM mode is enabled.
- [x] Generate execution proofs in altruistic manner when a block is imported.
- [x] Check whether the proof generation is needed or not.
- [x] Broadcast the proof via gossipsub.

## 📚 References

- My current implementation: https://github.com/syjn99/prysm/pull/11
- Kev's implementation on Lighthouse: https://github.com/sigp/lighthouse/pull/8316
    - Related write-up: https://hackmd.io/@kevaundray/BJeZCo5Tgx
- Developeruche's initial implementation on Prysm: https://github.com/OffchainLabs/prysm/pull/16003
    - Related write-up: https://hackmd.io/@0xdeveloperuche/rJyb-aEyWx
- EIP-8025 Specification (Note: Spec is outdated): https://github.com/ethereum/consensus-specs/tree/master/specs/_features/eip8025