OP Succinct

Documentation for OP Succinct users and developers.

OP Succinct transforms any OP Stack rollup into a fully type-1 ZK rollup using SP1. OP Succinct provides:

  • 1 hour finality secured by ZKPs, a dramatic improvement over the 7-day withdrawal window of standard OP Stack rollups.
  • Unlimited customization for rollup modifications in pure Rust and easy maintainability.
  • Cost-effective proving with an average cost of proving only fractions of cent per transaction (with an expected 5-10x improvement by EOY), thanks to SP1's blazing fast performance.

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

Reach out today if you want a Type-1 zkEVM rollup powered by SP1 (either a new rollup or a conversion from an optimistic rollup).

Architecture

Prerequisites

Every OP Stack rollup is composed of four main components.

  • op-geth: Takes transactions from users and uses them to generate blocks and execute blocks.
  • op-batcher: Batches transactions from users and submits them to the L1.
  • op-node: Reads batch data from L1 and uses it to drive op-geth in non-sequencer mode to perform state transitions.
  • op-proposer: Posts an output root to L1 at regular intervals, which captures the L2 state so withdrawals can be processed.

You can read more about the components in the OP Stack Specification.

OP Succinct

OP Succinct is a lightweight upgrade to the OP Stack that allows the chain to progress only with ZK-proven blocks, while keeping the other components (op-geth, op-batcher, and op-node) unchanged. Deploying OP Succinct requires deploying one contract, OPSuccinctL2OutputOracle, and spinning up a lightweight modification to the op-proposer that requests proofs to be submitted to the L1 contract.

Here is a high-level overview of the new components that are introduced in OP Succinct:

  • Range Program. A program that derives and executes batches of blocks. The program is written in Rust and designed to be executed inside the zkVM.
  • Aggregation Program. Aggregates proofs of range programs to reduce on-chain verification costs. This program is also written in Rust and designed to be executed inside the zkVM.
  • OP Succinct L2 Output Oracle. A solidity smart contract that contains an array of L2 state outputs, where each output is a commit to the state of the L2 chain. This contract already exists in Optimism's original system but is modified to verify proofs as the authentication mechanism.
  • OP Succinct Proposer. Observes the posted batches on L1 and controls the proving of the range and aggregation programs.

OP Succinct Architecture

Getting Started

In this section, we'll guide you through upgrading an OP Stack chain to a fully type-1 ZK rollup using SP1 and OP Succinct.

The steps are the following:

  1. Run the cost estimator to confirm that all of your endpoints are set up correctly. The cost estimator will simulate the proving of the chain and return the estimated "costs" of proving.
  2. Run OP Succinct in mock mode to confirm that your on-chain configuration is correct.
  3. Run OP Succinct in full mode to begin generating proofs of op-succinct.
  4. Upgrade your OP Stack configuration to upgrade the L2OutputOracle contract to the new OPSuccinctL2OutputOracle contract using your ADMIN key.

Getting Started

Prerequisites

Requirements

You must have the following installed:

You must have the following RPCs available:

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

The following RPC endpoints must be accessible:

  • L1 Archive Node.
    • debug_getRawHeader, debug_getRawReceipts, debug_getRawBlock
  • L2 Execution Node (op-geth): Archive node with hash state scheme.
    • debug_getRawHeader, debug_getRawTransaction, debug_getRawBlock, debug_dbGet
  • L2 Optimism Node (op-node)
    • optimism_outputAtBlock, optimism_rollupConfig, optimism_syncStatus, optimism_safeHeadAtL1Block.

If you do not have access to an L2 OP Geth node + rollup node for your OP Stack chain, you can follow the L2 node setup instructions to spin them up.

OP Stack Chain

The rest of this section will assume you have an existing OP Stack Chain running. If you do not have one, there are two ways you can get started:

  • Self-hosted. If you want to run your own OP Stack Chain, please follow Optimism's tutorial first.
  • Rollup-as-a-service providers. You can also use an existing RaaS provider, such as Conduit, Caldera, Alchemy, AltLayer or Gelato. Contact them to upgrade your rollup to use OP Succinct.

