OP Succinct

CI License Book

Overview

OP Succinct gives every OP Stack rollup the ability to become a ZK rollup.

It combines a few key technologies:

  • Kona, Optimism's Rust implementation of the OP Stack's state transition function
  • SP1, Succinct's state-of-the-art Rust zkVM
  • Succinct Prover Network, Succinct's low-latency, cost-effective proving API

OP Succinct is the only production-ready proving solution for the OP Stack and trusted by teams like Mantle and Phala.

Proving Options

Rollups can choose between two configurations:

  • ZK fault proofs (OP Succinct Lite) — only generate a zero-knowledge proof when there is a dispute
  • Validity proofs (OP Succinct) — generate a zero-knowledge proof for every transaction, eliminating disputes entirely

Both configurations offer meaningful advantages over the standard OP Stack design.

Comparison Table

Support and Community

All of this has been possible thanks to close collaboration with the core team at OP Labs.

Ready to upgrade your rollup with ZK? Contact us to get started with OP Succinct.

Architecture

System Overview

OP Succinct is a lightweight upgrade to the OP Stack that enables ZK-based finality.

This document explains the standard OP Stack components and the lightweight modifications OP Succinct adds to enable proving blocks with zero-knowledge proofs using SP1.

Standard OP Stack Design

In the specification of the standard OP Stack design, there are 4 main components.

  1. OP Geth: Execution engine for the L2.
  2. OP Batcher: Collects and batches users transactions efficiently and posts to L1.
  3. OP Node: Reads batch data from L1, and passes to OP Geth to perform state transitions.
  4. OP Proposer: Posts state roots from OP Node to L1. Enables withdrawal processing.

OP Stack Architecture

OP Succinct Design

Overview

OP Succinct is a lightweight upgrade to the OP Stack that enables ZK-based finality. Specifically, it upgrades a single on-chain contract and the op-proposer component. No changes are needed to op-geth, op-batcher, or op-node.

OP Succinct Design

Service Architecture

The OP Succinct Proposer is a new service that orchestrates the proving pipeline. It monitors L1 for posted batches, generates proofs, and submits them to L1 with ZK proofs.

  1. User transactions are processed by standard OP Stack components.
  2. Validity proofs are generated for ranges of blocks with the range program.
  3. Combine range proofs into a single aggregationproof that is cheaply verifiable on-chain.
  4. OP Succinct proposer submits aggregation proof to the on-chain contract.
  5. The on-chain contract verifies the proof and updates the L2 state root for withdrawals.

OP Succinct Architecture

Repository Structure

op-succinct
├── contracts      # Smart contracts for OP Succinct
│   ├── validity   # Validity contracts
│   ├── fp         # Fault proof contracts
├── validity       # Validity proposer and challenger implementation
├── fault-proof    # Fault proof system implementation
├── programs       # Range and aggregation program implementations
├── utils          # Shared utilities
│   ├── client     # Client-side utilities
│   ├── host       # Host-side utilities
│   └── build      # Build utilities
├── scripts        # Development and deployment scripts
└── elf            # Reproducible binaries

Contracts

The contracts directory contains the core contracts for OP Succinct.

Validity

  • OPSuccinctL2OutputOracle.sol: Modified version of L2OutputOracle.sol that implements validity proof verification. Compatible with OptimismPortal.sol.
  • OPSuccinctDisputeGame.sol: Validity proof verification contract that is compatible with the IDisputeGame.sol interface on OptimismPortal2.sol. Wraps the OPSuccinctL2OutputOracle.sol contract.

Fault Proof

The fault-proof directory contains the contracts for OP Succinct Lite.

  • OPSuccinctFaultDisputeGame.sol: Fault proof verification contract that is compatible with the IDisputeGame.sol interface on OptimismPortal2.sol. Implements the OP Succinct Lite fault proof challenge mechanism.
  • AccessManager.sol: Contract that manages access control for the fault proof system.

OP Succinct (Validity)

Quick Start

This guide will walk you through the steps to deploy OP Succinct for your OP Stack chain. By the end of this guide, you will have a deployed smart contract on L1 that is tracking the state of your OP Stack chain with mock SP1 proofs.

Prerequisites

  • Compatible RPCs. If you don't have these already, see Node Setup for more information.
    • L1 Archive Node (L1_RPC)
    • L1 Consensus (Beacon) Node (L1_BEACON_RPC)
    • L2 Execution Node (L2_RPC)
    • L2 Rollup Node (L2_NODE_RPC)
  • Foundry
  • Docker
  • Rust
  • Just

Info

On Ubuntu, you'll need some system dependencies to run the service: curl, clang, pkg-config, libssl-dev, ca-certificates, git, libclang-dev, llvm-dev, and jq. You can see the Dockerfile for more details.

Step 1: Set environment variables.

In the root directory, create a file called .env and set the following environment variables:

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Beacon Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
PRIVATE_KEYPrivate key for the account that will be deploying the contract.
ETHERSCAN_API_KEYEtherscan API key for verifying the deployed contracts.

Step 2: Deploy an SP1MockVerifier for verifying mock proofs

Deploy an SP1MockVerifier for verifying mock proofs by running the following command:

% just deploy-mock-verifier
[⠊] Compiling...
[⠑] Compiling 1 files with Solc 0.8.15
[⠘] Solc 0.8.15 finished in 615.84ms
Compiler run successful!
Script ran successfully.

== Return ==
0: address 0x4cb20fa9e6FdFE8FDb6CE0942c5f40d49c898646

....

In these deployment logs, 0x4cb20fa9e6FdFE8FDb6CE0942c5f40d49c898646 is the address of the SP1MockVerifier contract.

Step 3: Deploy the OPSuccinctL2OutputOracle contract

This contract is a modification of Optimism's L2OutputOracle contract which verifies a proof along with the proposed state root.

First, add the address of the SP1MockVerifier contract from the previous step to the .env file in the root directory.

ParameterDescription
VERIFIER_ADDRESSThe address of the SP1MockVerifier contract.

Now, you should have the following in your .env file:

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Beacon Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
PRIVATE_KEYPrivate key for the account that will be deploying the contract.
ETHERSCAN_API_KEYEtherscan API key for verifying the deployed contracts.
VERIFIER_ADDRESSThe address of the SP1MockVerifier contract.

Then, deploy the OPSuccinctL2OutputOracle contract by running the following command:

% just deploy-oracle    
...

== Return ==
0: address 0xde4656D4FbeaC0c0863Ab428727e3414Fa251A4C

In these deployment logs, 0xde4656D4FbeaC0c0863Ab428727e3414Fa251A4C is the address of the proxy for the OPSuccinctL2OutputOracle contract. This deployed proxy contract is used to track the verified state roots of the OP Stack chain on L1.

Step 4: Set op-succinct service environment variables

To start the mock op-succinct service, add the following parameters to the .env file in the root directory:

ParameterDescription
L2OO_ADDRESSThe address of the OPSuccinctL2OutputOracle contract from the previous step.
OP_SUCCINCT_MOCKWhen set to true, the op-succinct service will generate mock proofs. For this quick start guide, set to true.

Now, you should have the following in your .env file:

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Beacon Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
PRIVATE_KEYPrivate key for the account that will be deploying the contract and relaying proofs on-chain.
ETHERSCAN_API_KEYEtherscan API key for verifying the deployed contracts.
L2OO_ADDRESSThe address of the OPSuccinctL2OutputOracle contract from the previous step.
OP_SUCCINCT_MOCKWhen set to true, the op-succinct service will generate mock proofs. For this quick start guide, set to true.

Info

When running just the proposer, you won't need the ETHERSCAN_API_KEY or VERIFIER_ADDRESS environment variables. These are only required for contract deployment.

Step 5: Start the op-succinct service in mock mode.

We provide a Docker Compose file for running the op-succinct service.

Build the Docker Compose setup.

docker compose build

Run the Proposer

This command launches the op-succinct in the background.

After a few minutes, you should see the OP Succinct proposer start to generate mock range proofs. Once enough range proofs have been generated, a mock aggregation proof will be created and submitted to the L1.

docker compose up

To see the logs of the op-succinct service, run:

docker compose logs -f

To stop the op-succinct service, run:

docker compose stop

Contract Management

This section will show you how to configure and deploy the on-chain contracts required for OP Succinct in validity mode.

Environment Variables

This section will show you how to configure the environment variables required to deploy the OPSuccinctL2OutputOracle contract.

Required Parameters

When deploying or upgrading the OPSuccinctL2OutputOracle contract, the following parameters are required to be set in your .env file:

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Consensus (Beacon) Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
PRIVATE_KEYPrivate key for the account that will be deploying the contract.
ETHERSCAN_API_KEYEtherscan API key used for verifying the contract (optional).

Optional Advanced Parameters

You can configure additional parameters when deploying or upgrading the OPSuccinctL2OutputOracle contract in your .env file.

ParameterDescription
VERIFIER_ADDRESSDefault: Succinct's official Groth16 VerifierGateway. Address of the ISP1Verifier contract used to verify proofs. For mock proofs, this should be the address of the SP1MockVerifier contract.
SUBMISSION_INTERVALDefault: 10. The minimum interval in L2 blocks for a proof to be submitted. An aggregation proof can be posted for any range larger than this interval.
FINALIZATION_PERIOD_SECSDefault: 3600 (1 hour). The time period (in seconds) after which a proposed output becomes finalized and withdrawals can be processed.
STARTING_BLOCK_NUMBERDefault: The finalized block number on L2. The block number to initialize the contract from. OP Succinct will start proving state roots from this block number.
PROPOSERDefault: The address of the account associated with PRIVATE_KEY. If PRIVATE_KEY is not set, address(0). An Ethereum address authorized to submit proofs. Set to address(0) to allow permissionless submissions. Note: Use addProposer and removeProposer functions to update the list of approved proposers.
CHALLENGERDefault: The address of the account associated with PRIVATE_KEY. If PRIVATE_KEY is not set, address(0). Ethereum address authorized to dispute proofs. Set to address(0) for no challenging.
OWNERDefault: The address of the account associated with PRIVATE_KEY. If PRIVATE_KEY is not set, address(0). Ethereum address authorized to update the aggregationVkey, rangeVkeyCommitment, verifier, and rollupConfigHash parameters. Can also transfer ownership of the contract and
DEPLOY_PKDefault: The address of the account associated with PRIVATE_KEY. The private key of the account that will be deploying the contract.
ADMIN_PKDefault: The address of the account associated with PRIVATE_KEY. The private key of the account that will be deploying the contract.
PROXY_ADMINDefault: address(0). The address of the L1 ProxyAdmin contract used to manage the OPSuccinctL2OutputOracle proxy. More information can be found here.
OP_SUCCINCT_L2_OUTPUT_ORACLE_IMPLDefault: address(0). The address of an already-deployed OPSuccinctL2OutputOracle implementation contract. If this is not set, a new OPSuccinctL2OutputOracle implementation contract will be deployed.

Deploying OPSuccinctL2OutputOracle

Similar to the L2OutputOracle contract, the OPSuccinctL2OutputOracle is managed via an upgradeable proxy. Follow the instructions below to deploy the contract.

