ZK Fault Proof VM
Table of Contents
Overview
The ZK Fault Proof VM is a succinct proof system that verifies a super root state transition across all chains in the interop set through a single cryptographic proof. It consists of two components:
- ZK Program: an off-chain circuit that re-executes the derivation and state transitions for all chains in the interop set and produces a succinct proof of correctness.
- On-chain Verifier: a smart contract (see
IZKVerifier) that checks the proof and the committed public values in a single call.
This is the ZK analogue of SuperFaultDisputeGame. Where the fault proof bisects an execution
trace down to a single instruction, the ZK VM proves the entire block range across all
chains — from one super root to the next — in a single on-chain call.
In a standalone deployment (single chain), the interop set contains exactly one chain. The program
is identical; the SuperRootProof preimage carries one entry. No separate standalone mode exists.
ZK Program
The ZK program is the circuit executed off-chain to generate a proof. It takes a set of
public values as inputs and verifies that executing the derivation and state transitions
for every chain in the interop set, starting from startingProposal.root and using L1 data up to
l1Head, produces the super root rootClaim at timestamp l2SequenceNumber.
Inputs
The following public values are committed to by the ZK proof. They are constructed on-chain from game state and passed to the verifier:
| Field | Type | Description |
|---|---|---|
l1Head | bytes32 | L1 block hash at which the L1 state was sampled. Authenticates all observed L1 data. |
startingProposal.root | bytes32 | Super root hash of the parent game's claim, or the anchor state if parentIndex == type(uint32).max. Starting point for all chain state transitions. |
rootClaim | bytes32 | The super root hash being asserted by this game. It commits to the output roots of all chains in the interop set at l2SequenceNumber. |
l2SequenceNumber | uint256 | Super root timestamp corresponding to rootClaim. Constrained to uint64 range. |
proverAddress | address | Address of the proof submitter (msg.sender in prove()). Binds the proof to a specific submission. |
l2ChainId is intentionally absent. Chain scoping is provided by the SuperRootProof preimage
committed to by rootClaim and startingProposal.root. In a standalone (single-chain) deployment,
the preimage contains exactly one chain entry; no separate field is needed.
Output
The program produces a proof that commits to the public values. A proof that passes
on-chain verification means: executing the derivation logic for every chain in the interop set
from the state committed to in startingProposal.root, under the L1 data observed at l1Head,
yields exactly the super root rootClaim at timestamp l2SequenceNumber.
Absolute Prestate
The absolutePrestate is a bytes32 value that uniquely identifies the ZK program version being
proven. Two different programs MUST NOT share the same absolutePrestate.
It serves as the program identity in
IZKVerifier.verify and is injected into each game
instance via the CWIA game args.
For SP1 deployments, absolutePrestate corresponds to the program's verification key
(programVKey), derived deterministically from the circuit binary and structure.
Program updates (e.g. a bug fix, a new hard fork, or a change to the interop set definition)
MUST produce a new absolutePrestate. OPCM manages absolutePrestate per deployment; updates
require governance.
Reference Implementation
SP1 (PLONK)
The initial reference implementation uses SP1 by Succinct with the PLONK backend.
- The ZK program is compiled to run inside the SP1 zkVM.
absolutePrestate= the SP1 program verification key (programVKey).- The on-chain verifier is Succinct's PLONK verifier, wrapped behind the
IZKVerifierinterface.
Proof Generation
Proofs are generated off-chain by a prover that:
- Fetches the required L1 and L2 data up to
l1Headfor all chains in the interop set. - Decodes the
SuperRootProofpreimage from the game'sextraDatato identify the chain set and their expected output roots. - Executes the ZK program inside the zkVM with the public values as inputs and provides the required L1 and L2 data as private values to the ZK program.
- Produces a proof blob (
proofBytes). - Submits the proof on-chain via
ZKDisputeGame.prove(proofBytes).
Proof generation is permissionless: any party may generate and submit a proof. In practice the proposer or a third-party proving service will act as prover.
Invariants
iZKVM-001: Private Inputs Must Be Anchored to Public Values
The ZK program receives private inputs (block and transaction data for all chains) that are known
only to the prover and never seen by the on-chain verifier. The ZK program MUST verify that all
private inputs are directly derived from or cryptographically linked to the public values (e.g.
by hashing block data and comparing against l1Head, or verifying that blocks form a chain rooted
at startingProposal.root). The ZK program MUST NOT trust any private input without an explicit
check against a public value.
Impact
Severity: Critical
A violation means a malicious prover can supply manipulated private inputs that lead to a proof that verifies on-chain but represents an invalid state transition, and enable finalization of a fraudulent super root.
iZKVM-002: Super Root Preimage Must Commit to All Chain Outputs
The ZK program MUST verify the output root of every chain listed in the SuperRootProof preimage
and MUST produce a rootClaim that is the hash of that complete preimage. Omitting any chain from
the proof or producing a partial preimage hash MUST cause the program to fail.
Impact
Severity: Critical
A violation would allow a proof to finalize a super root that does not faithfully represent all chains in the interop set. Funds that do not exist on L2 could be withdrawn, or the state of a chain could be suppressed from the commitment.