Cost Estimator

The cost estimator is a convenient CLI tool to determine the costs of running OP Succinct. The tool simulates proving the validity of a range of blocks and returns the "costs" in terms of RISC-V cycles.

Machines with fast network connectivity (500+ Mbps) are recommended because witness generation is bandwidth-intensive.

Overview

In the root directory, add the following RPCs to your .env file for your rollup:

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Consensus (Beacon) Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).

More details on the RPC requirements can be found in the prerequisites section.

By running the cost estimator, you can validate that your endpoints are configured correctly.

Running the Cost Estimator

Overview

The cost estimator:

  1. Splits large block ranges into smaller batches
  2. Simulates proving each batch (similar to op-succinct)
  3. Outputs aggregate statistics to a CSV.

⚠️ First run with a small block range - execution can be slow for large ranges.

Basic Usage

Run for the last 5 finalized blocks:

RUST_LOG=info just cost-estimator

Run for a specific block range:

RUST_LOG=info just cost-estimator <start_l2_block> <end_l2_block>

Configuration Options

FlagDefaultDescription
batch-size300Blocks per batch. Chain-specific defaults:
- Base: 5
- OP Mainnet: 10
- OP Sepolia: 30
env-file.envCustom env file path (e.g. .env.opmainnet)
use-cachefalseReuse previously generated witness data

Advanced Usage

Full command with all options:

RUST_LOG=info cargo run --bin cost-estimator --release \
    --start <start_l2_block> \
    --end <end_l2_block> \
    --env-file <path_to_env_file> \
    --batch-size <batch_size> \
    --use-cache

Block Number Helpers

cast is a CLI tool installed with Foundry that can be used to fetch the latest/finalized block number of an OP Stack chain.

# Get latest finalized block
cast block finalized -f number --rpc-url <L2_RPC>

# Get latest block
cast bn --rpc-url <L2_RPC>

Sample Output

stdout

Executing blocks 5,484,100 to 5,484,200 on World Chain Mainnet:

Aggregate Execution Stats for Chain 480: 
 +--------------------------------+---------------------------+
| Metric                         | Value                     |
+--------------------------------+---------------------------+
| Batch Start                    |                 5,484,100 |
| Batch End                      |                 5,484,200 |
| Witness Generation (seconds)   |                        66 |
| Execution Duration (seconds)   |                       458 |
| Total Instruction Count        |            19,707,995,043 |
| Oracle Verify Cycles           |             1,566,560,795 |
| Derivation Cycles              |             2,427,683,234 |
| Block Execution Cycles         |            15,442,479,993 |
| Blob Verification Cycles       |               674,091,948 |
| Total SP1 Gas                  |            22,520,841,820 |
| Number of Blocks               |                       101 |
| Number of Transactions         |                     1,977 |
| Ethereum Gas Used              |               546,370,916 |
| Cycles per Block               |               195,128,663 |
| Cycles per Transaction         |                 9,968,636 |
| Transactions per Block         |                        19 |
| Gas Used per Block             |                 5,409,613 |
| Gas Used per Transaction       |                   276,363 |
| BN Pair Cycles                 |             7,874,860,533 |
| BN Add Cycles                  |               310,550,754 |
| BN Mul Cycles                  |             1,636,223,094 |
| KZG Eval Cycles                |                         0 |
| EC Recover Cycles              |                96,983,901 |
+--------------------------------+---------------------------+

csv

The CSV associated with the range will have the columns from the ExecutionStats struct. The aggregate data for executing each "batch" within the block range will be included in the CSV.

Here, the CSV is execution-reports/480/5484100-5484200.csv:

