OP Succinct
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.
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.
- OP Geth: Execution engine for the L2.
- OP Batcher: Collects and batches users transactions efficiently and posts to L1.
- OP Node: Reads batch data from L1, and passes to OP Geth to perform state transitions.
- OP Proposer: Posts state roots from OP Node to L1. Enables withdrawal processing.
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
.
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.
- User transactions are processed by standard OP Stack components.
- Validity proofs are generated for ranges of blocks with the range program.
- Combine range proofs into a single aggregationproof that is cheaply verifiable on-chain.
- OP Succinct proposer submits aggregation proof to the on-chain contract.
- The on-chain contract verifies the proof and updates the L2 state root for withdrawals.
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 ofL2OutputOracle.sol
that implements validity proof verification. Compatible withOptimismPortal.sol
.OPSuccinctDisputeGame.sol
: Validity proof verification contract that is compatible with theIDisputeGame.sol
interface onOptimismPortal2.sol
. Wraps theOPSuccinctL2OutputOracle.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 theIDisputeGame.sol
interface onOptimismPortal2.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
)
- L1 Archive Node (
- Foundry
- Docker
- Rust
- Just
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:
Parameter | Description |
---|---|
L1_RPC | L1 Archive Node. |
L1_BEACON_RPC | L1 Beacon Node. |
L2_RPC | L2 Execution Node (op-geth ). |
L2_NODE_RPC | L2 Rollup Node (op-node ). |
PRIVATE_KEY | Private key for the account that will be deploying the contract. |
ETHERSCAN_API_KEY | Etherscan 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.
Parameter | Description |
---|---|
VERIFIER_ADDRESS | The address of the SP1MockVerifier contract. |
Now, you should have the following in your .env
file:
Parameter | Description |
---|---|
L1_RPC | L1 Archive Node. |
L1_BEACON_RPC | L1 Beacon Node. |
L2_RPC | L2 Execution Node (op-geth ). |
L2_NODE_RPC | L2 Rollup Node (op-node ). |
PRIVATE_KEY | Private key for the account that will be deploying the contract. |
ETHERSCAN_API_KEY | Etherscan API key for verifying the deployed contracts. |
VERIFIER_ADDRESS | The 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:
Parameter | Description |
---|---|
L2OO_ADDRESS | The address of the OPSuccinctL2OutputOracle contract from the previous step. |
OP_SUCCINCT_MOCK | When 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:
Parameter | Description |
---|---|
L1_RPC | L1 Archive Node. |
L1_BEACON_RPC | L1 Beacon Node. |
L2_RPC | L2 Execution Node (op-geth ). |
L2_NODE_RPC | L2 Rollup Node (op-node ). |
PRIVATE_KEY | Private key for the account that will be deploying the contract and relaying proofs on-chain. |
ETHERSCAN_API_KEY | Etherscan API key for verifying the deployed contracts. |
L2OO_ADDRESS | The address of the OPSuccinctL2OutputOracle contract from the previous step. |
OP_SUCCINCT_MOCK | When set to true , the op-succinct service will generate mock proofs. For this quick start guide, set to true . |
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:
Parameter | Description |
---|---|
L1_RPC | L1 Archive Node. |
L1_BEACON_RPC | L1 Consensus (Beacon) Node. |
L2_RPC | L2 Execution Node (op-geth ). |
L2_NODE_RPC | L2 Rollup Node (op-node ). |
PRIVATE_KEY | Private key for the account that will be deploying the contract. |
ETHERSCAN_API_KEY | Etherscan 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.
Parameter | Description |
---|---|
VERIFIER_ADDRESS | Default: 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_INTERVAL | Default: 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_SECS | Default: 3600 (1 hour). The time period (in seconds) after which a proposed output becomes finalized and withdrawals can be processed. |
STARTING_BLOCK_NUMBER | Default: 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. |
PROPOSER | Default: 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. |
CHALLENGER | Default: 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. |
OWNER | Default: 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_PK | Default: The address of the account associated with PRIVATE_KEY . The private key of the account that will be deploying the contract. |
ADMIN_PK | Default: The address of the account associated with PRIVATE_KEY . The private key of the account that will be deploying the contract. |
PROXY_ADMIN | Default: 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_IMPL | Default: 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.
Parameter | Description |
---|---|
EXECUTE_UPGRADE_CALL | Set 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
(Recommended) Using OPSuccinctL2OutputOracle
from a release
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:
-
Make the relevant changes to the
OPSuccinctL2OutputOracle
contract. -
Then, manually bump the
initializerVersion
version in theOPSuccinctL2OutputOracle
contract. You will need to manually bump theinitializerVersion
version in theOPSuccinctL2OutputOracle
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.
Parameter | Description |
---|---|
EXECUTE_UPGRADE_CALL | Set 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:
Variable | Description | Example |
---|---|---|
L2OO_ADDRESS | Address of the OPSuccinctL2OutputOracle contract | 0x123... |
PROPOSER_ADDRESSES | Comma-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 theapprovedProposers
mapping before enabling optimistic mode. Ifaddress(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
Parameter | Description |
---|---|
L1_RPC | L1 Archive Node. |
L1_BEACON_RPC | L1 Consensus (Beacon) Node. |
L2_RPC | L2 Execution Node (op-geth ). |
L2_NODE_RPC | L2 Rollup Node (op-node ). |
NETWORK_PRIVATE_KEY | Key for the Succinct Prover Network. Get access here. |
L2OO_ADDRESS | Address of the OPSuccinctL2OutputOracle contract. |
PRIVATE_KEY | Private key for the account that will be posting output roots to L1. |
Optional Environment Variables
Parameter | Description |
---|---|
NETWORK_RPC_URL | Default: https://rpc.production.succinct.xyz . RPC URL for the Succinct Prover Network. |
DATABASE_URL | Default: postgres://op-succinct@postgres:5432/op-succinct . The address of a Postgres database for storing the intermediate proposer state. |
DGF_ADDRESS | Address 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_STRATEGY | Default: reserved . Set to hosted to use hosted proof strategy. |
AGG_PROOF_STRATEGY | Default: reserved . Set to hosted to use hosted proof strategy. |
AGG_PROOF_MODE | Default: groth16 . Set to plonk to use PLONK proof type. Note: The verifier gateway contract address must be updated to use PLONK proofs. |
SUBMISSION_INTERVAL | Default: 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_INTERVAL | Default: 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_REQUESTS | Default: 1 . The maximum number of concurrent proof requests (in mock and real mode). |
MAX_CONCURRENT_WITNESS_GEN | Default: 1 . The maximum number of concurrent witness generation requests. |
OP_SUCCINCT_MOCK | Default: false . Set to true to run in mock proof mode. The OPSuccinctL2OutputOracle contract must be configured to use an SP1MockVerifier . |
METRICS_PORT | Default: 8080 . The port to run the metrics server on. |
LOOP_INTERVAL | Default: 60 . The interval (in seconds) between each iteration of the OP Succinct service. |
PROVER_ADDRESS | Address 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_URL | URL for the Web3Signer. Note: This takes precedence over the PRIVATE_KEY environment variable. |
SIGNER_ADDRESS | Address 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_FALLBACK | Default: 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:
- Validates that the requester config matches the contract configuration
- Logs proposer metrics like number of requests in each state
- Handles ongoing tasks by checking completed/failed tasks and cleaning them up
- Sets orphaned tasks (in WitnessGeneration/Execution but not in tasks map) to Failed status
- Gets proof statuses for all requests in proving state from the Prover Network
- 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.
- Creates aggregation proofs from completed contiguous range proofs.
- Requests proofs for any unrequested proofs from the prover network/generates mock proofs.
- 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
androllupConfigHash
parameters - New binary → Upgrade Docker images
Upgrade Contract
- Check out the latest release of
op-succinct
from here. - 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:
- Check out the latest release of
op-succinct
from here. - 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:
Parameter | Description |
---|---|
CELESTIA_CONNECTION | URL of the Celestia light node RPC endpoint. For setup instructions, see Celestia's documentation on light node. |
NAMESPACE | Namespace 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
- Proposals: Each proposal represents a claimed state transition from a start L2 block to an end L2 block with a
startL2OutputRoot
and aclaimedL2OutputRoot
where the output root is a commitment to the entirety of L2 state. - Challenges: Participants can challenge proposals they believe are invalid.
- Proofs: ZK proofs that verify the correctness of state transitions contained in proposals, anchored against an L1 block hash.
- 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
orGameStatus.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
Variable | Description |
---|---|
MAX_CHALLENGE_DURATION | Time window during which a proposal can be challenged. |
MAX_PROVE_DURATION | Time allowed for proving a challenge. |
GAME_TYPE | The type of the game, which is set in the DisputeGameFactory contract. |
DISPUTE_GAME_FACTORY | The factory contract that creates this game. |
SP1_VERIFIER | The verifier contract that verifies the SP1 proof. |
ROLLUP_CONFIG_HASH | Hash of the chain's rollup configuration |
AGGREGATION_VKEY | The verification key for the aggregation SP1 program. |
RANGE_VKEY_COMMITMENT | The commitment to the BabyBear representation of the verification key of the range SP1 program. |
CHALLENGER_BOND | Amount of ETH required to submit a challenge. If a prover supplies a valid proof, the bond is disbursed to the prover. |
ANCHOR_STATE_REGISTRY | The anchor state registry contract. |
ACCESS_MANAGER | The access manager contract. |
Types
ProposalStatus
While GameStatus
(IN_PROGRESS, DEFENDER_WINS, CHALLENGER_WINS) represents the final outcome of the game, we need ProposalStatus
to:
- allow proving for fast finality even if the proposal is unchallenged.
- 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
(viachallenge()
)UnchallengedAndValidProofProvided
(viaprove()
)Resolved
(viaresolve()
)
Challenged
→ChallengedAndValidProofProvided
(viaprove()
)Resolved
(viaresolve()
)
UnchallengedAndValidProofProvided
→Resolved
(viaresolve()
)ChallengedAndValidProofProvided
→Resolved
(viaresolve()
)
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 oninitialize()
.- Simplified to single claim instead of array of claims.
- Removed
claimant
andposition
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 stateparentIndex
: Reference to the parent game (uint32.max
for first game)counteredBy
: Initiallyaddress(0)
prover
: Initiallyaddress(0)
claim
: Proposer's claimed output rootstatus
: Set toProposalStatus.Unchallenged
deadline
: Set toblock.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
- Uses SP1 verifier to validate the aggregation proof against public inputs:
-
State Updates
- Records the prover's address in
claimData.prover
- Updates proposal status if the proof is valid:
ProposalStatus.UnchallengedAndValidProofProvided
if there was no challengeProposalStatus.ChallengedAndValidProofProvided
if there was a challenge
- Records the prover's address in
-
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 toCHALLENGER_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
- Unchallenged:
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
: UsesnormalModeCredit
for standard game outcomesREFUND
: UsesrefundModeCredit
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:
- Proposal Bond: Required to submit a proposal.
- Challenger Bond (Proof Reward): Required to challenge a proposal, which is paid to successful provers.
Time Windows
Two key time windows ensure fair participation:
- Challenge Window: Period during which proposals can be challenged.
- 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
-
Clone and setup the repository:
git clone https://github.com/succinctlabs/op-succinct.git cd op-succinct/contracts forge install
-
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.
- 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
-
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
-
Run the proposer:
cargo run --bin proposer
(Optional) Run with fast finality mode
- 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.
- Run the proposer:
cargo run --bin proposer
Step 3: Run the Challenger
-
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>
-
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:
- Deploys the
DisputeGameFactory
implementation and proxy. - Deploys the
AnchorStateRegistry
implementation and proxy. - Deploys a mock
OptimismPortal2
for testing. - Deploys the
AccessManager
and configures it for permissionless games. - Deploys either a mock SP1 verifier for testing or uses a provided verifier address.
- Deploys the
OPSuccinctFaultDisputeGame
implementation. - 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
Variable | Description | Example |
---|---|---|
GAME_TYPE | Unique identifier for the game type (uint32). | 42 |
DISPUTE_GAME_FINALITY_DELAY_SECONDS | Delay before finalizing dispute games. | 604800 for 7 days |
MAX_CHALLENGE_DURATION | Maximum duration for challenges in seconds. | 604800 for 7 days |
MAX_PROVE_DURATION | Maximum duration for proving in seconds. | 86400 for 1 day |
STARTING_L2_BLOCK_NUMBER | Starting L2 block number in decimal. | 786000 |
STARTING_ROOT | Starting 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:
Variable | Description | Example |
---|---|---|
VERIFIER_ADDRESS | Address of the SP1 verifier (see contract addresses) | 0x... |
ROLLUP_CONFIG_HASH | Hash of the rollup configuration | 0x... |
AGGREGATION_VKEY | Verification key for aggregation | 0x... |
RANGE_VKEY_COMMITMENT | Commitment to range verification key | 0x... |
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
-
Change directory to contracts:
cd contracts
-
Install dependencies:
forge install
-
Build the contracts:
forge build
-
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:
Variable | Description | Example |
---|---|---|
INITIAL_BOND_WEI | Initial bond for the game. | 1_000_000_000_000_000 (for 0.001 ETH) |
CHALLENGER_BOND_WEI | Challenger bond for the game. | 1_000_000_000_000_000 (for 0.001 ETH) |
OPTIMISM_PORTAL2_ADDRESS | Address of an existing OptimismPortal2 contract. If not provided, a mock will be deployed. | 0x... |
PERMISSIONLESS_MODE | If set to true, anyone can propose or challenge games. | true or false |
PROPOSER_ADDRESSES | Comma-separated list of addresses allowed to propose games. Ignored if PERMISSIONLESS_MODE is true. | 0x123...,0x456... |
CHALLENGER_ADDRESSES | Comma-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
, andRANGE_VKEY_COMMITMENT
. - Review and adjust finality delay and duration parameters.
- Consider access control settings.
- Provide a valid
Troubleshooting
Common issues and solutions:
-
Compilation Errors:
- Ensure Foundry is up to date (run
foundryup
). - Run
forge clean && forge build
.
- Ensure Foundry is up to date (run
-
Deployment Failures:
- Check RPC connection.
- Verify sufficient ETH balance.
- Confirm environment variables are set correctly.
Next Steps
After deployment:
- Update the proposer configuration with the factory address.
- Configure the challenger with the game parameters.
- Test the deployment with a sample game.
- Monitor initial games for correct behavior.
- 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:
- Deploys a new implementation of the
OPSuccinctFaultDisputeGame
contract - 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:
Variable | Description | Example |
---|---|---|
FACTORY_ADDRESS | Address of the existing DisputeGameFactory | 0x... |
GAME_TYPE | Unique identifier for the game type (uint32) | 42 |
MAX_CHALLENGE_DURATION | Maximum duration for challenges in seconds | 604800 for 7 days |
MAX_PROVE_DURATION | Maximum duration for proving in seconds | 86400 for 1 day |
VERIFIER_ADDRESS | Address of the SP1 verifier | 0x... |
ROLLUP_CONFIG_HASH | Hash of the rollup configuration | 0x... |
AGGREGATION_VKEY | Verification key for aggregation | 0x... |
RANGE_VKEY_COMMITMENT | Commitment to range verification key | 0x... |
ANCHOR_STATE_REGISTRY | Address of the AnchorStateRegistry | 0x... |
ACCESS_MANAGER | Address of the AccessManager | 0x... |
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
Variable | Description | Default | Example |
---|---|---|---|
CHALLENGER_BOND_WEI | Challenger bond for the game | 0.001 ether | 1000000000000000 |
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:
-
Compilation Errors:
- Run
cd contracts && forge clean
- Run
-
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:
- Rust toolchain installed (latest stable version)
- Access to L1 and L2 network nodes
- The DisputeGameFactory contract deployed (See Deploy)
- Sufficient ETH balance for:
- Transaction fees
- Game bonds (configurable in the factory)
- Required environment variables properly configured (see Configuration)
Overview
The proposer performs several key functions:
- Game Creation: Creates new dispute games for L2 blocks at configurable intervals
- Game Resolution: Optionally resolves unchallenged games after their deadline passes
- Chain Monitoring: Continuously monitors the L2 chain's safe head and creates proposals accordingly
- 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
Variable | Description |
---|---|
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 | Private 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
Variable | Description | Default Value |
---|---|---|
MOCK_MODE | Whether to use mock mode | false |
FAST_FINALITY_MODE | Whether to use fast finality mode | false |
PROPOSAL_INTERVAL_IN_BLOCKS | Number of L2 blocks between proposals | 1800 |
FETCH_INTERVAL | Polling interval in seconds | 30 |
ENABLE_GAME_RESOLUTION | Whether to enable automatic game resolution | true |
MAX_GAMES_TO_CHECK_FOR_RESOLUTION | Maximum number of games to check for resolution | 100 |
MAX_GAMES_TO_CHECK_FOR_DEFENSE | Maximum number of recent games to check for defense | 100 |
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMING | Maximum number of games to check for bond claiming | 100 |
L1_BEACON_RPC | L1 Beacon RPC endpoint URL | (Only used if FAST_FINALITY_MODE is true ) |
L2_NODE_RPC | L2 Node RPC endpoint URL | (Only used if FAST_FINALITY_MODE is true ) |
PROVER_ADDRESS | Address 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_FALLBACK | Whether 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_PORT | The 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
- Deploy the DisputeGameFactory contract following the deployment guide
- Copy the factory address from the deployment output
- Create
.env
file with the above configuration - 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:
- Ensure all environment variables are properly set.
- Test with a local L1/L2 setup first.
- Monitor logs for proper operation.
- Test game creation and resolution separately.
- Verify proper handling of edge cases (network issues, invalid responses, etc.).
- 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:
- Rust toolchain installed (latest stable version)
- Access to L1 and L2 network nodes
- The DisputeGameFactory contract deployed (See Deploy)
- Sufficient ETH balance for:
- Transaction fees
- Challenger bonds (proof rewards)
- Required environment variables properly configured (See Configuration)
Overview
The challenger performs several key functions:
- Game Monitoring: Continuously scans for invalid games that need to be challenged
- Game Challenging: Challenges invalid games by providing counter-proofs
- Game Resolution: Optionally resolves challenged games after their deadline passes
- 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
Variable | Description |
---|---|
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 Environment Variables
Variable | Description | Default Value |
---|---|---|
FETCH_INTERVAL | Polling interval in seconds | 30 |
ENABLE_GAME_RESOLUTION | Whether to enable automatic game resolution | true |
MAX_GAMES_TO_CHECK_FOR_CHALLENGE | Maximum number of games to scan for challenges | 100 |
MAX_GAMES_TO_CHECK_FOR_RESOLUTION | Maximum number of games to check for resolution | 100 |
MAX_GAMES_TO_CHECK_FOR_BOND_CLAIMING | Maximum number of games to check for bond claiming | 100 |
CHALLENGER_METRICS_PORT | The 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 configurationhandle_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:
- Ensure all environment variables are properly set
- Test with a local L1/L2 setup first
- Monitor logs for proper operation
- Test challenging and resolution separately
- Verify proper handling of edge cases
- 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
- 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
- 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:
- Check the logs for error messages
- Verify your RPC endpoints are accessible
- Ensure your private key has sufficient ETH
- Confirm the factory address and contract deployment environment variables were set as expected
- 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:
- Rust toolchain installed
- Environment variables properly configured
- Access to L1 and L2 test networks
- 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:
- Less reliable derivation
- 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 allrange
proofs in a given block range are linked and use therangeVkeyCommitment
from theL2OutputOracleProxy
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:
RPC | Endpoints | Description |
---|---|---|
L1_RPC | debug_getRawHeader , debug_getRawReceipts , debug_getRawBlock | L1 Execution Archive Node |
L2_RPC | debug_getRawHeader , debug_getRawTransaction , debug_getRawBlock , debug_dbGet | L2 Execution Node (op-geth ) |
L2_NODE_RPC | optimism_outputAtBlock , optimism_rollupConfig , optimism_syncStatus , optimism_safeHeadAtL1Block | L2 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
- Clone simple-optimism-node and follow the instructions in the README to set up your rollup.
- Ensure you configure the rollup with
NODE_TYPE
=archive
andOP_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:
Endpoint | Service | URL |
---|---|---|
L1_RPC | rpc port of el-1-geth-lighthouse | http://127.0.0.1:32935 |
L2_RPC | rpc port of op-el-1-op-geth-op-node-op-kurtosis | http://127.0.0.1:32944 |
L1_BEACON_RPC | http port of cl-1-lighthouse-geth | http://127.0.0.1:32940 |
L2_NODE_RPC | http port of op-cl-1-op-node-op-geth-op-kurtosis | http://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:
- Wait for your L1 archive node to fully sync more historical state data (typically takes about an hour, depending on your batcher interval)
- 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:
- 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; }
- 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