ZK Verification of the State Transition Function

Table of Contents

ZK Verification Design

We verify the state transition function using a zkVM built using OpenVM. OpenVM enables on-chain verification of an implementation of the keystore state transition function written in Rust using additional intrinsics implemented as VM extensions in OpenVM.

The ZK verification proof has one public value pvsHash which is constructed as:

bytes32 pvsHash = keccak256(abi.encodePacked(
  parentOutputRoot,
  parentSequencerBatchCommitment,
  parentL1BatchCommitment,
  targetOutputRoot,
  targetSequencerBatchCommitment,
  targetL1BatchCommitment,
  proverRewardsAddress,
));
  • bytes32 parentOutputRoot: The parent output root will decommit to an associated tuple of (parentBlockhash, parentStateRoot, parentWithdrawalsRoot). Verification of the execution will begin from the state of this tuple.
  • bytes32 parentSequencerBatchCommitment: The sequencer batch commitment from which subsequent transactions are applied, skipping the transactions in this batch.
  • bytes32 parentL1BatchCommitment: The L1 batch commitment from which subsequent transactions are applied, skipping the transactions in this batch.
  • bytes32 targetOutputRoot: The target output root will decommit to an associated tuple of (targetBlockhash, targetStateRoot, targetWithdrawalsRoot) which represent the post-state of executing all the transactions in the range.
  • bytes32 targetSequencerBatchCommitment: The sequencer batch commitment of the batch until which to continue applying transactions.
  • bytes32 targetL1BatchCommitment: The L1 batch commitment of the batch until which to continue applying transactions.
  • address proverRewardsAddress: The address to which the prover reward is sent.

The ZK verification proof verifies the following version of the state transition function:

Applying the state transition function from rollup data committed to in parentSequencerBatchCommitment and parentL1BatchCommitment on rollup state committed to in parentOutputRoot and parentWithdrawalsRoot until sequencer batches up to index parentSequencerBatchIndex and L1 batches up to index parentL1BatchIndex results in the rollup state committed to in targetOutputRoot and targetWithdrawalsRoot.

Reading L1 state

For certain operations within the state transition function, it is necessary to read variables from the L1 state, such as gas and blob fee data. While a sequencer can directly query an L1 node for this data for preconfirmations, verifying the STF in ZK requires a root of trust for the L1 state which commits to the desired data. We use the blockhash as this root of trust, as it commits to all historical L1 state.

Open-Source Dependencies

The ZK verification of the state transition function is done using OpenVM, a performant and modular zkVM framework built for customization and extensibility. OpenVM is open-source and developed by contributors including Axiom, Scroll, and other individuals. It uses the following open-source dependencies:

  • Plonky3: A modular toolkit for building ZK proof systems. It was developed by Polygon Zero and has been audited.
  • halo2: A widely used elliptic curve based ZK proof system, which is deployed in production by teams including Axiom, Scroll, and Taiko. This also uses the following open-source libraries built on top of halo2:
    • halo2-lib: A core library for halo2 circuits developed by Axiom. This library is audited and used across the ZK ecosystem, including in production by Axiom and Scroll.
    • snark-verifier: A core library for proof aggregation in halo2 developed by Privacy & Scaling Explorations (PSE) and modified by Axiom. This library is audited and used across the ZK ecosystem, including in production by Axiom, Scroll, and Taiko.