batch_start,batch_end,witness_generation_time_sec,total_execution_time_sec,total_instruction_count,oracle_verify_instruction_count,derivation_instruction_count,block_execution_instruction_count,blob_verification_instruction_count,total_sp1_gas,nb_blocks,nb_transactions,eth_gas_used,l1_fees,total_tx_fees,cycles_per_block,cycles_per_transaction,transactions_per_block,gas_used_per_block,gas_used_per_transaction,bn_pair_cycles,bn_add_cycles,bn_mul_cycles,kzg_eval_cycles,ec_recover_cycles
5484184,5484200,0,0,2877585481,299740342,462008456,2066943417,134844448,3304337522,17,316,81926057,540908658541982,596950839845253,169269734,9106283,18,4819179,259259,1017572318,40106182,211873811,0,11948870
5484100,5484120,0,0,3754402331,308914395,461086557,2932162167,134779302,4287957207,21,350,106933244,710095197624994,783053876268826,178781063,10726863,16,5092059,305523,1561122615,61455811,324588766,0,14705709
5484163,5484183,0,0,4005997705,311171459,435265918,3206173584,134844448,4570091686,21,365,110883055,690170871678571,779801718014140,190761795,10975336,17,5280145,303789,1676711949,66155955,346844998,0,17337504
5484121,5484141,0,0,4152572226,316652487,486166806,3293854188,134779302,4746305028,21,440,117222955,767310117504021,846733606470274,197741534,9437664,20,5582045,266415,1584230563,62520537,329178548,0,25124445
5484142,5484162,0,0,4917437300,330082112,583155497,3943346637,134844448,5612150377,21,506,129405605,935016666707488,1031433531465147,234163680,9718255,24,6162171,255742,2035223088,80312269,423736971,0,27867373

Mock OP Succinct

Running OP Succinct in mock mode is useful for testing your configuration is correct before generating proofs.

Overview

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.

There are additional optional parameters that you can set in the .env file. See the Advanced Parameters section for more information.

2) Deploy an SP1MockVerifier for verifying mock proofs

just deploy-mock-verifier

If successful, you should see the following output:

% 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

## Setting up 1 EVM.

==========================

Chain 11155111

Estimated gas price: 3.851705636 gwei

Estimated total gas used for script: 171869

Estimated amount required: 0.000661988795953684 ETH

==========================
....

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

Custom Environment

If you have multiple environments, you can pass the environment file to the deploy-mock-verifier command.

just deploy-mock-verifier <env_file>

3) Deploy the OPSuccinctL2OutputOracle contract.

This contract is a modification of the 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.

Then, deploy the OPSuccinctL2OutputOracle contract.

just deploy-oracle

If successful, you should see the following output:

% just deploy-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 9.00s
     Running `target/release/fetch-rollup-config --env-file .env`
[⠊] Compiling...
[⠘] Compiling 2 files with Solc 0.8.15
[⠃] Solc 0.8.15 finished in 1.72s
Compiler run successful!
Script ran successfully.

== Return ==
0: address 0xde4656D4FbeaC0c0863Ab428727e3414Fa251A4C

## Setting up 1 EVM.

==========================

Chain 11155111

Estimated gas price: 1.57226658 gwei

Estimated total gas used for script: 2746913

Estimated amount required: 0.00431887950806754 ETH

==========================

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.

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_MOCKSet to true for mock mode.

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_MOCKSet to true for mock mode.

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 service in the background. It launches two containers: one container that manages proof generation and another container that is a small fork of the original op-proposer service.

After a few minutes, you should see the op-succinct-proposer service start to generate mock range proofs. Once enough range proofs have been generated, a mock aggregate 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

Full OP Succinct

Running OP Succinct in full mode will generate proofs of valid OP Stack L2 outputs and submit them to the L1.

Prerequisites

You will need a whitelisted key on the Succinct Prover Network. Follow the instructions here to get your key whitelisted.

To get access to 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.

Overview

1) Contract deployment 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.

There are additional optional parameters that you can set in the .env file. See the Advanced Parameters section for more information.