Prerequisites

  • Foundry
  • Configured RPCs. If you don't have these already, see Node Setup for more information.

1. Pull the version of OPSuccinctL2OutputOracle you want to deploy

Check out the latest release of op-succinct from here.

2. Configure your environment

First, ensure that you have the correct environment variables set in your .env file. See the Environment Variables section for more information.

3. Deploy OPSuccinctL2OutputOracle

To deploy the OPSuccinctL2OutputOracle contract, run the following command in /contracts.

just deploy-oracle

Optionally, you can pass the environment file you want to use to the command.

just deploy-oracle .env.example

This will deploy the OPSuccinctL2OutputOracle contract using the parameters in the .env.example file.

You will see the following output. The contract address that should be used is the proxy address.

% just deploy-oracle .env.example
    Finished `release` profile [optimized] target(s) in 0.40s
     Running `target/release/fetch-rollup-config --env-file .env.example`
[⠊] Compiling...
No files changed, compilation skipped
Script ran successfully.

== Return ==
0: address 0xa8A51b0a66FF2ee852a633cC2D59B6C1b47c7f00

...

In the logs above, the proxy address is 0xa8A51b0a66FF2ee852a633cC2D59B6C1b47c7f00.

Updating OPSuccinctL2OutputOracle Parameters

If you just need to update the aggregationVkey, rangeVkeyCommitment or rollupConfigHash parameters and not upgrade the contract itself, you can use the just update-parameters command.

The command will only update the parameters in the contract if they don't match the verification keys or the rollup config hash locally.

1. Configure your environment

First, ensure that you have the correct environment variables set in your .env file. See the Environment Variables section for more information.

2. Update the parameters

If you are updating the parameters with an EOA ADMIN key, you will execute the parameter update calls with the ADMIN key. If you do not have the ADMIN key, the call below will output the raw calldata for the parameter update calls, which can be executed by the "owner" in a separate context.

Updating Parameters with an EOA ADMIN key

To update the parameters of the OPSuccinctL2OutputOracle contract with an EOA ADMIN key, run the following command in /contracts.

just update-parameters

Updating Parameters with a non-EOA ADMIN key

If the owner of the OPSuccinctL2OutputOracle is not an EOA (e.g. multisig, contract), set EXECUTE_UPGRADE_CALL to false in your .env file. This will output the raw calldata for the parameter update calls, which can be executed by the owner in a separate context.

ParameterDescription
EXECUTE_UPGRADE_CALLSet to false to output the raw calldata for the parameter update calls.

Then, run the following command in /contracts.

just update-parameters
% just update-parameters
warning: op-succinct-scripts@0.1.0: fault-proof built with release-client-lto profile
warning: op-succinct-scripts@0.1.0: range built with release-client-lto profile
warning: op-succinct-scripts@0.1.0: native_host_runner built with release profile
    Finished `release` profile [optimized] target(s) in 0.35s
     Running `target/release/fetch-rollup-config --env-file .env`
[⠊] Compiling...
Script ran successfully.

== Logs ==
  The calldata for upgrading the aggregationVkey is:
  0xc4cb03ec005e5786785a9c61015fa1f0543831fb0e0602684473de8758496556010b1d08
  The calldata for upgrading the rangeVkeyCommitment is:
  0xbc91ce33472e8f9b2f650ae74c7997a3272ef5b50be834145b44cf7f1d52b58235bd6018

Upgrading OPSuccinctL2OutputOracle

Similar to the L2OutputOracle contract, the OPSuccinctL2OutputOracle is managed via an upgradeable proxy. The upgrade process is the same as the L2OutputOracle contract.

1. Decide on the target OPSuccinctL2OutputOracle contract code

Check out the latest release of op-succinct from here. You can always find the latest version of the OPSuccinctL2OutputOracle on the latest release.

Manual Changes to OPSuccinctL2OutputOracle

If you want to manually upgrade the OPSuccinctL2OutputOracle contract, follow these steps:

  1. Make the relevant changes to the OPSuccinctL2OutputOracle contract.

  2. Then, manually bump the initializerVersion version in the OPSuccinctL2OutputOracle contract. You will need to manually bump the initializerVersion version in the OPSuccinctL2OutputOracle contract to keep track of the upgrade history.

// Original initializerVersion
uint256 public constant initializerVersion = 1;
// Increment the initializerVersion to 2.
uint256 public constant initializerVersion = 2;

2. Configure your environment

Ensure that you have the correct environment variables set in your environment file. See the Environment Variables section for more information.

3. Upgrade the OPSuccinctL2OutputOracle contract

Get the address of the existing OPSuccinctL2OutputOracle contract. If you are upgrading with an EOA ADMIN key, you will execute the upgrade call with the ADMIN key. If you do not have the ADMIN key, the call below will output the raw calldata for the upgrade call, which can be executed by the "owner" in a separate context.

Upgrading with an EOA ADMIN key

To update the L2OutputOracle implementation with an EOA ADMIN key, run the following command in /contracts.

just upgrade-oracle

Upgrading with a non-EOA ADMIN key

If the owner of the L2OutputOracle is not an EOA (e.g. multisig, contract), set EXECUTE_UPGRADE_CALL to false in your .env file. This will output the raw calldata for the upgrade call, which can be executed by the owner in a separate context.

ParameterDescription
EXECUTE_UPGRADE_CALLSet to false to output the raw calldata for the upgrade call.

Then, run the following command in /contracts.

just upgrade-oracle
% just upgrade-oracle
warning: op-succinct-scripts@0.1.0: fault-proof built with release-client-lto profile
warning: op-succinct-scripts@0.1.0: range built with release-client-lto profile
warning: op-succinct-scripts@0.1.0: native_host_runner built with release profile
    Finished `release` profile [optimized] target(s) in 0.35s
     Running `target/release/fetch-rollup-config --env-file .env`
[⠊] Compiling...
Script ran successfully.

== Logs ==
  The calldata for upgrading the contract with the new initialization parameters is:
  0x4f1ef2860000000000000000000000007f5d6a5b55ee82090aedc859b40808103b30e46900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000184c0e8e2a100000000000000000000000000000000000000000000000000000000000004b00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000135161100000000000000000000000000000000000000000000000000000000674107ce000000000000000000000000ded0000e32f8f40414d3ab3a830f735a3553e18e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001346d7ea10cc78c48e3da6f1890bb16cf27e962202f0f9b2c5c57f3cfb0c559ec1dca031e9fc246ec47246109ebb324a357302110de5d447af13a07f527620000000000000000000000004cb20fa9e6fdfe8fdb6ce0942c5f40d49c8986469ad6f24abc0df5e4cab37c1efae21643938ed5393389ce9e747524a59546a8785e32d5f9f902c6a46cb75cbdb083ea67b9475d7026542a009dc9d99072f4bdf100000000000000000000000000000000000000000000000000000000

## Setting up 1 EVM.

OptimismPortal2 Support

Overview

This section demonstrates how OP Succinct can be used with OptimismPortal2 by conforming to Optimism's IDisputeGame.

The OPSuccinctDisputeGame contract is a thin wrapper around OPSuccinctL2OutputOracle that implements IDisputeGame.

Set dispute game to OPSuccinctDisputeGame

OptimismPortal2 requires a DisputeGameFactory contract which manages the lifecycle of dispute games, and the current active dispute game.

To use OP Succinct with OptimismPortal2, you must set the canonical dispute game implementation to OPSuccinctDisputeGame.

No Existing DisputeGameFactory (Testing)

If you don't have a DisputeGameFactory contract or OptimismPortal2 setup and want to test OP Succinct with the DisputeGameFactory contract, follow these steps:

After deploying the OPSuccinctL2OutputOracle contract, set the following environment variables in your .env file:

VariableDescriptionExample
L2OO_ADDRESSAddress of the OPSuccinctL2OutputOracle contract0x123...
PROPOSER_ADDRESSESComma-separated list of addresses allowed to propose games.0x123...,0x456...

Run the following command to deploy the OPSuccinctDisputeGame contract:

just deploy-dispute-game-factory

If successful, you should see the following output:

[⠊] Compiling...
[⠊] Compiling 1 files with Solc 0.8.15
[⠒] Solc 0.8.15 finished in 1.93s
Compiler run successful!
Script ran successfully.

== Return ==
0: address 0x6B3342821680031732Bc7d4E88A6528478aF9E38

In these deployment logs, 0x6B3342821680031732Bc7d4E88A6528478aF9E38 is the address of the proxy for the DisputeGameFactory contract.

Existing DisputeGameFactory

If you already have a DisputeGameFactory contract, you must call the setImplementation function to set the canonical dispute game implementation to OPSuccinctDisputeGame.

    // The game type for the OP_SUCCINCT proof system.
    GameType gameType = GameType.wrap(uint32(6));

    // Set the canonical dispute game implementation.
    gameFactory.setImplementation(gameType, IDisputeGame(address(game)));

Use OP Succinct with OptimismPortal2

Once you have a DisputeGameFactory contract, you can use OP Succinct with OptimismPortal2 by setting the DGF_ADDRESS environment variable with the address of the DisputeGameFactory contract in your .env file.

With this environment variable set, the proposer will create, initialize and finalize a new OPSuccinctDisputeGame contract on the DisputeGameFactory contract with every aggregation proof.

Toggle Optimistic Mode

Optimistic mode is a feature that allows the L2OutputOracle to accept outputs without verification (mirroring the permissioned L2OutputOracle contract). This is useful for testing and development purposes, and as a fallback for OPSuccinctL2OutputOracle in the event of an outage.

When optimistic mode is enabled, the OPSuccinctL2OutputOracle's proposeL2Output function will match the interface of the original L2OutputOracle contract, with the modification that the proposer address must be in the approvedProposers mapping, or permissionless proposing must be enabled.

Enable Optimistic Mode

Warning: If you had permissionless proving enabled in non-optimistic (OP Succinct) mode, ensure that address(0) is set to false in the approvedProposers mapping before enabling optimistic mode. If address(0) remains approved, any account will be able to submit outputs without verification in optimistic mode.

To enable optimistic mode, call the enableOptimisticMode function on the OPSuccinctL2OutputOracle contract.

function enableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenNotOptimistic {
    finalizationPeriodSeconds = _finalizationPeriodSeconds;
    optimisticMode = true;
    emit OptimisticModeToggled(true, _finalizationPeriodSeconds);
}

Ensure that the finalizationPeriodSeconds is set to a value that is appropriate for your use case. The standard setting is 1 week (604800 seconds).

The finalizationPeriodSeconds should never be 0.

Disable Optimistic Mode

By default, optimistic mode is disabled. To switch back to validity mode, call the disableOptimisticMode function on the OPSuccinctL2OutputOracle contract.

function disableOptimisticMode(uint256 _finalizationPeriodSeconds) external onlyOwner whenOptimistic {
    finalizationPeriodSeconds = _finalizationPeriodSeconds;
    optimisticMode = false;
    emit OptimisticModeToggled(false, _finalizationPeriodSeconds);
}

