ZK Fault Proof VM

Table of Contents

Overview

A ZK fault proof VM is a succinct proof system that verifies an L2 state transition through a single cryptographic proof rather than an interactive bisection game. It consists of two components:

  • ZK Program: an off-chain circuit that re-executes the L2 derivation and state transition 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 the Cannon Fault Proof VM. While Cannon bisects an execution trace down to a single MIPS instruction and proves it on L1, the ZK VM proves the entire block range — from a starting output root to a claimed output root — in one shot.

Unlike interactive fault proofs, the ZK VM requires no multi-round on-chain interaction. A single prove() call to the dispute game is sufficient to cryptographically establish correctness, provided the verifier is sound.

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 applying L2 derivation and executing the resulting transactions against the starting state yields the claimed output root at the specified L2 block number, given the observed L1 data up to l1Head.

The program is the ZK equivalent of the Fault Proof Program: it runs the same L2 derivation logic as the fault proof program but inside a zkVM, producing a proof instead of an interactive trace.

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:

FieldTypeDescription
l1Headbytes32L1 block hash at which the L1 state was sampled. Authenticates all observed L1 data.
startingOutputRootbytes32Output root of the parent game (or anchor state if parentIndex == type(uint32).max).
rootClaimbytes32The output root being asserted by this game.
l2SequenceNumberuint256L2 block number corresponding to rootClaim.
l2ChainIduint256L2 chain identifier. Replaces rollupConfigHash to avoid JSON serialization fragility.

l2ChainId scopes the proof to a specific chain. Config changes that affect execution semantics (e.g. a hard fork) are absorbed into a new absolutePrestate, not a new l2ChainId.

Output

The program produces a proof that commits to the public values. A proof that passes on-chain verification is the sole signal of correctness: it means the L2 derivation from startingOutputRoot under the given l1Head yields exactly rootClaim at l2SequenceNumber on chain l2ChainId.

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), which is derived deterministically from the circuit binary and structure.

Program updates (e.g. a bug fix or a new hard fork) MUST produce a new absolutePrestate. OPCM manages absolutePrestate per chain; 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 IZKVerifier interface.

Proof Generation

Proofs are generated off-chain by a prover that:

  1. Fetches the required L1 and L2 data up to l1Head.
  2. 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.
  3. Produces a proof blob (proofBytes).
  4. 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 needed to replay the state transition) 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 startingOutputRoot). 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, enabling finalization of a fraudulent output root.