2) Deploy the OPSuccinctL2OutputOracle contract.

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

Then, deploy the OPSuccinctL2OutputOracle contract.

just deploy-oracle

If successful, you should see the following output:

% just deploy-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 9.00s
     Running `target/release/fetch-rollup-config --env-file .env`
[⠊] Compiling...
[⠘] Compiling 2 files with Solc 0.8.15
[⠃] Solc 0.8.15 finished in 1.72s
Compiler run successful!
Script ran successfully.

== Return ==
0: address 0xde4656D4FbeaC0c0863Ab428727e3414Fa251A4C

## Setting up 1 EVM.

==========================

Chain 11155111

Estimated gas price: 1.57226658 gwei

Estimated total gas used for script: 2746913

Estimated amount required: 0.00431887950806754 ETH

==========================

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.

3) op-succinct service environment variables

To start the 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.
SP1_PRIVATE_KEYThe private key for the account that will be submitting proofs to the L1.
PROVER_NETWORK_RPCThe RPC endpoint for the Succinct Prover Network. The default endpoint (https://rpc.succinct.xyz) is not suitable for use in OP Succinct. Reach out to the Succinct team to get access with OP Succinct.

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.
SP1_PRIVATE_KEYThe private key for the account that will be submitting proofs to the L1.
PROVER_NETWORK_RPCReach out to the Succinct team to get access here. The default endpoint (https://rpc.succinct.xyz) is not suitable for use in OP Succinct.

4) Start the op-succinct service.

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 service in the background. It launches two containers: one container that manages proof generation and another container that is a small fork of the original op-proposer service.

After a few minutes, you should see the op-succinct-proposer service start to request proofs from the Succinct Prover Network. Once enough proofs have been generated, an aggregate proof will be requested 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

Migrate L2OutputOracle to OPSuccinctL2OutputOracle

The last step is to update your OP Stack configuration to use the new OPSuccinctL2OutputOracle contract managed by the op-succinct service.

⚠️ Caution: When upgrading to the OPSuccinctL2OutputOracle contract, maintain the existing finalizationPeriod for a duration equal to at least one finalizationPeriod. Failure to do so will result in immediate finalization of all pending output roots upon upgrade, which is unsafe. Only after this waiting period has elapsed should you set the finalizationPeriod to your final desired value (e.g. 1 hour).

To upgrade an existing OP Stack deployment to use the OPSuccinctL2OutputOracle contract, follow the instructions in the Upgrading OPSuccinctL2OutputOracle section.

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-elf
cargo prove build --elf-name range-elf --docker --tag "v3.0.0"

cd programs/dummy-range
# Build the dummy-range-elf
cargo prove build --elf-name dummy-range-elf --docker --tag "v3.0.0"

cd ../aggregation
# Build the aggregation-elf
cargo prove build --elf-name aggregation-elf --docker --tag "v3.0.0"

Now, verify the binaries by confirming the output of vkey matches the vkeys on the contract. The vkey program outputs the verification keys based on the ELFs in /elf.

cargo run --bin vkey --release

L2 Node Setup

This guide will show you how to set up an L2 execution node (op-geth) and a rollup node (op-node) for your OP Stack chain.

Instructions

  1. Clone ops-anton and follow the instructions in the README to set up your rollup.
  2. Go to op-node.sh and set the L2_RPC to your rollup RPC. Modify the l1 and l1.beacon to your L1 and L1 Beacon RPCs. Note: Your L1 node should be an archive node.
  3. If you are starting a node for a different chain, you will need to modify op-network in op-geth.sh here and network in op-node.sh here.
  4. In /L2/op-mainnet (or the directory you chose):
    1. Generate a JWT secret ./generate_jwt.sh
    2. docker network create anton-net (Creates a Docker network for the nodes to communicate on).
    3. just up (Starts all the services).

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

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

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:8547

op-node:

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

Block Data

The block-data script is a convenient CLI tool to fetch the block & fee data for a given range of blocks on a rollup.

Performs better with high RPS (requests per second) supported on the L2 RPC endpoint.

Overview

In the root directory, add the following RPC to your .env file for your rollup:

ParameterDescription
L2_RPCL2 Execution Node (op-geth).

To perform analysis on the fees collected on L2, you can use the block-data script. This script will fetch the block & fee data for each block in the range from the L2 and output a CSV file with the columns: block_number, transaction_count, gas_used, total_l1_fees, total_tx_fees.

Compared to the cost estimator, the block data script is much faster and requires less resources, so it's recommended to use this script if you only need the block data and want to calculate data quantities like average txns per block, avg gas per block, etc.

Once the script has finished execution, it will write the statistics for each block in the range to a CSV file at block-data/{chain_id}/{start_block}-{end_block}.csv.

Run the Block Data Script

To run the block data script, use the following command:

RUST_LOG=info cargo run --bin block-data --release -- --start <start_l2_block> --end <end_l2_block>

Optional flags

FlagDescription
--env-fileThe path to the environment file to use. (Ex. .env.opmainnet)

Useful Commands

  • cast block finalized -f number --rpc-url <L2_RPC>: Get the latest finalized block number on the L2.
  • cast bn --rpc-url <L2_RPC>: Get the latest block number on the L2.

Sample Output

stdout

Fetching block data for blocks 5,484,100 to 5,484,200 on World Chain Mainnet:

Wrote block data to block-data/480/5484100-5484200-block-data.csv

Aggregate Block Data for blocks 5484100 to 5484200:
Total Blocks: 101
Total Transactions: 1977
Total Gas Used: 546370916
Total L1 Fees: 0.003644 ETH
Total TX Fees: 0.004038 ETH
Avg Txns/Block: 19.57
Avg Gas/Block: 5409613.03
Avg L1 Fees/Block: 0.000036 ETH
Avg TX Fees/Block: 0.000040 ETH

csv

block_number,transaction_count,gas_used,total_l1_fees,total_tx_fees
5710000,13,5920560,8004099318905,9272809586600
5710001,10,3000975,4810655220218,5353583855906
5710002,16,7269303,10842878782866,12174517937838
5710003,10,4521953,6142429255728,6967734553050
5710004,10,5558505,6534408691877,7550749486247
5710005,14,6670097,10210757953683,11431964042061
5710006,7,4725805,5863003171921,6725878188991
5710007,10,4798495,7011790976814,8252141668373
5710008,7,3805639,4428577556414,5121866899850
5710009,9,4348521,6732491769192,7697076627632
5710010,17,8728999,12317431205317,14022097163617
5710011,9,5882229,6229718004537,7411261930653
5710012,8,2053460,3062348125908,3938363190799
5710013,11,4829936,7756633163332,8721805365111
5710014,5,798837,1684650453190,2292476216082
5710015,10,4123290,7122749550608,7874581655693
5710016,8,1416529,3110958598569,3414612717107
...

Proposer

The op-succinct service consists of two containers:

  • op-succinct-server: Receives proof requests from the op-succinct-proposer, generates the witness for the proof, and submits the proof to the Succinct Prover Network. Handles the communication with the Succinct's Prover Network to fetch the proof status and completed proof data.
  • op-succinct-proposer: Monitors L1 state to determine when to request a proof. Sends proof requests to the op-succinct-server. Once proofs have been generated for a sufficiently large range, aggregates range proofs into an aggregation proof. Submits the aggregation proof to the OPSuccinctL2OutputOracle contract which includes the L2 state outputs.

We've packaged the op-succinct service in a docker compose file to make it easier to run.

Prerequisites

RPC Requirements

Confirm that your RPC's have all of the required endpoints. More details can be found in the prerequisites section.

Hardware Requirements

We recommend the following hardware configuration for the op-succinct service containers:

Using the docker compose file:

  • Full op-succinct service: 16 vCPUs, 64GB RAM.
  • Mock op-succinct service: 32 vCPUs, 128GB RAM. Increased memory because the machine is executing the proofs locally.

Running as separate containers:

  • op-succinct-server
    • Full op-succinct service: 16 vCPUs, 64GB RAM.
    • Mock op-succinct service: 32 vCPUs, 128GB RAM. Increased memory because the machine is executing the proofs locally.
  • op-succinct-proposer: 1 vCPU, 4GB RAM

For advanced configurations, depending on the number of concurrent requests you expect, you may need to increase the number of vCPUs and memory allocated to the op-succinct-server container.

Environment Setup

Required Environment Variables

Before starting the proposer, the following environment variables should be in your .env file. You should have already set up your environment when you deployed the L2 Output Oracle. If you have not done so, follow the steps in the Contract Configuration section.

ParameterDescription
L1_RPCL1 Archive Node.
L1_BEACON_RPCL1 Consensus (Beacon) Node.
L2_RPCL2 Execution Node (op-geth).
L2_NODE_RPCL2 Rollup Node (op-node).
PROVER_NETWORK_RPCDefault: rpc.succinct.xyz.
SP1_PRIVATE_KEYKey for the Succinct Prover Network. Get access here.
SP1_PROVERDefault: network. Set to network to use the Succinct Prover Network.
PRIVATE_KEYPrivate key for the account that will be deploying the contract and posting output roots to L1.
L2OO_ADDRESSAddress of the OPSuccinctL2OutputOracle contract.

Advanced Environment Variables

The following environment variables are optional.

ParameterDescription
MAX_CONCURRENT_PROOF_REQUESTSDefault: 10. The maximum number of concurrent proof requests to send to the op-succinct-server.
MAX_BLOCK_RANGE_PER_SPAN_PROOFDefault: 300. The maximum number of blocks to include in each span proof. For chains with high throughput, you need to decrease this value.
OP_SUCCINCT_MOCKDefault: false. Set to true to run in mock proof mode. The OPSuccinctL2OutputOracle contract must be configured to use an SP1MockVerifier.
OP_SUCCINCT_SERVER_URLDefault: http://op-succinct-server:3000. The URL of the op-succinct-server service which the op-succinct-proposer will send proof requests to.
METRICS_ENABLEDDefault: true. Set to false to disable metrics collection.
METRICS_PORTDefault: 7300. The port to run the metrics server on.
DB_PATHDefault: /usr/local/bin/dbdata. The path to the database directory within the container.
POLL_INTERVALDefault: 20s. The interval at which the op-succinct-proposer service runs.
USE_CACHED_DBDefault: false. Set to true to use cached proofs from previous runs when restarting the service, avoiding regeneration of unused proofs.

Build the Proposer Service

Build the docker images for the op-succinct-proposer service.

docker compose build

Run the Proposer

This command launches the op-succinct-proposer service in the background. It launches two containers: one container that manages proof generation and another container that is a small fork of the original op-proposer service.

After a few minutes, you should see the op-succinct-proposer service start to generate range proofs. Once enough range proofs have been generated, they will be verified in an aggregate proof and submitted to the L1.

docker compose up

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

docker compose logs -f

and to stop the op-succinct-proposer service, run:

docker compose stop

Toggle Optimistic Mode

Optimistic mode is a feature that allows the L2OutputOracle to accept outputs without verification (mirroring the original L2OutputOracle contract). This is useful for testing and development purposes, and as a fallback for when OPSuccinctL2OutputOracle is unable to verify outputs.

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

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.

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

Upgrading to new op-succinct version

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 OPSuccinctL2OutputOracle contract
  • New verification keys → Update aggregationVkey, rangeVkeyCommitment and rollupConfigHash parameters
  • New binary → Upgrade Docker images

Upgrade Docker Containers

If you're using Docker, upgrade your containers to use the latest version of op-succinct by checking out the latest release.

Docker images are not built for releases, but we support a docker compose setup for the latest version of op-succinct.

Upgrade Contract

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

Note: As of release beta-v0.3.0, the aggregationVkey, rangeVkeyCommitment and rollupConfigHash are upgradeable without re-initializing the contract.

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 OPSuccinctL2OutputOracle contract.

Contracts

This section contains information about how to configure and deploy the OPSuccinctL2OutputOracle contract.

Overview

The OPSuccinctL2OutputOracle contract is a modification of the L2OutputOracle contract that will verify SP1 proofs of the Optimism state transition function to get fully validity-proven state roots for the OP Stack rollup.

Configuration

When deploying or upgrading the OPSuccinctL2OutputOracle contract, you will need to set the configuration parameters in your .env file.

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 is the address of the SP1MockVerifier contract.
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.
SUBMISSION_INTERVALDefault: 1000. The minimum interval in L2 blocks at which checkpoints must 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.
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 update the approved proposers. In a production setting, set to the governance smart contract or multi-sig of the chain.

Deploying OPSuccinctL2OutputOracle

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

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 Configuration 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. In the logs below, the proxy address is 0xa8A51b0a66FF2ee852a633cC2D59B6C1b47c7f00.

% just deploy-oracle .env.example

warning: op-succinct-scripts@0.1.0: fault-proof built with release-client-lto profile at 2024-12-07 01:24:00
warning: op-succinct-scripts@0.1.0: range built with release-client-lto profile at 2024-12-07 01:24:00
warning: op-succinct-scripts@0.1.0: native_host_runner built with release profile at 2024-12-07 01:24:01
    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

## Setting up 1 EVM.

==========================

Chain 11155111

Estimated gas price: 2.524425396 gwei

Estimated total gas used for script: 3225968

Estimated amount required: 0.008143715545883328 ETH

==========================

##### sepolia
✅  [Success]Hash: 0x0bdc8571277951abcc91759086d0b9e092354a14b4180b0182883a0ee2185833
Contract Address: 0xa8A51b0a66FF2ee852a633cC2D59B6C1b47c7f00
Block: 7246470
Paid: 0.0005939866343301 ETH (439035 gas * 1.35293686 gwei)


...
                                                                                                                                                                       

==========================

ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
##
Start verification for (2) contracts
Start verifying contract `0x476130149cD3828b8d8A9bb57eBf1B8A54592539` deployed on sepolia

...

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 Configuration 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.

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 Configuration 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

Modifications to Original L2OutputOracle

The original L2OutputOracle contract can be found here.

The changes introduced in the OPSuccinctL2OutputOracle contract are:

  1. The submissionInterval parameter is now the minimum interval in L2 blocks at which checkpoints must be submitted. An aggregation proof can be posted after this interval has passed.
  2. The addition of the aggregationVkey, rangeVkeyCommitment, verifier, startingOutputRoot, and rollupConfigHash parameters. startingOutputRoot is used for initalizing the contract from an empty state, because op-succinct requires a starting output root from which to prove the next state root. The other parameters are used for verifying the proofs posted to the contract.
  3. The addition of historicBlockHashes to store the L1 block hashes which the op-succinct proofs are anchored to. Whenever a proof is posted, the merkle proof verification will use these L1 block hashes to verify the state of the L2 which is posted as blobs or calldata to the L1.
  4. The new checkpointBlockHash function which checkpoints the L1 block hash at a given L1 block number using the blockhash function.
  5. The proposeL2Output function now takes an additional _proof parameter, which is the proof that is posted to the contract, and removes the unnecessary _l1BlockHash parameter (which is redundant given the historicBlockHashes mapping). This function also verifies the proof using the ISP1VerifierGateway contract.
  6. Use an initializerVersion constant to track the "upgrade", rather than just the semantic version of the OPSuccinctL2OutputOracle contract.

FAQ

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.