Set the finalizationPeriodSeconds to a value that is appropriate for your use case. An example configuration is 1 hour (3600 seconds).

The finalizationPeriodSeconds should never be 0.

Validity Proposer

In validity mode, OP Succinct will generate validity proofs for all OP Stack transactions.

The following guide will walk you through the steps of configuring the OP Succinct proposer service in validity mode for your chain. Proceed with this section after you have deployed the OPSuccinctL2OutputOracle contract or the OPSuccinctDisputeGame contract.

Proposer

The op-succinct service monitors the state of the L2 chain, requests proofs from the Succinct Prover Network[https://docs.succinct.xyz/docs/network/introduction] and submits them to the L1.

RPC Requirements

Confirm that your RPC's have all of the required endpoints as specified in the prerequisites section.

Hardware Requirements

We recommend the following hardware configuration for the default op-succinct validity service (1 concurrent proof request & 1 concurrent witness generation thread):

Using the docker compose file:

  • Full op-succinct service: 2 vCPUs, 4GB RAM.
  • Mock op-succinct service: 2 vCPUs, 8GB RAM. Increased memory because the machine is executing the proofs locally.

Depending on the number of concurrent requests you want to run, you may need to increase the number of vCPUs and memory allocated to the op-succinct container.

Environment Setup

Make sure to include all of the required environment variables in the .env file.

Before starting the proposer, ensure you have deployed the relevant contracts and have the address of the proxy contract ready. Follow the steps in the Environment Variables section.

Required Environment Variables

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Consensus (Beacon) Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
NETWORK_PRIVATE_KEYKey for the Succinct Prover Network. Get access here.
L2OO_ADDRESSAddress of the OPSuccinctL2OutputOracle contract.
PRIVATE_KEYPrivate key for the account that will be posting output roots to L1.

Optional Environment Variables

ParameterDescription
NETWORK_RPC_URLDefault: https://rpc.production.succinct.xyz. RPC URL for the Succinct Prover Network.
DATABASE_URLDefault: postgres://op-succinct@postgres:5432/op-succinct. The address of a Postgres database for storing the intermediate proposer state.
DGF_ADDRESSAddress of the DisputeGameFactory contract. Note: If set, the proposer will create a dispute game with the DisputeGameFactory, rather than the OPSuccinctL2OutputOracle. Compatible with OptimismPortal2.
RANGE_PROOF_STRATEGYDefault: reserved. Set to hosted to use hosted proof strategy.
AGG_PROOF_STRATEGYDefault: reserved. Set to hosted to use hosted proof strategy.
AGG_PROOF_MODEDefault: groth16. Set to plonk to use PLONK proof type. Note: The verifier gateway contract address must be updated to use PLONK proofs.
SUBMISSION_INTERVALDefault: 1800. The number of L2 blocks that must be proven before a proof is submitted to the L1. Note: The interval used by the validity service is always >= to the submissionInterval configured on the L2OO contract. To allow for the validity service to configure this parameter entirely, set the submissionInterval in the contract to 1.
RANGE_PROOF_INTERVALDefault: 1800. The number of blocks to include in each range proof. For chains with high throughput, you need to decrease this value.
MAX_CONCURRENT_PROOF_REQUESTSDefault: 1. The maximum number of concurrent proof requests (in mock and real mode).
MAX_CONCURRENT_WITNESS_GENDefault: 1. The maximum number of concurrent witness generation requests.
OP_SUCCINCT_MOCKDefault: false. Set to true to run in mock proof mode. The OPSuccinctL2OutputOracle contract must be configured to use an SP1MockVerifier.
METRICS_PORTDefault: 8080. The port to run the metrics server on.
LOOP_INTERVALDefault: 60. The interval (in seconds) between each iteration of the OP Succinct service.
PROVER_ADDRESSAddress of the account that will be posting output roots to L1. This address is committed to when generating the aggregation proof to prevent front-running attacks. It can be different from the signing address if you want to separate these roles. Default: The address derived from the PRIVATE_KEY environment variable.
SIGNER_URLURL for the Web3Signer. Note: This takes precedence over the PRIVATE_KEY environment variable.
SIGNER_ADDRESSAddress of the account that will be posting output roots to L1. Note: Only set this if the signer is a Web3Signer. Note: Required if SIGNER_URL is set.
SAFE_DB_FALLBACKDefault: false. Whether to fallback to timestamp-based L1 head estimation even though SafeDB is not activated for op-node. When false, proposer will panic if SafeDB is not available. It is by default false since using the fallback mechanism will result in higher proving cost.

Build the Proposer Service

Build the OP Succinct validity service.

docker compose build

Run the Proposer

Run the OP Succinct validity service.

docker compose up

To see the logs of the OP Succinct services, run:

docker compose logs -f

After several minutes, the validity service will start to generate range proofs. Once enough range proofs have been generated, an aggregation proof will be created. Once the aggregation proof is complete, it will be submitted to the L1.

To stop the OP Succinct validity service, run:

docker compose stop

Proposer Lifecycle

The proposer service monitors the state of the L2 chain, requests proofs and submits them to the L1. Proofs are submitted to the Succinct Prover Network. Here's how the proposer service decides when to request range and aggregation proofs.

Range Proof Lifecycle

stateDiagram-v2
    [*] --> Unrequested: New Request for Range of Blocks
    Unrequested --> WitnessGeneration: make_proof_request
    WitnessGeneration --> Execution: mock=true
    WitnessGeneration --> Prove: mock=false
    Execution --> Complete: generate_mock_range_proof
    Execution --> Failed: error
    Prove --> Complete: proof fulfilled
    Prove --> Failed: proof unfulfillable/error
    Failed --> Split: range AND (2+ failures OR unexecutable)
    Split --> Unrequested: two new smaller ranges
    Failed --> Unrequested: retry same range

Aggregation Proof Lifecycle

stateDiagram-v2
    [*] --> Unrequested: Total interval of completed range proofs > submissionInterval.
    Unrequested --> WitnessGeneration: make_proof_request
    WitnessGeneration --> Execution: mock=true
    WitnessGeneration --> Prove: mock=false
    Execution --> Complete: generate_mock_agg_proof
    Execution --> Failed: error
    Prove --> Complete: proof fulfilled
    Prove --> Failed: proof unfulfillable/error
    Failed --> Unrequested: retry same range
    Complete --> Relayed: submit_agg_proofs

Proposer Operations

The proposer performs the following operations each loop:

  1. Validates that the requester config matches the contract configuration
  2. Logs proposer metrics like number of requests in each state
  3. Handles ongoing tasks by checking completed/failed tasks and cleaning them up
  4. Sets orphaned tasks (in WitnessGeneration/Execution but not in tasks map) to Failed status
  5. Gets proof statuses for all requests in proving state from the Prover Network
  6. Adds new range requests to cover gaps between latest proposed and finalized blocks. If a request failed, this is where the request is re-tried.
  7. Creates aggregation proofs from completed contiguous range proofs.
  8. Requests proofs for any unrequested proofs from the prover network/generates mock proofs.
  9. Submits any completed aggregation proofs to the L2 output oracle contract.

Upgrading OP Succinct

Each new release of op-succinct will specify if it includes:

  • New verification keys
  • Contract changes
  • New op-succinct binary version

Based on what's included:

  • Contract changes → Upgrade the relevant contracts
  • New verification keys → Update aggregationVkey, rangeVkeyCommitment and rollupConfigHash parameters
  • New binary → Upgrade Docker images

Upgrade Contract

  1. Check out the latest release of op-succinct from here.
  2. Follow the instructions here to upgrade the relevant contracts.

Update Contract Parameters

If you just need to update the aggregationVkey, rangeVkeyCommitment or rollupConfigHash parameters and not upgrade the contract itself, follow these steps:

  1. Check out the latest release of op-succinct from here.
  2. Follow the instructions here to update the parameters of the relevant contracts.

Upgrade Docker Images

If you're using Docker, you can use the images associated with the latest release of OP Succinct from GitHub Container Registry (GHCR) here: https://github.com/succinctlabs/op-succinct/pkgs/container/op-succinct%2Fop-succinct.

For example, to use version v2.0.0, you can use ghcr.io/succinctlabs/op-succinct:v2.0.0.

Experimental Features

This section covers experimental features for OP Succinct (Validity).

Celestia DA

The op-succinct-celestia service monitors the state of an OP Stack chain with Celestia DA enabled, requests proofs from the Succinct Prover Network[https://docs.succinct.xyz/docs/network/introduction] and submits them to the L1.

For more information, see the Celestia DA section.

Celestia Data Availability

This section describes the requirements to use OP Succinct for a chain with Celestia DA. The requirements are additive to the ones required for the op-succinct service. Please refer to the Proposer section for the base configuration.

Environment Setup

To use Celestia DA, you need additional environment variables in the .env file:

ParameterDescription
CELESTIA_CONNECTIONURL of the Celestia light node RPC endpoint. For setup instructions, see Celestia's documentation on light node.
NAMESPACENamespace ID for the Celestia DA. A namespace is a unique identifier that allows applications to have their own data availability space within Celestia's data availability layer. For more details, see Celestia's documentation on Namespaced Merkle Trees (NMTs).

Run the Celestia Proposer Service

Run the op-succinct-celestia service.

docker compose -f docker-compose-celestia.yml up -d

To see the logs of the op-succinct-celestia service, run:

docker compose -f docker-compose-celestia.yml logs -f

To stop the op-succinct-celestia service, run:

docker compose -f docker-compose-celestia.yml down

Deploying OPSuccinctL2OutputOracle with Celestia features

just deploy-oracle .env celestia

Updating OPSuccinctL2OutputOracle Parameters

just update-parameters .env celestia

For more details on the just update-parameters command, see the Updating OPSuccinctL2OutputOracle Parameters section.

OP Succinct Lite (Fault Proofs)

Architecture

Overview

OP Succinct Lite offers a powerful, configurable fault proof system that enables a simplified on-chain dispute mechanism, reduced time to finality, configurable fast finality and support for Alt-DA.

  • The same OP Succinct program is used for both validity and fault proof modes.
  • OPSuccinctFaultDisputeGame contract integrated with OP Stack's DisputeGameFactory.
  • Single-round dispute resolution with ZK proofs.

We assume that the reader has a solid understanding of the OP Stack's DisputeGameFactory and IDisputeGame interface. Documentation can be found here. We implement the IDisputeGame interface with a ZK-enabled fault proof (using the OP Succinct ZK program) instead of the standard interactive bisection game that the vanilla OP Stack uses.

Core Concepts

  1. Proposals: Each proposal represents a claimed state transition from a start L2 block to an end L2 block with a startL2OutputRoot and a claimedL2OutputRoot where the output root is a commitment to the entirety of L2 state.
  2. Challenges: Participants can challenge proposals they believe are invalid.
  3. Proofs: ZK proofs that verify the correctness of state transitions contained in proposals, anchored against an L1 block hash.
  4. Resolution: Process of determining whether a proposal is valid or not.

Dispute Game Implementation

Proposing new state roots goes through the regular flow of the DisputeGameFactory to the OPSuccinctFaultDisputeGame contract that implements the IDisputeGame interface. Each proposal contains a link to a previous parent proposal (unless it is the first proposal after initialization, in which case it stores the parent index as uint32.max), and includes a l2BlockNumber and claimed l2OutputRoot.

Once a proposal is published and a OPSuccinctFaultDisputeGame created, the dispute game can be in one of several states:

  • Unchallenged: The initial state of a new proposal.
  • Challenged: A proposal that has been challenged but not yet proven.
  • UnchallengedAndValidProofProvided: A proposal that has been proven valid with a verified proof.
  • ChallengedAndValidProofProvided: A challenged proposal that has been proven valid with a verified proof.
  • Resolved: The final state after resolution, either GameStatus.CHALLENGER_WINS or GameStatus.DEFENDER_WINS.

Note that "challenging" a proposal does not require a proof--as we want challenges to be able to be submitted quickly, without waiting for proof generation delay. Once a challenge is submitted, then the proposal's "timeout" is set to MAX_PROVE_DURATION parameter that allows for an extended amount of time to generate a proof to prove that the original proposal is correct. If a proof of validity is not submitted by the deadline, then the proposal is assumed to be invalid and the challenger wins. If a valid proof is submitted by the deadline, then the original proposer wins the dispute. Note that if a parent game is resolved in favor of a challenger winning, then any child game will also be considered invalid.

Illustrative Example

graph TB
    A[Genesis State
    Block: 0
    Root: 0x123] --> |Parent| B[Proposal 1
    Block: 1000
    Root: 0xabc
    Game Status: DEFENDER_WINS]
    B --> |Parent| C[Proposal 2A
    Block: 2000
    Root: 0xdef
    Game Status: CHALLENGER_WINS]
    B --> |Parent| D[Proposal 2B
    Block: 2000
    Root: 0xff1
    Game Status: Unchallenged]
    C --> |Parent| E[Proposal 3A
    Block: 3000
    Root: 0xbee
    Game Status: Unchallenged]
    D --> |Parent| F[Proposal 3B
    Block: 3000
    Root: 0xfab
    Game Status: ChallengedAndValidProofProvided]

    classDef genesis fill:#d4edda,stroke:#155724
    classDef defender_wins fill:#28a745,stroke:#1e7e34,color:#fff
    classDef challenger_wins fill:#dc3545,stroke:#bd2130,color:#fff
    classDef unchallenged fill:#e2e3e5,stroke:#383d41
    classDef proven fill:#17a2b8,stroke:#138496,color:#fff

    class A genesis
    class B defender_wins
    class C challenger_wins
    class D,E unchallenged
    class F proven

In this example, Proposal 3A would always resolve to CHALLENGER_WINS, as its parent 2A has CHALLENGER_WINS. Proposal 3B would resolve to DEFENDER_WINS if and only if its parent 2B successfully is unchallenged past its deadline and the final status is DEFENDER_WINS.

Contract Description

Immutable Variables

VariableDescription
MAX_CHALLENGE_DURATIONTime window during which a proposal can be challenged.
MAX_PROVE_DURATIONTime allowed for proving a challenge.
GAME_TYPEThe type of the game, which is set in the DisputeGameFactory contract.
DISPUTE_GAME_FACTORYThe factory contract that creates this game.
SP1_VERIFIERThe verifier contract that verifies the SP1 proof.
ROLLUP_CONFIG_HASHHash of the chain's rollup configuration
AGGREGATION_VKEYThe verification key for the aggregation SP1 program.
RANGE_VKEY_COMMITMENTThe commitment to the BabyBear representation of the verification key of the range SP1 program.
CHALLENGER_BONDAmount of ETH required to submit a challenge. If a prover supplies a valid proof, the bond is disbursed to the prover.
ANCHOR_STATE_REGISTRYThe anchor state registry contract.
ACCESS_MANAGERThe access manager contract.

Types

ProposalStatus

While GameStatus (IN_PROGRESS, DEFENDER_WINS, CHALLENGER_WINS) represents the final outcome of the game, we need ProposalStatus to:

  1. allow proving for fast finality even if the proposal is unchallenged.
  2. allow anyone to prove a proposal even the prover is not the proposer.

Represents the current state of a proposal in the dispute game:

enum ProposalStatus {
    Unchallenged,                        // Initial state: New proposal without challenge
    Challenged,                          // Challenged by someone, awaiting proof
    UnchallengedAndValidProofProvided,   // Valid proof provided without any challenge
    ChallengedAndValidProofProvided,     // Valid proof provided after being challenged
    Resolved                             // Final state after game resolution
}

Proposal Status Transitions:

  • Unchallenged
    • Challenged (via challenge())
    • UnchallengedAndValidProofProvided (via prove())
    • Resolved (via resolve())
  • Challenged
    • ChallengedAndValidProofProvided (via prove())
    • Resolved (via resolve())
  • UnchallengedAndValidProofProvidedResolved (via resolve())
  • ChallengedAndValidProofProvidedResolved (via resolve())

ClaimData

Core data structure holding the state of a proposal:

struct ClaimData {
    uint32 parentIndex;        // Reference to parent game (uint32.max for first game)
    address counteredBy;       // Address of challenger (address(0) if unchallenged)
    address prover;            // Address that provided valid proof (address(0) if unproven)
    Claim claim;               // The claimed L2 output root
    ProposalStatus status;     // Current status of the proposal
    Timestamp deadline;        // Time by which proof must be provided
}

Key differences from Optimism's implementation:

  • parentIndex is initialized on initialize().
  • Simplified to single claim instead of array of claims.
  • Removed claimant and position fields since there is no bisection.
  • Removed bond field since bonds are stored in the game contract.
  • Added prover field to enable anyone to prove a proposal even the prover is not the proposer.
  • Added proposal status.
  • Uses deadline instead of clock with period getters.

Key Functions

Initialization

function initialize() external payable virtual

Initializes the dispute game with:

  • Initial state of the game

    • startingOutputRoot: Starting point for verification

      • For first game: Anchor state root for the game type
      • For subsequent games: Parent game's root claim and block number
    • claimData: Core game state

      • parentIndex: Reference to the parent game (uint32.max for first game)
      • counteredBy: Initially address(0)
      • prover: Initially address(0)
      • claim: Proposer's claimed output root
      • status: Set to ProposalStatus.Unchallenged
      • deadline: Set to block.timestamp + MAX_CHALLENGE_DURATION
  • Validation Rules

    • Parent Game (if not first game)

      • Must not have been blacklisted
      • Must have been respected game type when created
      • Must not have been won by challenger
    • Output Root

      • Must be after starting block number
      • First game starts from the anchor state root for the game type
      • Later games start from parent's output root
  • Bond Management

    • Proposer's bond enforced by factory
    • Held in contract until resolution

Initialization will revert if:

  • Already initialized
  • Invalid parent game
  • Root claim at/before starting block
  • Incorrect calldata size for extraData
  • Proposer is not whitelisted

Challenge

function challenge() external payable returns (ProposalStatus)

Allows participants to challenge a proposal by:

  • Depositing the challenger bond (the proof reward)
  • Setting the proposal deadline to be + provingTime over the current timestamp
  • Updating proposal state to ProposalStatus.Challenged

Attempting to challenge a game will revert if:

  • Game is over (past deadline or already proven)
  • Challenger is not whitelisted
  • Incorrect bond amount provided

Proving

function prove(bytes calldata proofBytes) external returns (ProposalStatus)

Validates a proposal with a proof:

  • Timing Requirements

    • Must be submitted before the game is over (deadline passed or already proven)
  • Proof Verification

    • Uses SP1 verifier to validate the aggregation proof against public inputs:
      • L1 head hash (from game creation)
      • Starting output root (from parent game or anchor state root)
      • Claimed output root
      • Claimed L2 block number
      • Rollup configuration hash
      • Range verification key commitment
    • Uses aggregation verification key to verify the proof
  • State Updates

    • Records the prover's address in claimData.prover
    • Updates proposal status if the proof is valid:
      • ProposalStatus.UnchallengedAndValidProofProvided if there was no challenge
      • ProposalStatus.ChallengedAndValidProofProvided if there was a challenge
  • Rewards

    • No immediate reward distribution
    • Proof reward is distributed in claimCredit():
      • If challenged: prover receives the challenger's bond
      • If unchallenged: no reward but can have fast finality

Attempting to submit a proof will revert if:

  • Proof is not submitted before the proof deadline
  • Proof is not valid
  • If a prover tries to prove a game that has already been proven

Resolution

function resolve() external returns (GameStatus)

Resolves the game by:

  • Checking parent game status:

    • Must be resolved
    • Must be respected
    • Must not be blacklisted
    • Must not be retired
    • If parent game is CHALLENGER_WINS, current game automatically resolves to CHALLENGER_WINS
  • For other cases:

    • Game must be over (past deadline or proven)
    • Resolution depends on final state:
      • Unchallenged: DEFENDER_WINS, proposer gets its bond back
      • Challenged: CHALLENGER_WINS, challenger gets everything
      • UnchallengedAndValidProofProvided: DEFENDER_WINS, proposer gets its bond back
      • ChallengedAndValidProofProvided: DEFENDER_WINS, prover gets challenger bond, proposer gets its bond back

Attempting to resolve will revert if:

  • Parent game is not yet resolved
  • Game is already resolved (i.e, not IN_PROGRESS)
  • Challenge/proof window has not expired

Reward Distribution

function claimCredit(address _recipient) external

Claims rewards by:

  • Closing the game and determining bond distribution mode if not already set
  • Checking recipient's credit balance based on distribution mode:
    • NORMAL: Uses normalModeCredit for standard game outcomes
    • REFUND: Uses refundModeCredit when game is improper
  • Setting recipient's credit balance to 0
  • Transferring ETH to recipient

Attempting to claim will revert if:

  • Game is not resolved
  • Game is not finalized according to AnchorStateRegistry
  • Recipient has no credit to claim
  • ETH transfer fails
  • Invalid bond distribution mode

Bond distribution modes:

  • NORMAL: Standard outcome-based distribution
    • Defender wins unchallenged: Proposer gets full bond
    • Defender wins challenged: Prover gets reward, proposer gets remainder
    • Challenger wins: Challenger gets full bond
  • REFUND: Returns bonds to original depositors when game is improper

Security Model

Bond System

The contract implements a bond system to incentivize honest behavior:

  1. Proposal Bond: Required to submit a proposal.
  2. Challenger Bond (Proof Reward): Required to challenge a proposal, which is paid to successful provers.

Time Windows

Two key time windows ensure fair participation:

  1. Challenge Window: Period during which proposals can be challenged.
  2. Proving Window: Time allowed for submitting proofs after a challenge.

Parent-Child Relationships

  • Each game (except genesis) has a parent
  • Invalid parent automatically invalidates children
  • Child games can only be resolved if their parent game is resolved

Acknowledgements

Special thanks to Kelvin Fichter for his invaluable contributions with thorough design reviews, technical guidance, and insightful feedback throughout the development process.

Zach Obront, who worked on the first version of OP Succinct, prototyped a similar MultiProof dispute game implementation with OP Succinct as part of his work with the Ithaca team. This fault proof implementation takes some inspiration from his multiproof work.

Quick Start Guide: OP Succinct Fault Dispute Game

This guide provides the fastest path to try out OP Succinct fault dispute games by deploying contracts and running a proposer to create games.

Prerequisites

  • Foundry
  • Rust (latest stable version)
  • L1 and L2 archive node RPC endpoints. L2 node should be configured with SafeDB enabled. See SafeDB Configuration for more details.
  • ETH on L1 for:
    • Contract deployment
    • Game bonds (configurable in factory)
    • Challenge bonds (proof rewards)
    • Transaction fees

Step 1: Deploy Contracts

  1. Clone and setup the repository:

    git clone https://github.com/succinctlabs/op-succinct.git
    cd op-succinct/contracts
    forge install
    
  2. Create a .env file in the contracts directory:

    # example .env file
    
    # Required - set these values
    GAME_TYPE=42
    DISPUTE_GAME_FINALITY_DELAY_SECONDS=604800
    MAX_CHALLENGE_DURATION=604800
    MAX_PROVE_DURATION=86400
    STARTING_ROOT=0xbd4ab027af3c4db0c6b1329dad6d9f8e2505accabec75bfaa2b6b8033c1e60c5...
    STARTING_L2_BLOCK_NUMBER=791000
    
    # For testing, use mock verifier
    USE_SP1_MOCK_VERIFIER=true
    

See Getting the Starting Root for more information on how to get the starting root.

  1. Deploy contracts:
    forge script script/fp/DeployOPSuccinctFDG.s.sol --broadcast --rpc-url <L1_RPC_URL> --private-key <YOUR_PRIVATE_KEY>
    

Save the output addresses, particularly the FACTORY_ADDRESS output as "Factory Proxy: 0x..."

Step 2: Run the Proposer

  1. Create a .env.proposer file in the project root directory:

    # Required Configuration
    L1_RPC=<YOUR_L1_RPC_URL>
    L2_RPC=<YOUR_L2_RPC_URL>
    FACTORY_ADDRESS=<FACTORY_ADDRESS_FROM_DEPLOYMENT>
    GAME_TYPE=42
    PRIVATE_KEY=<YOUR_PRIVATE_KEY>
    MOCK_MODE=true # Set to true for mock mode
    
  2. Run the proposer:

    cargo run --bin proposer
    

(Optional) Run with fast finality mode

  1. Configure more environment variables to the .env.proposer file in the project root directory:
    FAST_FINALITY_MODE=true
    NETWORK_PRIVATE_KEY=0x...
    L1_BEACON_RPC=<L1_BEACON_RPC_URL>
    L2_NODE_RPC=<L2_NODE_RPC_URL>
    

To get a whitelisted key on the Succinct Prover Network for OP Succinct, fill out this form. The Succinct team will reach out to you with an RPC endpoint you can use.

  1. Run the proposer:
    cargo run --bin proposer
    

Step 3: Run the Challenger

  1. Create a .env.challenger file in the project root directory:

    # Required Configuration
    L1_RPC=<YOUR_L1_RPC_URL>
    L2_RPC=<YOUR_L2_RPC_URL>
    FACTORY_ADDRESS=<FACTORY_ADDRESS_FROM_DEPLOYMENT>
    GAME_TYPE=42
    PRIVATE_KEY=<YOUR_PRIVATE_KEY>
    
  2. Run the challenger:

    cargo run --bin challenger
    

Monitoring

  • Games are created every 1800 blocks by default (via PROPOSAL_INTERVAL_IN_BLOCKS in .env.proposer. See Optional Environment Variables for Proposer)
  • Track games via block explorer using factory/game addresses and tx hashes from logs
  • Both proposer and challenger attempt to resolve eligible games after challenge period

Troubleshooting

Common issues:

  • Deployment fails: Check RPC connection and ETH balance
  • Proposer won't start: Verify environment variables and addresses
  • Games not creating: Check proposer logs for errors and L1,L2 RPC endpoints

For detailed configuration and advanced features, see:

Contracts

Deploying OP Succinct Fault Dispute Game

This guide explains how to deploy the OP Succinct Fault Dispute Game contracts using the DeployOPSuccinctFDG.s.sol script.

Overview

The deployment script performs the following actions:

  1. Deploys the DisputeGameFactory implementation and proxy.
  2. Deploys the AnchorStateRegistry implementation and proxy.
  3. Deploys a mock OptimismPortal2 for testing.
  4. Deploys the AccessManager and configures it for permissionless games.
  5. Deploys either a mock SP1 verifier for testing or uses a provided verifier address.
  6. Deploys the OPSuccinctFaultDisputeGame implementation.
  7. Configures the factory with initial bond and game implementation.

Prerequisites

  • Foundry installed.
  • Access to an Ethereum node (local or network).
  • Environment variables properly configured.

Configuration

Create a .env file in the contracts directory with the following variables:

Required Environment Variables

VariableDescriptionExample
GAME_TYPEUnique identifier for the game type (uint32).42
DISPUTE_GAME_FINALITY_DELAY_SECONDSDelay before finalizing dispute games.604800 for 7 days
MAX_CHALLENGE_DURATIONMaximum duration for challenges in seconds.604800 for 7 days
MAX_PROVE_DURATIONMaximum duration for proving in seconds.86400 for 1 day
STARTING_L2_BLOCK_NUMBERStarting L2 block number in decimal.786000
STARTING_ROOTStarting anchor root in hex.0x...

Getting the Starting Root

You can get the starting root for the STARTING_L2_BLOCK_NUMBER from the L2 node RPC using this command:

# Convert block number to hex and remove '0x' prefix
BLOCK_HEX=$(cast --to-hex <STARTING_L2_BLOCK_NUMBER> | sed 's/0x//')

# Construct the JSON RPC request
JSON_DATA='{
    "jsonrpc": "2.0",
    "method": "optimism_outputAtBlock",
    "params": ["0x'$BLOCK_HEX'"],
    "id": 1
}'

# Make the RPC call and extract the output root
starting_root=$(curl -s -X POST \
    -H "Content-Type: application/json" \
    <L2_NODE_RPC> \
    --data "$JSON_DATA" \
    | jq -r '.result.outputRoot')

# Display the result
printf "\nStarting root: %s\n" "$starting_root"

SP1 Verifier Configuration

For testing, set:

USE_SP1_MOCK_VERIFIER=true

For production, remove the USE_SP1_MOCK_VERIFIER environment variable and set all of these:

VariableDescriptionExample
VERIFIER_ADDRESSAddress of the SP1 verifier (see contract addresses)0x...
ROLLUP_CONFIG_HASHHash of the rollup configuration0x...
AGGREGATION_VKEYVerification key for aggregation0x...
RANGE_VKEY_COMMITMENTCommitment to range verification key0x...

Getting the Rollup Config Hash, Aggregation Verification Key, and Range Verification Key Commitment

First, create a .env file in the root directory with the following variables:

L1_RPC=<L1_RPC_URL>
L1_BEACON_RPC=<L1_BEACON_RPC_URL>
L2_RPC=<L2_RPC_URL>
L2_NODE_RPC=<L2_NODE_RPC_URL>

You can get the aggregation program verification key, range program verification key commitment, and rollup config hash by running the following command:

cargo run --bin config --release -- --env-file <PATH_TO_ENV_FILE>

Deployment

  1. Change directory to contracts:

    cd contracts
    
  2. Install dependencies:

    forge install
    
  3. Build the contracts:

    forge build
    
  4. Run the deployment script:

    forge script script/fp/DeployOPSuccinctFDG.s.sol --broadcast --rpc-url <RPC_URL> --private-key <PRIVATE_KEY>
    

Optional Environment Variables

The deployment script deploys the contract with the following parameters:

VariableDescriptionExample
INITIAL_BOND_WEIInitial bond for the game.1_000_000_000_000_000 (for 0.001 ETH)
CHALLENGER_BOND_WEIChallenger bond for the game.1_000_000_000_000_000 (for 0.001 ETH)
OPTIMISM_PORTAL2_ADDRESSAddress of an existing OptimismPortal2 contract. If not provided, a mock will be deployed.0x...
PERMISSIONLESS_MODEIf set to true, anyone can propose or challenge games.true or false
PROPOSER_ADDRESSESComma-separated list of addresses allowed to propose games. Ignored if PERMISSIONLESS_MODE is true.0x123...,0x456...
CHALLENGER_ADDRESSESComma-separated list of addresses allowed to challenge games. Ignored if PERMISSIONLESS_MODE is true.0x123...,0x456...

Use cast --to-wei <value> eth to convert the value to wei to avoid mistakes.

These values depend on the L2 chain, and the total value secured. Generally, to prevent frivolous challenges, CHALLENGER_BOND should be set to at least 10x of the proving cost needed to prove a game.

Post-Deployment

After deployment, the script will output the addresses of:

  • Factory Proxy.
  • Game Implementation.
  • SP1 Verifier.
  • OptimismPortal2.
  • Anchor State Registry.
  • Access Manager.

Save these addresses for future reference and configuration of other components.

Security Considerations

  • The deployer address will be set as the factory owner.
  • Initial parameters are set for testing - adjust for production.
  • The mock SP1 verifier (USE_SP1_MOCK_VERIFIER=true) should ONLY be used for testing.
  • For production deployments:
    • Provide a valid VERIFIER_ADDRESS.
    • Configure proper ROLLUP_CONFIG_HASH, AGGREGATION_VKEY, and RANGE_VKEY_COMMITMENT.
    • Review and adjust finality delay and duration parameters.
    • Consider access control settings.

Troubleshooting

Common issues and solutions:

  1. Compilation Errors:

    • Ensure Foundry is up to date (run foundryup).
    • Run forge clean && forge build.
  2. Deployment Failures:

    • Check RPC connection.
    • Verify sufficient ETH balance.
    • Confirm environment variables are set correctly.

Next Steps

After deployment:

  1. Update the proposer configuration with the factory address.
  2. Configure the challenger with the game parameters.
  3. Test the deployment with a sample game.
  4. Monitor initial games for correct behavior.
  5. For production: Replace mock OptimismPortal2 with the real implementation.

Upgrading the OPSuccinct Fault Dispute Game

This guide explains how to upgrade the OPSuccinct Fault Dispute Game contract.

Overview

The upgrade script performs the following actions:

  1. Deploys a new implementation of the OPSuccinctFaultDisputeGame contract
  2. Sets the new implementation in the DisputeGameFactory for the specified game type

Required Environment Variables

Create a .env file in the contracts directory with the following variables:

VariableDescriptionExample
FACTORY_ADDRESSAddress of the existing DisputeGameFactory0x...
GAME_TYPEUnique identifier for the game type (uint32)42
MAX_CHALLENGE_DURATIONMaximum duration for challenges in seconds604800 for 7 days
MAX_PROVE_DURATIONMaximum duration for proving in seconds86400 for 1 day
VERIFIER_ADDRESSAddress of the SP1 verifier0x...
ROLLUP_CONFIG_HASHHash of the rollup configuration0x...
AGGREGATION_VKEYVerification key for aggregation0x...
RANGE_VKEY_COMMITMENTCommitment to range verification key0x...
ANCHOR_STATE_REGISTRYAddress of the AnchorStateRegistry0x...
ACCESS_MANAGERAddress of the AccessManager0x...

Getting the Rollup Config Hash, Aggregation Verification Key, and Range Verification Key Commitment

First, create a .env file in the root directory with the following variables:

L1_RPC=<L1_RPC_URL>
L1_BEACON_RPC=<L1_BEACON_RPC_URL>
L2_RPC=<L2_RPC_URL>
L2_NODE_RPC=<L2_NODE_RPC_URL>

You can get the aggregation program verification key, range program verification key commitment, and rollup config hash by running the following command:

cargo run --bin config --release -- --env-file <PATH_TO_ENV_FILE>

Optional Environment Variables

VariableDescriptionDefaultExample
CHALLENGER_BOND_WEIChallenger bond for the game0.001 ether1000000000000000

Use cast --to-wei <value> eth to convert the value to wei to avoid mistakes.

Upgrade Command

Dry run the upgrade command in the root directory of the project:

DRY_RUN=true just -f fault-proof/justfile --dotenv-filename contracts/.env upgrade-fault-dispute-game

Run the upgrade command in the root directory of the project:

DRY_RUN=false just -f fault-proof/justfile --dotenv-filename contracts/.env upgrade-fault-dispute-game

Verification

You can verify the upgrade by running the following command:

cast call <FACTORY_ADDRESS> "gameImpls(uint32)" <GAME_TYPE> --rpc-url <L1_RPC_URL>

Troubleshooting

Common issues and solutions:

  1. Compilation Errors:

    • Run cd contracts && forge clean
  2. Deployment Failures:

    • Check RPC connection
    • Verify sufficient ETH balance
    • Confirm environment variables are set correctly

Running OP Succinct Lite

Fault Proof Proposer

The fault proof proposer is a component responsible for creating and managing OP-Succinct fault dispute games on the L1 chain. It continuously monitors the L2 chain and creates new dispute games at regular intervals to ensure the validity of L2 state transitions.

Prerequisites

Before running the proposer, ensure you have:

  1. Rust toolchain installed (latest stable version)
  2. Access to L1 and L2 network nodes
  3. The DisputeGameFactory contract deployed (See Deploy)
  4. Sufficient ETH balance for:
    • Transaction fees
    • Game bonds (configurable in the factory)
  5. Required environment variables properly configured (see Configuration)

Overview

The proposer performs several key functions:

  1. Game Creation: Creates new dispute games for L2 blocks at configurable intervals
  2. Game Resolution: Optionally resolves unchallenged games after their deadline passes
  3. Chain Monitoring: Continuously monitors the L2 chain's safe head and creates proposals accordingly
  4. Fast Finality Mode: Optionally enables fast finality by including proofs with proposals

Configuration

The proposer is configured through various environment variables. Create a .env.proposer file in the fault_proof directory:

Required Environment Variables

VariableDescription
L1_RPCL1 RPC endpoint URL
L2_RPCL2 RPC endpoint URL
FACTORY_ADDRESSAddress of the DisputeGameFactory contract
GAME_TYPEType identifier for the dispute game
PRIVATE_KEYPrivate key for transaction signing
NETWORK_PRIVATE_KEYPrivate key for the succinct prover network (Set to 0x0000000000000000000000000000000000000000000000000000000000000001 if not using fast finality mode)

To get a whitelisted key on the Succinct Prover Network for OP Succinct, fill out this form. The Succinct team will reach out to you with an RPC endpoint you can use.

Optional Environment Variables

VariableDescriptionDefault Value
MOCK_MODEWhether to use mock modefalse
FAST_FINALITY_MODEWhether to use fast finality modefalse
PROPOSAL_INTERVAL_IN_BLOCKSNumber of L2 blocks between proposals1800
FETCH_INTERVALPolling interval in seconds30
ENABLE_GAME_RESOLUTIONWhether to enable automatic game resolutiontrue
MAX_GAMES_TO_CHECK_FOR_RESOLUTIONMaximum number of games to check for resolution100
MAX_GAMES_TO_CHECK_FOR_DEFENSEMaximum number of recent games to check for defense100
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMINGMaximum number of games to check for bond claiming100
L1_BEACON_RPCL1 Beacon RPC endpoint URL(Only used if FAST_FINALITY_MODE is true)
L2_NODE_RPCL2 Node RPC endpoint URL(Only used if FAST_FINALITY_MODE is true)
PROVER_ADDRESSAddress of the account that will be posting output roots to L1. This address is committed to when generating the aggregation proof to prevent front-running attacks. It can be different from the signing address if you want to separate these roles. Default: The address derived from the PRIVATE_KEY environment variable.(Only used if FAST_FINALITY_MODE is true)
SAFE_DB_FALLBACKWhether to fallback to timestamp-based L1 head estimation even though SafeDB is not activated for op-node. When false, proposer will return an error if SafeDB is not available. It is by default false since using the fallback mechanism will result in higher proving cost.false
PROPOSER_METRICS_PORTThe port to expose metrics on. Update prometheus.yml to use this port, if using docker compose.9000
# Required Configuration
L1_RPC=                  # L1 RPC endpoint URL
L2_RPC=                  # L2 RPC endpoint URL
FACTORY_ADDRESS=         # Address of the DisputeGameFactory contract (obtained from deployment)
GAME_TYPE=               # Type identifier for the dispute game (must match factory configuration)
PRIVATE_KEY=             # Private key for transaction signing

# Optional Configuration
MOCK_MODE=false                          # Whether to use mock mode
FAST_FINALITY_MODE=false                 # Whether to use fast finality mode
PROPOSAL_INTERVAL_IN_BLOCKS=1800         # Number of L2 blocks between proposals
FETCH_INTERVAL=30                        # Polling interval in seconds
ENABLE_GAME_RESOLUTION=false             # Whether to enable automatic game resolution
MAX_GAMES_TO_CHECK_FOR_RESOLUTION=100    # Maximum number of games to check for resolution
MAX_GAMES_TO_CHECK_FOR_DEFENSE=100       # Maximum number of recent games to check for defense
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMING=100 # Maximum number of games to check for bond claiming
PROPOSER_METRICS_PORT=9000               # The port to expose metrics on

Configuration Steps

  1. Deploy the DisputeGameFactory contract following the deployment guide
  2. Copy the factory address from the deployment output
  3. Create .env file with the above configuration
  4. Ensure your account has sufficient ETH for bonds and gas

Running

To run the proposer:

cargo run --bin proposer

The proposer will run indefinitely, creating new games and optionally resolving them based on the configuration.

Features

Game Creation

  • Creates new dispute games at configurable block intervals.
  • Computes L2 output roots for game proposals.
  • Ensures proper game sequencing with parent-child relationships.
  • Handles bond requirements for game creation.
  • Supports mock mode for testing without using the Succinct Prover Network. (Set MOCK_MODE=true in .env.proposer)
  • Supports fast finality mode with proofs. (Set FAST_FINALITY_MODE=true in .env.proposer)

Game Defense

  • Monitors games for challenges against valid claims
  • Automatically defends valid claims by providing proofs
  • Checks games within a configurable window (set by MAX_GAMES_TO_CHECK_FOR_DEFENSE)
  • Only defends games that:
    • Have been challenged
    • Are within their proof submission window
    • Have valid output root claims
  • Generates and submits proofs using the Succinct Prover Network
  • Supports mock mode for testing without using the Succinct Prover Network. (Set MOCK_MODE=true in .env.proposer)

Game Resolution

When enabled (ENABLE_GAME_RESOLUTION=true), the proposer:

  • Monitors unchallenged games
  • Resolves games after their challenge period expires
  • Respects parent-child game relationships in resolution
  • Only resolves games whose parent games are already resolved

Bond Claiming

  • Monitors games for bond claiming opportunities
  • Only claims bonds from games that:
    • Are finalized (resolved and airgapped)
    • Has credit left to claim

Chain Monitoring

  • Monitors the L2 chain's finalized (safe) head
  • Creates proposals for new blocks as they become available
  • Maintains proper spacing between proposals based on configuration
  • Tracks the latest valid proposal for proper sequencing

Logging

The proposer uses the tracing crate for logging with a default level of INFO. You can adjust the log level by setting the RUST_LOG environment variable:

RUST_LOG=debug cargo run --bin proposer

Error Handling

The proposer includes robust error handling for:

  • RPC connection issues
  • Transaction failures
  • Contract interaction errors
  • Invalid configurations

Errors are logged with appropriate context to aid in debugging.

Architecture

The proposer is built around the OPSuccinctProposer struct which manages:

  • Configuration state.
  • Wallet management for transactions.
  • Game creation, defense, and resolution logic.
  • Chain monitoring and interval management.

Key components:

  • ProposerConfig: Handles environment-based configuration.
  • handle_game_creation: Main function for proposing new games that:
    • Monitors the L2 chain's safe head.
    • Determines appropriate block numbers for proposals.
    • Creates new games with proper parent-child relationships.
  • handle_game_defense: Main function for defending challenged games that:
    • Finds the oldest defensible game
    • Generates and submits proofs for valid claims
    • Manages proof generation through the Succinct Prover Network
  • handle_game_resolution: Main function for resolving games that:
    • Checks if resolution is enabled.
    • Manages resolution of unchallenged games.
    • Respects parent-child relationships.
  • run: Main loop that:
    • Runs at configurable intervals.
    • Handles game creation, defense, and resolution.
    • Provides error isolation between tasks.

Helper Functions

  • create_game: Creates individual games with proper bonding.
  • try_resolve_unchallenged_game: Attempts to resolve a single game.
  • should_attempt_resolution: Determines if games can be resolved based on parent status.
  • resolve_unchallenged_games: Manages batch resolution of games.

Development

When developing or modifying the proposer:

  1. Ensure all environment variables are properly set.
  2. Test with a local L1/L2 setup first.
  3. Monitor logs for proper operation.
  4. Test game creation and resolution separately.
  5. Verify proper handling of edge cases (network issues, invalid responses, etc.).
  6. Test both normal and fast finality modes.

Fault Proof Challenger

The fault proof challenger is a component responsible for monitoring and challenging invalid OP-Succinct fault dispute games on the L1 chain. It continuously scans for invalid games and challenges them to maintain L2 state validity.

Prerequisites

Before running the challenger, ensure you have:

  1. Rust toolchain installed (latest stable version)
  2. Access to L1 and L2 network nodes
  3. The DisputeGameFactory contract deployed (See Deploy)
  4. Sufficient ETH balance for:
    • Transaction fees
    • Challenger bonds (proof rewards)
  5. Required environment variables properly configured (See Configuration)

Overview

The challenger performs several key functions:

  1. Game Monitoring: Continuously scans for invalid games that need to be challenged
  2. Game Challenging: Challenges invalid games by providing counter-proofs
  3. Game Resolution: Optionally resolves challenged games after their deadline passes
  4. Bond Management: Handles proof rewards and challenge bonds

Configuration

The challenger is configured through environment variables. Create a .env.challenger file in the project root directory:

Required Environment Variables

VariableDescription
L1_RPCL1 RPC endpoint URL
L2_RPCL2 RPC endpoint URL
FACTORY_ADDRESSAddress of the DisputeGameFactory contract
GAME_TYPEType identifier for the dispute game
PRIVATE_KEYPrivate key for transaction signing

Optional Environment Variables

VariableDescriptionDefault Value
FETCH_INTERVALPolling interval in seconds30
ENABLE_GAME_RESOLUTIONWhether to enable automatic game resolutiontrue
MAX_GAMES_TO_CHECK_FOR_CHALLENGEMaximum number of games to scan for challenges100
MAX_GAMES_TO_CHECK_FOR_RESOLUTIONMaximum number of games to check for resolution100
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMINGMaximum number of games to check for bond claiming100
CHALLENGER_METRICS_PORTThe port to expose metrics on. Update prometheus.yml to use this port, if using docker compose.9001
# Required Configuration
L1_RPC=                  # L1 RPC endpoint URL
L2_RPC=                  # L2 RPC endpoint URL
FACTORY_ADDRESS=         # Address of the DisputeGameFactory contract
GAME_TYPE=               # Type identifier for the dispute game
PRIVATE_KEY=             # Private key for transaction signing

# Optional Configuration
FETCH_INTERVAL=30                     # Polling interval in seconds
ENABLE_GAME_RESOLUTION=true           # Whether to enable automatic game resolution
MAX_GAMES_TO_CHECK_FOR_CHALLENGE=100  # Maximum number of games to scan for challenges
MAX_GAMES_TO_CHECK_FOR_RESOLUTION=100 # Maximum number of games to check for resolution
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMING=100 # Maximum number of games to check for bond claiming
CHALLENGER_METRICS_PORT=9001          # The port to expose metrics on

Running

To run the challenger:

cargo run --bin challenger

The challenger will run indefinitely, monitoring for invalid games and challenging them as needed.

Features

Game Monitoring

  • Continuously scans for invalid games
  • Checks game validity against L2 state
  • Prioritizes oldest challengeable games
  • Maintains efficient scanning through configurable limits

Game Challenging

  • Challenges invalid games with counter-proofs
  • Handles proof reward bonds
  • Ensures proper transaction confirmation
  • Provides detailed logging of challenge actions

Game Resolution

When enabled (ENABLE_GAME_RESOLUTION=true), the challenger:

  • Monitors challenged games
  • Resolves games after their resolution period expires
  • Handles resolution of multiple games efficiently
  • Respects game resolution requirements

Architecture

The challenger is built around the OPSuccinctChallenger struct which manages:

  • Configuration state
  • Wallet management for transactions
  • Game challenging and resolution logic
  • Chain monitoring and interval management

Key components:

  • ChallengerConfig: Handles environment-based configuration
  • handle_game_challenging: Main function for challenging invalid games that:
    • Scans for challengeable games
    • Determines game validity
    • Executes challenge transactions
  • handle_game_resolution: Main function for resolving games that:
    • Checks if resolution is enabled
    • Manages resolution of challenged games
    • Handles resolution confirmations
  • run: Main loop that:
    • Runs at configurable intervals
    • Handles both challenging and resolution
    • Provides error isolation between tasks

Error Handling

The challenger includes robust error handling for:

  • RPC connection issues
  • Transaction failures
  • Contract interaction errors
  • Invalid configurations

Errors are logged with appropriate context to aid in debugging.

Development

When developing or modifying the challenger:

  1. Ensure all environment variables are properly set
  2. Test with a local L1/L2 setup first
  3. Monitor logs for proper operation
  4. Test challenging and resolution separately
  5. Verify proper handling of edge cases
  6. Test with various game states and conditions

OP Succinct Fault Proof Docker Setup

This guide explains how to run the OP Succinct fault proof system using Docker Compose.

Prerequisites

  • Docker and Docker Compose installed
  • L1 and L2 RPC endpoints available
  • Private key with sufficient ETH for bonds and transaction fees
  • Deployed DisputeGameFactory contract address (see Deploy Guide)

Docker Setup Overview

The fault proof system consists of two main components:

  • Proposer: Creates and defends claims about L2 state. See fault_proof/Dockerfile.proposer.
  • Challenger: Monitors and challenges invalid claims. See fault_proof/Dockerfile.challenger.

Quick Start

  1. Create environment files under the fault_proof directory for both components:

Proposer Configuration (.env.proposer)

# Required Configuration
L1_RPC=                  # L1 RPC endpoint URL
L2_RPC=                  # L2 RPC endpoint URL
FACTORY_ADDRESS=         # Address of the DisputeGameFactory contract
GAME_TYPE=               # Type identifier for the dispute game
PRIVATE_KEY=             # Private key for transaction signing
NETWORK_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000001

# Optional Configuration
FAST_FINALITY_MODE=false # Whether to use fast finality mode
PROPOSAL_INTERVAL_IN_BLOCKS=1800    # Number of L2 blocks between proposals
FETCH_INTERVAL=30                   # Polling interval in seconds
ENABLE_GAME_RESOLUTION=true         # Whether to enable automatic game resolution
MAX_GAMES_TO_CHECK_FOR_RESOLUTION=100  # Maximum number of games to check for resolution
MAX_GAMES_TO_CHECK_FOR_DEFENSE=100    # Maximum number of recent games to check for defense

Challenger Configuration (.env.challenger)

# Required Configuration
L1_RPC=                  # L1 RPC endpoint URL
L2_RPC=                  # L2 RPC endpoint URL
FACTORY_ADDRESS=         # Address of the DisputeGameFactory contract
GAME_TYPE=               # Type identifier for the dispute game
PRIVATE_KEY=             # Private key for transaction signing

# Optional Configuration
FETCH_INTERVAL=30        # Polling interval in seconds
MAX_GAMES_TO_CHECK_FOR_CHALLENGE=100  # Maximum number of games to check for challenges
ENABLE_GAME_RESOLUTION=true           # Whether to enable automatic game resolution
MAX_GAMES_TO_CHECK_FOR_RESOLUTION=100 # Maximum number of games to check for resolution
  1. Start the services:
# Navigate to the fault_proof directory
cd fault_proof

# Start both proposer and challenger
docker compose up -d

# Or start them individually
docker compose up -d proposer
docker compose up -d challenger

Monitoring

The fault proof system includes a Grafana dashboard for monitoring the proposer and challenger. To access the dashboard, open your browser and navigate to http://localhost:3000. Use the following credentials:

  • Username: admin
  • Password: admin

If the default port 9090 and 3000 for Prometheus and Grafana are already in use, you can change the port by setting the FP_PROMETHEUS_PORT and FP_GRAFANA_PORT environment variables in fault-proof/.env file.

View logs for the services:

# View logs for both services
docker compose logs -f

# View logs for a specific service
docker logs -f op-succinct-fp-proposer
docker logs -f op-succinct-fp-challenger

Stopping the Services

docker compose down

Advanced Configuration

For fast finality mode, add these to your .env.proposer:

FAST_FINALITY_MODE=true
NETWORK_PRIVATE_KEY=     # Private key for the Succinct Prover Network
L1_BEACON_RPC=           # L1 Beacon RPC endpoint URL
L2_NODE_RPC=             # L2 Node RPC endpoint URL

To get a whitelisted key on the Succinct Prover Network for OP Succinct, fill out this form.

Building Images Manually

If you need to build the Docker images manually:

# Navigate to the fault_proof directory
cd fault_proof

# Build the proposer image
docker build -f Dockerfile.proposer -t op-succinct-fp-proposer:latest ..

# Build the challenger image
docker build -f Dockerfile.challenger -t op-succinct-fp-challenger:latest ..

Troubleshooting

If you encounter issues:

  1. Check the logs for error messages
  2. Verify your RPC endpoints are accessible
  3. Ensure your private key has sufficient ETH
  4. Confirm the factory address and contract deployment environment variables were set as expected
  5. Check that the Docker images built successfully with docker images

For more detailed information, refer to the documentation:

Resources

Gas Costs

For the most accurate gas estimates, always test with your specific use case and conditions.

Here are the typical gas costs for key operations in the fault proof system:

Game Creation (Propose)

Creating a new game through the factory costs approximately 330,000 gas. This includes:

  • Factory proxy delegation (~25,000 gas)
  • Game proxy creation (~44,000 gas)
  • Game initialization (~175,000 gas)
  • Event emissions and state updates

Challenge

Challenging a game costs approximately 85,000 gas. This includes:

  • State validation checks
  • Game over checks
  • Updating game status
  • Setting challenge deadline
  • Recording challenger address
  • Event emissions

Proving

Proving a game costs approximately 280,000~300,000 gas. This includes:

  • Game over checks
  • Proof verification
  • State updates
  • Event emissions

Resolution

Resolving a game costs approximately 85,000 gas for a standard resolution. This includes:

  • Parent game validation
  • Status checks and updates
  • Bond distribution calculations
  • Event emissions

Credit Claiming

Claiming credit (bonds/rewards) costs approximately 85,000 gas. This includes:

  • Game finalization checks
  • Credit balance updates
  • ETH transfers
  • Event emissions

Testing Guide

This guide explains how to run and understand the test suite for the OP Succinct fault dispute game system. Tests are located in:

  • End-to-end tests: fault_proof/tests/e2e.rs
  • Integration tests: fault_proof/tests/integration.rs

Prerequisites

Before running the tests, ensure you have:

  1. Rust toolchain installed
  2. Environment variables properly configured
  3. Access to L1 and L2 test networks
  4. Sufficient test ETH for transactions

Configuration

See Proposer Configuration and Challenger Configuration for more information on how to configure the proposer and challenger properly.

Available Tests

Integration Tests

1. Proposer Defense Scenario

test_proposer_defends_successfully(): Tests the scenario where:

  • The proposer creates a valid game
  • A malicious challenger challenges it
  • The proposer successfully defends with a valid proof

End-to-End Tests

1. Proposer Wins Scenario

test_e2e_proposer_wins(): Tests the happy path where:

  • The honest proposer creates valid games
  • No challenges are submitted
  • Games are resolved successfully in favor of the proposer after timeout

2. Challenger Wins Scenario

test_e2e_challenger_wins(): Tests the scenario where:

  • The malicious proposer creates invalid games
  • The challenger successfully challenges them
  • Games resolve in favor of the challenger after timeout

Running the Tests

To run a specific test:

# For e2e tests
cargo test --test e2e <TEST_NAME>

# For integration tests
cargo test --test integration <TEST_NAME>

For example:

# Run the proposer defense test
cargo test --test integration test_proposer_defends_successfully

# Run the proposer wins e2e test
cargo test --test e2e test_e2e_proposer_wins

Best Practices

This document covers best practices for OP Succinct Lite deployment setup.

Mock Mode

When using mock mode, the proposer will not use the Succinct Prover Network to generate proofs. Instead, it will generate mock proofs using the public values from execution. This is useful for testing since it is much faster than generating real proofs from the Succinct Prover Network.

To enable mock mode, set the MOCK_MODE environment variable to true in the .env.proposer file.

SafeDB Configuration

Enabling SafeDB in op-node

SafeDB is a critical component for efficient L1 head determination. When SafeDB is not enabled, the system falls back to timestamp-based L1 head estimation, which can lead to several issues:

  1. Less reliable derivation
  2. Potential cycle count blowup for derivation

To enable SafeDB in your op-node, see Consensus layer configuration options (op-node).

This ensures that L1 head can be efficiently determined without relying on the more expensive fallback mechanism.

Advanced

This section contains advanced topics for OP Succinct.

Verify the OP Succinct binaries

When deploying OP Succinct in production, it's important to ensure that the SP1 programs used when generating proofs are reproducible.

Introduction

Recall there are two programs used in OP Succinct:

  • range: Proves the correctness of an OP Stack derivation + STF for a range of blocks.
  • aggregation: Aggregates multiple range proofs into a single proof. This is the proof that lands on-chain. The aggregation proof ensures that all range proofs in a given block range are linked and use the rangeVkeyCommitment from the L2OutputOracleProxy as the verification key.

Prerequisites

To reproduce the OP Succinct program binaries, you first need to install the cargo prove toolchain.

Ensure that you have the latest version of the toolchain by running:

sp1up

Confirm that you have the toolchain installed by running:

cargo prove --version

Verify the SP1 binaries

To build the SP1 binaries, first ensure that Docker is running.

docker ps

Then build the binaries:

cd programs/range
# Build the range elfs
cargo prove build --elf-name range-elf-bump --docker --tag v4.1.7
cargo prove build --elf-name range-elf-embedded --docker --tag v4.1.7 --features embedded

cd ../aggregation
# Build the aggregation-elf
cargo prove build --elf-name aggregation-elf --docker --tag v4.1.7

Now, you can verify the binaries. The config script outputs the rollup config hash, aggregation verification key, and range verification key commitment based on the ELFs in /elf.

cargo run --bin config --release

Node Setup

Overview

To run OP Succinct or OP Succinct Lite, you will need the following RPCs:

  • L1 Execution Archive Node
  • L1 Consensus (Beacon) Node
  • L2 Execution Node (op-geth)
  • L2 Rollup Node (op-node)

Required Accessible Endpoints

The RPCs must support the following endpoints:

RPCEndpointsDescription
L1_RPCdebug_getRawHeader, debug_getRawReceipts, debug_getRawBlockL1 Execution Archive Node
L2_RPCdebug_getRawHeader, debug_getRawTransaction, debug_getRawBlock, debug_dbGetL2 Execution Node (op-geth)
L2_NODE_RPCoptimism_outputAtBlock, optimism_rollupConfig, optimism_syncStatus, optimism_safeHeadAtL1BlockL2 Rollup Node (op-node)

External RPC Provider

First, we'd recommend asking your RPC provider if they support these endpoints. If they do, you can use them.

Note: While some RPC providers (like Alchemy and Infura) support the required L1 endpoints after enabling them, most RPC providers do not support the debug_dbGet endpoint for external users. You will likely need to run your own L2 nodes for this reason.

Running Your Own L2 Nodes

If you don't have access to these endpoints, you can run your own L2 nodes.

Instructions

  1. Clone simple-optimism-node and follow the instructions in the README to set up your rollup.
  2. Ensure you configure the rollup with NODE_TYPE=archive and OP_GETH__SYNCMODE=snap. With this, you will be able to prove blocks in OP Succcinct using blocks after the snap sync.

Your op-geth endpoint will be available at the RPC port chosen here, which in this case is 8545 (e.g. http://localhost:8545).

Your op-node endpoint (rollup node) will be available at the RPC port chosen here, which in this case is 9545 (e.g. http://localhost:9545).

Check Sync Status

After a few hours, your node should be fully synced and you can use it to begin generating ZKPs.

To check your node's sync status, you can run the following commands:

op-geth:

curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' http://localhost:8545

op-node:

curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"optimism_syncStatus","params":[],"id":1}' http://localhost:9545

Using Kurtosis to test op-succinct

What is Kurtosis?

Kurtosis is a development tool that allows you spin up local testnets for blockchain development and testing. The ethPandaOps team has created a package for spinning up OP Stack devnets using Kurtosis called optimism-package.

Install Kurtosis

First, install Kurtosis by following the instructions here: Kurtosis Installation Guide.

How to configure the OP Stack devnet

Configure the op-network.yaml file to use the Kurtosis engine:

optimism_package:
  chains:
    - participants:
        - el_type: op-geth
          cl_type: op-node
      network_params:
        fjord_time_offset: 0
        granite_time_offset: 0
        holocene_time_offset: 0
      additional_services:
        - blockscout
ethereum_package:
  participants:
    - el_type: geth
    - el_type: reth
  network_params:
    preset: minimal
  additional_services:
    - blockscout

How to run Kurtosis?

Run the testnet using the following command:

kurtosis run --enclave my-testnet github.com/ethpandaops/optimism-package --args-file op-network.yaml --image-download always

How to get the relevant RPC's from Kurtosis?

Once the Kurtosis service is running, you can get the relevant RPC endpoints (L1_RPC, L2_RPC, L1_BEACON_RPC, L2_NODE_RPC) from the logs:

========================================== User Services ==========================================
UUID           Name                                             Ports                                         Status
f4d46dd9d329   cl-1-lighthouse-geth                             http: 4000/tcp -> http://127.0.0.1:32940      RUNNING
                                                                metrics: 5054/tcp -> http://127.0.0.1:32941   
                                                                tcp-discovery: 9000/tcp -> 127.0.0.1:32942    
                                                                udp-discovery: 9000/udp -> 127.0.0.1:32796    
e42d898efb2e   el-1-geth-lighthouse                             engine-rpc: 8551/tcp -> 127.0.0.1:32937       RUNNING
                                                                metrics: 9001/tcp -> http://127.0.0.1:32938   
                                                                rpc: 8545/tcp -> 127.0.0.1:32935              
                                                                tcp-discovery: 30303/tcp -> 127.0.0.1:32939   
                                                                udp-discovery: 30303/udp -> 127.0.0.1:32795   
                                                                ws: 8546/tcp -> 127.0.0.1:32936               
37ed2311790f   op-batcher-op-kurtosis                           http: 8548/tcp -> http://127.0.0.1:32951      RUNNING
d068303cf7af   op-cl-1-op-node-op-geth-op-kurtosis              http: 8547/tcp -> http://127.0.0.1:32949      RUNNING
                                                                tcp-discovery: 9003/tcp -> 127.0.0.1:32950    
                                                                udp-discovery: 9003/udp -> 127.0.0.1:32798    
d2a8cecbf572   op-el-1-op-geth-op-node-op-kurtosis              engine-rpc: 8551/tcp -> 127.0.0.1:32946       RUNNING
                                                                metrics: 9001/tcp -> 127.0.0.1:32947          
                                                                rpc: 8545/tcp -> http://127.0.0.1:32944       
                                                                tcp-discovery: 30303/tcp -> 127.0.0.1:32948   
                                                                udp-discovery: 30303/udp -> 127.0.0.1:32797   
                                                                ws: 8546/tcp -> 127.0.0.1:32945               
7a6d8bc60601   validator-key-generation-cl-validator-keystore   <none>                                        RUNNING
bc47bef086de   vc-1-geth-lighthouse                             metrics: 8080/tcp -> http://127.0.0.1:32943   RUNNING

Relevant endpoints:

EndpointServiceURL
L1_RPCrpc port of el-1-geth-lighthousehttp://127.0.0.1:32935
L2_RPCrpc port of op-el-1-op-geth-op-node-op-kurtosishttp://127.0.0.1:32944
L1_BEACON_RPChttp port of cl-1-lighthouse-gethhttp://127.0.0.1:32940
L2_NODE_RPChttp port of op-cl-1-op-node-op-geth-op-kurtosishttp://127.0.0.1:32949

Spin down the devnet

Remove the devnet with:

kurtosis clean -a

FAQ

Has OP Succinct been audited?

Cantina has audited both OP Succinct and OP Succinct Lite.

How is data availability proven?

The range program proves the correctness of an OP Stack derivation + STF for a range of blocks. The BlobProvider verifies that the raw data (compressed L2 transaction calldata) matches the blob hash, and the ChainProvider verifies that the blob hashes belong to a certain L1 block hash. At this point, we've verified that compressed L2 transaction calldata is available against a specific L1 block.

Because the range program can include an arbitrary number of blocks with blobs, we supply an l1BlockHash to the verifier. Within the range program, we verify that the blocks from which the blobs are extracted chain up to the l1BlockHash. This l1BlockHash is made accessible when verifying a proof via the checkpointBlockHash function.

Is OP Succinct compatible with the Superchain's Standard Configuration?

If your rollup adopts OP Succinct, it will no longer be classified as a Standard Chain. Optimism currently considers ZK proofs to be an "experimental" feature, similar to alt-DA solutions and custom gas tokens. We are in regular discussions with Optimism, and we hope that this policy will evolve in the future.

Troubleshooting

Common Issues

Missing Trie Node Error

Error Message:

Error: server returned an error response: error code -32000: missing trie node <hash> (path) state <hash> is not available, not found

Cause: This error occurs when your L1 archive node has not fully synced all historical state data. It's common when:

  • The archive node was recently synced
  • You're attempting to prove blocks where the complete state data is not yet archived in the node

Solution:

  1. Wait for your L1 archive node to fully sync more historical state data (typically takes about an hour, depending on your batcher interval)
  2. Try proving blocks that are definitely included in your node's archived data:
    • With more recent blocks
    • Or wait longer for older blocks to be fully synced

L2 Block Validation Failure

Error Message:

Failed to validate L2 block #<block_number> with output root <hash>

Cause: This error occurs when the L1 head block selected is too close to the batch posting block, causing the derivation process to fail. The L2 node may have an inconsistent view of the safe head state, requiring additional L1 blocks to properly derive and validate the L2 blocks.

Solution:

  1. Increase the L1 head offset buffer in the derivation process. The code currently adds a buffer of 20 blocks after the batch posting block, but you can increase this to for example 100 blocks:
#![allow(unused)]
fn main() {
let l1_head_number = l1_head_number + 100;
}
  1. If you're still encountering this error, you can try:
    • Waiting for more L1 blocks to be produced and retry
    • Using a different L2 node with a more consistent safe head state
    • For development/testing, you can increase the buffer further (e.g., to 150 blocks)

Technical Details: The error occurs in the derivation pipeline when attempting to validate L2 blocks. The L1 head must be sufficiently ahead of the batch posting block to ensure all required data is available and the safe head state is consistent. The buffer of 20 blocks is added empirically to handle most cases where RPCs may have an incorrect view of the safe head state and have minimum overhead for the derivation process.

Reference: Fetcher Implementation