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 maitainability.
- 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 driveop-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 designed to be an upgrade that replaces the op-proposer
component of the OP Stack and its associated on-chain contracts. This change allows the chain to progress only with ZK-proven blocks, while keeping the other components (op-geth
, op-batcher
, and op-node
) unchanged.
Here is a high-level overview of the new components that are introduced in OP Succinct:
- Span 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 span programs to reduce on-chain verification cost. 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 span and aggregation programs.
Getting Started
In this section, we'll guide you through upgrading an existing OP Stack chain to a fully type-1 ZK rollup using SP1 and OP Succinct.
The steps are the following:
- Deploy the OP Succinct L2 Output Oracle Contract. This contract is a modified version of the existing
L2OutputOracle
contract that uses SP1 to verify the execution and derivation of the L2 state transitions. - Start the OP Succinct Proposer. This service is modified verison of the existing
op-proposer
service. It posts output roots to the L2 Output Oracle contract at regular intervals by orcheastrating the generation and aggregation of proofs. - Update your OP Stack Chain Configuration. You will need to update your configuration to update the
L2OutputOracle
contract to the new implementation using yourADMIN
key.
Prerequisites
Requirements
You must have the following installed:
You must have the following RPCs available:
- L1 Archive Node
- L1 Consensus (Beacon) Node
- L2 Archive Node
- L2 Rollup Node
If you do not have an L2 OP Geth node + rollup node running for your rollup, you can follow the node setup instructions to get started.
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 or Caldera. But you will need access to the admin keys for your rollup.
Deploy L2 Output Oracle
The first step in deploying OP Succinct is to deploy a Solidity smart contract that will verify ZKPs of OP derivation (OP's name for their state transition function) and contain the latest state root of your rollup.
Deployment
1) Clone the op-succinct
repo:
git clone https://github.com/succinctlabs/op-succinct.git
cd op-succinct
2) Set environment variables:
cp .env.example .env
Set the following environment variables:
L1_RPC=...
L2_RPC=...
L2_NODE_RPC=...
PRIVATE_KEY=...
ETHERSCAN_API_KEY=...
3) Navigate to the contracts directory:
cd contracts
4) Set Deployment Parameters
Inside the contracts
folder there is a file called zkl2ooconfig.json
that contains the parameters for the deployment. You will need to fill it with your chain's specific details.
The following parameters are required: proposer
, challenger
, finalizationPeriod
, owner
, verifierGateway
. The rest of the fields (startingBlockNumber
, l2BlockTime
, chainId
and vkey
) are automatically fetched by the fetch-rollup-config
script which is invoked by the ZKDeployer
forge script. To use a manually set startingBlockNumber
, set USE_CACHED_STARTING_BLOCK
to true
.
Parameter | Description |
---|---|
startingBlockNumber | The L2 block number at which to start generating validity proofs. This should be set to the current L2 block number. You can fetch this with cast bn --rpc-url <L2_RPC> . |
submissionInterval | The number of L2 blocks between each L1 output submission. |
l2BlockTime | The time in seconds between each L2 block. |
proposer | The Ethereum address of the proposer account. If address(0) , anyone can submit proofs. |
challenger | The Ethereum address of the challenger account. If address(0) , no one can dispute proofs. |
finalizationPeriod | The time period (in seconds) after which a proposed output becomes finalized. Specifically, the time period after which you can withdraw your funds against the proposed output. |
chainId | The chain ID of the L2 network. |
owner | The Ethereum address of the ZKL2OutputOracle owner, who can update the verification key and verifier address. |
vkey | The verification key for the aggregate program. Run cargo run --bin vkey --release to generate this. |
rollupConfigHash | The hash of the rollup config. This is used for non-superchain OP stack configurations. |
verifierGateway | The address of the verifier gateway contract. The canonical Succinct verifiers can be found here. |
l2OutputOracleProxy | The address of your OP Stack chain's L2 Output Oracle proxy contract which will be upgraded. Only used in ZKUpgrader . |
5) Deploy the ZKL2OutputOracle
contract:
This foundry script will deploy the ZKL2OutputOracle
contract to the specified L1 RPC and use the provided private key to sign the transaction:
forge script script/ZKDeployer.s.sol:ZKDeployer \
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--ffi \
--verify \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast
If successful, you should see the following output:
Script ran successfully.
== Return ==
0: address 0x9b520F7d8031d45Eb8A1D9fE911038576931ab95
## Setting up 1 EVM.
==========================
Chain 11155111
Estimated gas price: 11.826818849 gwei
Estimated total gas used for script: 3012823
Estimated amount required: 0.035632111845100727 ETH
==========================
##### sepolia
✅ [Success]Hash: 0xc57d97ac588563406183969e8ea15bc06496915547114b1df4e024c142df07b4
Contract Address: 0x2e4a7Dc6F19BdE1edF1040f855909afF7CcBeDeC
Block: 6633852
Paid: 0.00858210364707003 ETH (1503205 gas * 5.709203766 gwei)
##### sepolia
✅ [Success]Hash: 0x1343094b0be4e89594aedb57fb795d920e7cc1a76288485e8cf248fa206321ed
Block: 6633852
Paid: 0.001907479233443196 ETH (334106 gas * 5.709203766 gwei)
##### sepolia
✅ [Success]Hash: 0x708ce24c69c2637cadd6cffc654cbe2114e9ea4ec1e69838cd45c1fa27981713
Contract Address: 0x9b520F7d8031d45Eb8A1D9fE911038576931ab95
Block: 6633852
Paid: 0.00250654027540581 ETH (439035 gas * 5.709203766 gwei)
✅ Sequence #1 on sepolia | Total Paid: 0.012996123155919036 ETH (2276346 gas * avg 5.709203766 gwei)
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
##
Start verification for (2) contracts
Start verifying contract `0x9b520F7d8031d45Eb8A1D9fE911038576931ab95` deployed on sepolia
Submitting verification for [lib/optimism/packages/contracts-bedrock/src/universal/Proxy.sol:Proxy] 0x9b520F7d8031d45Eb8A1D9fE911038576931ab95.
...
Keep note of the address of the Proxy
contract that was deployed, which in this case is 0x9b520F7d8031d45Eb8A1D9fE911038576931ab95
.
It is also returned by the script as 0: address 0x9b520F7d8031d45Eb8A1D9fE911038576931ab95
.
Proposer
Now that you have deployed the ZKL2OutputOracle
contract, you can start the op-succinct-proposer
service which replaces the normal op-proposer
service in the OP Stack.
The op-succinct-proposer
service will call to Succinct's Prover Network to generate proofs of the execution and derivation of the L2 state transitions.
The modified proposer performs the following tasks:
- Monitors L1 state to determine when to request a proof.
- Requests proofs from the OP Succinct server. The server sends requests to the Succinct Prover Network.
- Once proofs have been generated for a sufficiently large range, aggregates span proofs and submits them on-chain.
We've packaged the op-succinct-proposer
service in a docker-compose file to make it easier to run.
1) Set Proposer Parameters
In the root directory, create a file called .env
(mirroring .env.example
) and set the following environment variables:
Parameter | Description |
---|---|
L1_RPC | The RPC URL for the L1 Ethereum node. |
L1_BEACON_RPC | The RPC URL for the L1 Ethereum consensus node. |
L2_RPC | The RPC URL for the L2 archive node (OP-Geth). |
L2_NODE_RPC | The RPC URL for the L2 node. |
POLL_INTERVAL | The interval at which to poll for new L2 blocks. |
L2OO_ADDRESS | The address of the L2OutputOracle contract. |
PRIVATE_KEY | The private key for the op-proposer account. |
L2_CHAIN_ID | The chain ID of the L2 network. |
MAX_CONCURRENT_PROOF_REQUESTS | The maximum number of concurrent proof requests (default is 20). |
MAX_BLOCK_RANGE_PER_SPAN_PROOF | The maximum block range per span proof (default is 30). |
OP_SUCCINCT_SERVER_URL | The URL of the OP Succinct server (default is http://op-succinct-server:3000). |
PROVER_NETWORK_RPC | The RPC URL for the Succinct Prover Network. |
SP1_PRIVATE_KEY | The private key for the SP1 account. |
SP1_PROVER | The type of prover to use (set to "network"). |
SKIP_SIMULATION | Whether to skip simulation of the proof before sending to the SP1 server (default is true). |
USE_CACHED_DB | Whether to use a cached database for the proposer (default is false). If set to true, the DB is persisted between runs, and can be re-used after the proposer shuts down. |
2) Build the Proposer
Build the docker images for the op-succinct-proposer
service.
docker-compose build
3) 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 span proofs. Once enough span 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 down
Configuration
The last step is to update your OP Stack configuration to use the new ZKL2OutputOracle
contract managed by the op-succinct-proposer
service.
Self-Managed OP Stack Chains
If you are using a self-managed OP Stack chain, you will need to use your ADMIN
key to update the existing L2OutputOracle
implementation. Recall that the L2OutputOracle
is a proxy contract that is upgradeable using the ADMIN
key.
To update the L2OutputOracle
implementation, you can use an existing script we have in the op-succinct
repo:
forge script script/ZKUpgrader.s.sol:ZKUpgrader \
--rpc-url $L1_RPC \
--private-key $PRIVATE_KEY \
--verify \
--verifier etherscan \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast \
--ffi
RaaS Providers
Cost Estimator
We provide a convenient CLI tool to estimate the RISC-V cycle counts (and cost) for generating ZKPs for a range of blocks for a given rollup.
Overview
First, add the following RPCs to your .env
file for your rollup:
# L1 RPC
L1_RPC=
# L1 Consensus RPC
L1_BEACON_RPC=
# L2 Archive Node (OP-Geth)
L2_RPC=
It is required that the L2 RPC is an archival node for your OP stack rollup, with the "debug_dbGet" endpoint enabled.
Then run the following command:
RUST_LOG=info just cost-estimator <start_l2_block> <end_l2_block>
This command will execute op-succinct
as if it's in production. First, it will divide the entire block range
into smaller ranges optimized along the span batch boundaries. Then it will fetch the required data for generating the ZKP for each of these ranges, and execute the SP1 span
program. Once each program finishes, it will collect the statistics and output the aggregate statistics
for the entire block range. From this data, you can extrapolate the cycle count to a cost based on the cost per billion cycles.
Example
On Optimism Sepolia, proving the block range 15840000 to 15840050 (50 blocks) generates 4 span proofs, takes ~1.8B cycles and ~2 minutes to execute.
RUST_LOG=info just cost-estimator 15840000 15840050
...Execution Logs...
+--------------------------------+---------------------------+
| Metric | Value |
+--------------------------------+---------------------------+
| Batch Start | 16,240,000 |
| Batch End | 16,240,050 |
| Execution Duration (seconds) | 130 |
| Total Instruction Count | 1,776,092,063 |
| Oracle Verify Cycles | 237,150,812 |
| Derivation Cycles | 493,177,851 |
| Block Execution Cycles | 987,885,587 |
| Blob Verification Cycles | 84,995,660 |
| Total SP1 Gas | 2,203,604,618 |
| Number of Blocks | 51 |
| Number of Transactions | 160 |
| Ethereum Gas Used | 43,859,242 |
| Cycles per Block | 74,736,691 |
| Cycles per Transaction | 23,422,603 |
| Transactions per Block | 11 |
| Gas Used per Block | 3,509,360 |
| Gas Used per Transaction | 1,105,066 |
| BN Pair Cycles | 0 |
| BN Add Cycles | 0 |
| BN Mul Cycles | 0 |
| KZG Eval Cycles | 0 |
| EC Recover Cycles | 9,407,847 |
+--------------------------------+---------------------------+
Misc
- For large enough block ranges, the RISC-V SP1 program will surpass the SP1 memory limit. Recommended limit is 20-30 blocks.
- Your L2 node must have been synced for the blocks in the range you are proving.
L2 Node Setup
Setup Instructions
- Clone ops-anton and follow the instructions in the README to set up your rollup.
- Go to op-node.sh and set the
L2_RPC
to your rollup RPC. Modify thel1
andl1.beacon
to your L1 and L1 Beacon RPCs. Note: Your L1 node should be an archive node. - If you are starting a node for a different chain, you will need to modify
op-network
inop-geth.sh
here andnetwork
inop-node.sh
here. - In
/L2/op-mainnet
(or the directory you chose):- Generate a JWT secret
./generate_jwt.sh
docker network create anton-net
(Creates a Docker network for the nodes to communicate on).just up
(Starts all the services).
- Generate a JWT secret
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
).
Checking 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