Kailua
This document is a guide to the design, implementation, and usage of RISC Zero's Kailua.
Introduction
Kailua is suite of tools and contracts for upgrading Optimistic rollups to use ZK Fault Proofs powered by the RISC Zero zkVM. Kailua introduces its own novel fault proof game design which provides the best in class security guarantees for sequencing rollup transactions. These benefits come at marginal added operational costs compared to full validity proving.
Withdrawal Delay
A delay attack happens when a dishonest party attempts to delay the withdrawal finality of correctly sequenced transactions. For optimistic rollups, the main attack vector is through triggering on-chain disputes using the fault proving mechanism.
Kailua's dispute resolution mechanism resolves disputes as fast as proofs can be generated. Thanks to the RISC Zero zkVM's scale-out design, this means that the impact of delay attacks can be mitigated with more proving power. For example, the worst-case single-block dispute requires proving 100bn cycles in the zkVM, a workload that can be computed by RISC Zero's Bonsai service in under an hour.
Denial-of-Service
Assuming an honest majority operates the parent chain of the rollup, on-chain denial-of-service attacks can still happen if a wealthy party raises the on-chain gas costs beyond what honest participants in the fault proof protocol can afford. This block congestion attack can effectively censor disputes against faulty sequencing proposals from being made on-chain, threatening the safety of the rollup.
Kailua's design incorporates "Adaptive Dispute Cutoffs", which delays withdrawal finality to increase the dispute opportunity based on the level of on-chain congestion. This guarantees that if faults cost more to dispute than a predetermined amount, honest parties will be granted more time until gas costs subside.
Sybil Identities
Whale attackers can overwhelm honest parties in a dispute resolution mechanism by using multiple identities to flood the system with disputes. In fault proving schemes where a defender has to issue a timely response on-chain to every dispute, the costs borne by the defender to continuously participate in all open disputes until they are resolved can be overwhelming, leading to some faults slipping through.
Sybil attacks against Kailua force attackers to prove each other's faults at no added cost to the honest defender. The only requirement for safety in Kailua is for an honest party to submit a correct sequencing proposal. The added requirement for liveness is for disputes to be resolved through proofs, which carry no time limit to generate.
Resource Exhaustion
Some fault proof protocols require additional collateral to be staked for every move made in the system, while others require proofs to be generated in a timely manner. These two requirements cause some other systems to be vulnerable to resource exhaustion, where the resource can be the collateral or the proving power required for an honest party to issue a timely response, even if it can afford the transaction fees.
Kailua operates under constant collateral requirements for honest parties, and places no restrictions on proving times, enabling honest parties to successfully defend against attacks of any size at a pre-determined maximum cost.
Quickstart
Kailua enables rollup operators to add a new fault proof system to their rollup via the Optimism DisputeGameFactory
contract.
Kailua's contracts rely on RISC-Zero zkVM proofs to finalize/dismiss output proposals, and are compatible with
Optimism's Bedrock contracts v1.4.0 and above.
Prerequisites
Live Chain
You can test out Kailua's validity proving on a running chain through the following commands:
just build- Compiles a release build of Kailua
just demo [BLOCKS_PER_PROOF] [L1_RPC] [BEACON_RPC] [L2_RPC] [OP_NODE_RPC]:- Runs the release build against the target chain endpoints.
- See here for advanced proving configuration
Local Devnet
You can deploy a local optimism devnet equipped with Kailua through the following commands:
just devnet-fetch- Fetches
v1.16.7of theoptimismmonorepo.
- Fetches
just devnet-build- Builds the local Kailua binaries.
- The OP Stack services themselves use prebuilt artifacts from the Optimism release pipeline.
just devnet-up- Starts a local OP Stack devnet using Kurtosis.
- Writes the devnet descriptor to
devnet/kurtosis-devnet.json. - Dumps the deployment output into
devnet.logfor inspection.
just devnet-upgrade- Upgrades the devnet to use the
KailuaGamecontract. - Auto-discovers RPC endpoints and default keys from
devnet/kurtosis-devnet.json, but can still take explicit overrides.
- Upgrades the devnet to use the
just devnet-propose- Launches the Kailua proposer.
- This runs the sequences, which periodically creates new
KailuaGameinstances.
just devnet-validate- Launches the Kailua validator.
- This monitors
KailuaGameinstances for disputes and creates proofs to resolve them. - (VALIDITY PROVING) Use
just devnet-validate [block-height]to generate validity proofs to fast-forward finality until the specified L2 block height. - (DEVELOPMENT MODE): Use
RISC0_DEV_MODE=1to use fake proofs.
just devnet-rpc- Launches the Kailua RPC.
- This provides utility RPC methods for initiating withdrawals.
- Listens on http://127.0.0.1:1337 and ws://127.0.0.1:1337 by default.
just devnet-fault- Deploys a single
KailuaGameinstance with a faulty sequencing proposal. - Tests the validator's fault proving functionality.
- Tests the proposer's canonical chain tracking functionality.
- Deploys a single
- After you're done:
just devnet-downto remove the running Kurtosis enclave.just devnet-cleanto remove the local descriptor and logs.
Design Overview
Kailua's ZK fault proof game operates seamlessly in two main ways:
- Disputes are non-interactive, removing the need for a "bisection" or multi-round on-chain search for a fault.
- Disputes are implicit between contradictory proposals, removing the need for a special "challenge" transaction.
Sequencing
Sequencing proposals in Kailua can utilize the publication of extra data instead of only the single commitment submitted in other protocols in order to reduce the amount of proving work required to resolve disputes. This extra data consists of commitments to the intermediate sequencing states, which allows the generated ZK proofs to target only a sub-sequence of blocks comprising one transition between the intermediate states instead of the entire proposal.
While Kailua can be configured to operate using a single published commitment per proposal, this may make the proving work required to resolve disputes expensive for chains with very low block times, or a significantly large number of blocks per proposal in general.
--- title: Example Kailua Sequencing Proposal --- graph LR; A -.8.-> B((B)) -.8.-> C((C)) -.8.-> D((D)) -.8.-> E((E)) -.8.-> F((F)) -.8.-> G((G)) -.8.-> H((H)) -.8.-> I;
--- title: Example Standard Sequencing Proposal --- graph LR; A --64--> I;
The above two diagrams illustrate the extended data in Kailua sequencing proposals. While a standard proposal for sequencing 64-blocks would only comprise a single commitment, the Kailua variant here is configured to also require the commitment for every 8th block. In this configuration, any Kailua fault proof would only have to provably derive a sequence of at most 8 blocks.
Disputes
Each new sequencing proposal implicitly disputes the last existing proposal that contradicts it. Once this happens, a proof is required to demonstrate which of the two contradictory proposals, if any, commits to the correct sequencing state at their first point of divergence. The proof then eliminates one, or both, contradictory proposals, and neither proposals can be finalized until the proof is submitted.
While any new contradictory proposal has to be made within the timeout period of the prior proposal it contradicts, proofs are granted an unlimited amount of time for permissionless submission by anyone.
---
title: Disputes Example
---
graph LR;
A --> B;
A --✓--> B';
A --✓--> B'';
B --> C';
B --✓--> C;
B' --> C'';
C --> D;
D --> E;
D --✗--> E';
Consider the above example scenario, where proposal A is finalized, while B, C, D and E are the only correct sequencing
proposals pending finalization, while all others are invalid.
A plain edge from a parent to a child indicates that the child proposal was made while no contradictory siblings should have existed. A checkmark on the edge indicates that the proposal was made within the timeout period of the contradicotry sibling. A crossmark indicates that the timeout period of the contradictory sibling proposal had expired before the child proposal was introduced.
The following three challenges are the only ones implied:
B'challengesBB''challengesB(the proof for the prior challenge will eliminateB').CchallengesC'.
The following two invalid proposals created no challenges:
C''has no siblings and therefore causes no implicit challenges, but will be eliminated once its parentB'is eliminated.E'was made after the timeout period forEhad expired, and was automatically eliminated.
In this scenario, B can only be finalized once two proofs are submitted to resolve its disputes against B' and B''.
Proposal C can only be finalized once a proof resolves its dispute against C', and its parent B is finalized.
D has no contenders and can be finalized once its parent C is finalized.
The timeout period for E had passed before E' was introduced, and therefore E can be finalized once its parent D is finalized.
Fault Proving Permits
Kailua includes an optional permit system that allows provers to acquire exclusive rights to submit fault proofs for disputed proposals. A prover can lock collateral to gain an exclusive window during which only their proof submission earns the fault proof reward.
Permits are entirely optional. Fault proofs can be submitted without acquiring a permit, and the dispute will still be resolved. However, permit holders may receive preferential payouts.
Permit Lifecycle
Each permit goes through three phases determined by two time durations configured at contract deployment: a delay duration and a total permit duration.
---
title: Permit Lifecycle
---
graph LR
A[Acquired] -- delay elapsed --> B[Active]
B -- duration elapsed --> C[Expired]
| Phase | Description | Guaranteed Reward? |
|---|---|---|
| Delayed | Permit has been acquired but is not yet active | No |
| Active | Permit is live and grants exclusive rights | Yes |
| Expired | Permit has lapsed | No |
Collateral
Acquiring a permit requires locking collateral proportional to the elimination reward. The collateral is returned when the permit is released after a proof is submitted. Active permit holders also receive a share of collateral from expired permits.
The number of permits that can be issued for a given dispute is bounded: new permits can only be acquired at a rate that grows exponentially with the number of expired permits. This prevents any single party from monopolizing permits indefinitely while still allowing competitive acquisition.
Reward Distribution
When a fault proof is submitted, the reward recipient for the proposer's collateral is determined as follows:
- Exactly one permit exists and is active at proof time: The permit holder receives the fault proof reward instead of the prover.
- Otherwise: The prover who submitted the fault proof receives the reward as usual.
When a permit holder releases their permit, the collateral payout depends on the state of all permits at proof time:
- Active at proof time: The holder receives their collateral back, plus an equal share of any expired permit collateral.
- Expired before proof time: The holder's collateral is forfeited to the pool split among active holders.
If a permit expires before the fault proof is submitted, the permit holder forfeits their collateral to the pool of active permit holders.
Project
Kailua's project structure is primarily as follows:
kailua // Root project directory
├── bin
│ └── cli // Main Kailua CLI
├── book // This document
├── build
│ └── risczero // RISC Zero zkVM proving binaries
│ ├── hokulea // Eigen DA
│ ├── hana // Celestia DA
│ └── kona // Native ETH DA
├── crates
│ ├── contracts // Fault proof contracts
│ ├── hana // Celestia DA support
│ ├── hokulea // Eigen DA support
│ ├── kona // Core Kona proving primitives
│ ├── proposer // Sequencing proposal submitter
│ ├── prover // Proof generation orcherstrator
│ ├── rpc // RPC server for introspection
│ ├── sync // Sequencing proposal tracker
│ └── validator // Sequencing proposal validator
└── justfile // Convenience commands
CLI
The CLI for Kailua the main entry point for all supported commands:
configInspect the configuration of a running rollupfast-trackFast-track migrate a rollup to use KailuaproposeStart the agent for publishing on-chain sequencing proposalsvalidateStart the agent for resolving on-chain Kailua disputesproveRun the prover to generate an execution/fault/validity prooftest-faultPublish a faulty sequencing proposal to test fault proofsbenchmarkBenchmark proving cost and performancedemoValidity prove any running OP Stack rolluprpcStart the RPC server for assisting withdrawalsbonsaiDownload a receipt from BonsaiboundlessDownload a receipt from BoundlessexportExport the FPVM binaries and their hardcoded image ids
The testing and benchmarking commands (test-fault, benchmark, demo, bonsai, and boundless) are documented in
the Testing Tools chapter.
Contracts
The contracts directory is a foundry project comprised of the following main contracts:
KailuaVerifier.sol: Logic for verifying fault/validity proofs and fault proof locks.KailuaTournament.sol: Logic for resolving disputes between contradictory proposals.KailuaTreasury.sol: Logic for maintaining proposer collateral.KailuaGame.sol: Logic for introducing new sequencing proposals.KailuaLib.sol: Misc. utilities.
The kailua-contracts crate builds and exports these contracts in Rust.
FPVM
The Kailua FPVM executes Optimism's Kona inside the RISC Zero zkVM to derive and execute optimism blocks and create fault proofs.
The following project components work together to enable this functionality:
build/risczero/kona: The zkVM binary to create ZK fault proofs withKona.crates/kona: A wrapper crate aroundKonawith utilities for efficient ZK fault proving.crates/prover: An orchestrator for proof generation locally, remotely on Bonsai, or through Boundless.
Rollups with alternative DA requirements are supported through the following components:
build/risczero/hokulea: The zkVM binary for rollups on EigenDA.build/risczero/hana: The zkVM binary for rollups on Celestia.crates/hokulea: A wrapper crate aroundkailua-konawith Eigen DA support.crates/hana: A wrapper crate aroundkailua-konawith Celestia DA support.
Celestia DA support is still an experimental work in progress with known liveness vulnerabilities.
Setup
Make sure to first install the prerequisites from the quickstart section before proceeding.
Installation
Before you can start migrating your rollup, you'll need to build and install Kailua's binaries by calling the following commands from the root project directory:
At the cost of longer compilation time, you can embed the RISC Zero zkvm prover logic into kailua-cli instead of
having it utilize your locally installed RISC Zero r0vm for proving.
To do this, add -F prove to the install command below.
For GPU-accelerated local proving, use one of the following feature flags:
- Apple:
-F metal - Nvidia:
-F cuda
CLI Binary
cargo install kailua-cli --path bin/cli --locked
Configuration
Once your installation is successful, you should be able to run the following command to fetch the Kailua configuration parameters for your rollup instance:
kailua-cli config --op-node-url [YOUR_OP_NODE_URL] --op-geth-url [YOUR_OP_GETH_URL] --eth-rpc-url [YOUR_ETH_RPC_URL]
Running the above command against the respective Base mainnet endpoints should produce the following output:
RISC0_VERSION: 3.0.6
KAILUA_FPVM_KONA_ID: 0xD47CC9319893A654CB774ED4A66BEAE5056F340C0C3FF6A3271712781A908BBE
KAILUA_FPVM_KONA_ELF: 10.4 MiB
KAILUA_FPVM_HOKULEA_ID: 0xA00F2620A1E64CEEF4E1A15A1E152D04D5E107F7AFDBA11774063D927B871C47
KAILUA_FPVM_HOKULEA_ELF: 11.4 MiB
KAILUA_FPVM_HANA_ID: 0x37C6358F72B5235513C6DC1FC90C18FD3B1D699DD32F84E07787C6D39F12467A
KAILUA_FPVM_HANA_ELF: 11 MiB
CONTROL_ROOT: 0xA54DC85AC99F851C92D7C96D7318AF41DBE7C0194EDFCC37EB4D422A998C1F56
CONTROL_ID: 0x04446E66D300EB7FB45C9726BB53C793DDA407A62E9601618BB43C5C14657AC0
RISC_ZERO_VERIFIER: 0x8EAB2D97DFCE405A1692A21B3FF3A172D593D319
GENESIS_TIMESTAMP: 1686789347
BLOCK_TIME: 2
ROLLUP_CONFIG_HASH: 0x21C9246CB36388245EF7CD08DC27531073F5C522E1BDD83180FBEEFCCB55D22E
DISPUTE_GAME_FACTORY: 0x43EDB88C4B80FDD2ADFF2412A7BEBF9DF42CB40E
OPTIMISM_PORTAL: 0x49048044D57E1C92A77F79988D21FA8FAF74E97E
KAILUA_GAME_TYPE: 1337
Make sure that your FPVM_IMAGE_ID matches the value above.
This value determines the exact program used to prove faults.
If your RISC_ZERO_VERIFIER value is blank, this means that your rollup might be deployed on a base layer that does
not have a deployed RISC Zero zkVM verifier contract.
This means you might have to deploy your own verifier.
Always revise the RISC Zero documentation
to double-check verifier availability.
Once you have these values you'll need to save them for later use during migration.
Experimental Config
If you are using the experimental build, you should see these values instead:
KAILUA_FPVM_KONA_ID: 0x423F11B6F52FD238F4B7784F5DA17572160B506736877431FB328377CBC160A6
KAILUA_FPVM_KONA_ELF: 11.2 MiB
KAILUA_FPVM_HOKULEA_ID: 0xF536311AD097C704FE69CEC8D8462EE8DDC43C1501D12F7FAF99A0BBF0AE4370
KAILUA_FPVM_HOKULEA_ELF: 12.1 MiB
KAILUA_FPVM_HANA_ID: 0x9FAF16B686E91C1B3CADFC65B91502A483EC05DFCAC35742007420AC92FE4EB3
KAILUA_FPVM_HANA_ELF: 11.8 MiB
Telemetry
All Kailua binaries and commands support exporting telemetry data to an
OTLP Collector.
The collector endpoint can be specified using the --otlp-collector parameter, or through specifying the
OTLP_COLLECTOR environment variable.
Parameters
Before migrating to Kailua, you'll need to decide on a few setup parameters and note them down for later use during the migration process.
Starting Block Number
You'll need to pick a block number for Kailua to start sequencing from.
The sequencing state according to your op-node at the block you pick will be immediately finalized.
When you choose to enable withdrawal against Kailua sequencing proposals, your users will be able to start withdrawals
using this finalized state.
You can postpone enabling withdrawals using Kailua at any later point in time after successful migration.
Proposal Output Count / Output Block Span
Each sequencing proposal in Kailua must cover a fixed number of L2 blocks, which will determine how much data must be published per proposal. Consequently, configuring how many output commitments are published per proposal and how many L2 blocks are covered per commitment will determine your proposer's DA costs for using Kailua and your validator's proving costs during dispute.
These commitments are published as Blobs, which means you should optimize your block span S to be S = B * 4096 + 1,
where B is the number of blobs required for a single proposal transaction (Ethereum currently limits a single block to
at most 6 blobs, i.e. B < 7).
Subsequently, combining S with your rollup's block time determines how often your proposer has to publish a proposal
to ensure the liveness of your chain.
Consider Optimism Mainnnet as an example, which has a block time of 2 seconds. To keep its current average sequencing frequency of ~55 minutes, it only needs to publish ~1650 commitments per proposal. To maximize the utilization of the extra blob published when proposing, OP Mainnet can relax its proposal rate to once per 2 hours and 15 minutes.
Collateral Amount
The collateral requirements for a proposer in Kailua come in the form of a fixed amount to be deposited, independent of how many sequencing proposals are in flight. This is because a malicious Kailua proposer, and any faulty sequencing proposals it has published, is eliminated using only a single fault proof.
The prover who submitted that fault proof consequently gets compensated with a portion of the faulty proposer's collateral. This portion should at least cover the proving cost, but should also include a sizeable incentive for the prover. Our estimates put a worst-case proving cost using Bonsai for a single (OP Mainnet) block at $100 USD.
Currently, OP Mainnet requires 0.08 ETH (~$300) of collateral per proposal, and finalizes a proposal after at least 3.5
days if it is undisputed.
This means, at an average hourly rate of proposing, the proposer has 84 * 0.08 = 6.72 ETH (~$3700 USD) on average
locked up as collateral in the best case where no disputes take place.
Using Kailua, 0.08 ETH would be sufficient as the total collateral locked up by the proposer, even under the same finality delay. This would cover the worst-case proving cost in case of dispute, and, discounting transaction costs, leave a $200 tip.
Challenge Timeout
The current implementation of Kailua does not yet have adaptive dispute periods based on congestion. Consequently, you should keep your existing challenge timeout period.
If you wish to practically operate Kailua using only validity proofs, set this value to 31536000000000000 seconds
(i.e. 1 billion calendar years).
The duration of the challenge timeout should not be less than twice the length of your rollup's sequencing window.
Verifier Contract
RISC Zero maintains a set of pre-deployed verifier contracts for its ZK proving system. These contracts are regularly upgraded to support new releases of the prover, and also have a permissionless fail-safe mechanism that anyone who can produce a proof-of-exploit can trigger to halt the verifier.
You must ensure that the chosen verifier contract supports your RISC Zero zkVM version. Once a new zkVM version is released, there can be a delay in adding it to the router.
You have the choice of either using the already deployed verifier for your parent chain, or deploying and maintaining your own verifier contracts, as described in the later sections.
Vanguard Advantage
Kailua supports the designation of one proposer as a Vanguard, along with an advantage time for said proposer. When set, this designation prevents any other proposer from publishing a proposal for a certain block height until either the Vanguard has made a proposal for that height, or the advantage time granted to the Vanguard has passed. The advantage time is counted down until after the Vanguard was allowed to publish a proposal. The Vanguard still needs to lock up the necessary bond to make proposals.
Having a Vanguard that makes honest proposals within the advantage time guarantees the safety of the rollup.
If the advantage time is unreasonably long, Vanguard downtime delays the liveness of the rollup.
This middleground enables rollups transitioning from a permissioned scheme to enjoy permissionless fault proofs in Kailua with an added safety net.
Lock timeout
Fault provers can acquire a lock on a faulty proposal to reserve the right to claim a reward for disproving it. This lock requires an amount of collateral directly proportional to the proposer collateral. Every lock expires after a configured period of time, after which two new locks can be acquired in its place. This process ensures that the collateral required to acquire a lock stays constant, while the collateral acquired to indefinitely hold a lock without submitting a proof grows exponentially. Additionally, a lock does not prevent anyone from holding a proof, but only guarantees that the prover will be rewarded.
Since fault proofs can be configured to cover a very small number of blocks, this lock timeout should be on the order of tens of minutes, if not an hour or two. Use your own judgement.
On-chain Contracts
In order to utilize Kailua, you'll need to deploy the Kailua dispute contracts, and configure your rollup to use them. This process will require access to your rollup's 'Owner' and 'Guardian' wallets.
Overview
The steps required to upgrade your on-chain rollup contracts to support Kailua are as follows:
- Deploy a
KailuaVerifiercontract.- This requires an underlying
RISCZeroVerifierRouter, which you can deploy yourself or use an existing deployment.
- This requires an underlying
- Deploy a
KailuaTreasuryand aKailuaGamecontract with your configuration. - Initialize the
KailuaTreasurycontract to mark the start of sequencing under Kailua. - Update the rollup's
DisputeGameFactorycontract to useKailuaGamefor sequencing proposals.- (Optional) Designate a Vanguard proposer.
- (Optional) Enable withdrawals using finalized Kailua proposals.
The Kailua CLI has a fast-track command for automating the L1 transactions required to migrate to Kailua.
If the command does not yet support your configuration, you'll need to follow the manual steps in the next sub-sections.
This command will use the KailuaVerifier directly without deploying it behind a Proxy, making future updates to the
Kailua configuration difficult.
Estimated Gas Costs
The below table contains rounded-up gas cost estimates of various contract operations
| Contract | Operation | Gas | Notes |
|---|---|---|---|
| RiscZeroVerifierRouter | deploy | 800K | |
| RiscZeroVerifierRouter | addVerifier | 50K | |
| RiscZeroGroth16Verifier | deploy | 1,300K | |
| RiscZeroMockVerifier | deploy | 615K | Fake test proofs only |
| KailuaVerifier | deploy | 1,337K | |
| KailuaTreasury | deploy | 4,560K | |
| KailuaTreasury | propose | 400K | 1 blob |
| KailuaGame | deploy | 4,200K | |
| KailuaGame | proveValidity | 375K | Groth16 proof |
| KailuaGame | proveOutputFault | 415K | Groth16 + 1 KZG proofs |
| KailuaGame | proveOutputFault | 470K | Groth16 + 2 KZG proofs |
| KailuaGame | proveNullFault | 171K | 1 KZG proof |
| KailuaGame | resolve | 120K | Undisputed |
| KailuaGame | resolve | 160K | 1 fault |
| KailuaGame | resolve | 280K | 2 faults |
| KailuaGame | resolve | 370K | 3 faults |
Fast-track Migration
The fast-track migration tool is restricted to certain rollup deployment configurations. As the tool is improved to accommodate more setups, these requirements will be relaxed.
Requirements
- The "Owner" account must be a "Safe" contract instance controlled by a single private-key controlled wallet (EOA).
- The "Guardian" account must be a private-key controlled wallet (EOA).
- You must have access to the raw private key(s) above.
You can skip the guardian key/account requirements if you do not wish to enable withdrawals against sequencing proposals
made by Kailua as part of the fast-track process via the respect-kailua-proposals flag.
You can enable withdrawals manually later using the OptimismPortal2 contract.
Usage
If all the above conditions are met, you can fast track the migration of your rollup to Kailua as follows:
kailua-cli fast-track \
--eth-rpc-url [YOUR_ETH_RPC_URL] \
--op-geth-url [YOUR_OP_GETH_URL] \
--op-node-url [YOUR_OP_NODE_URL] \
\
--starting-block-number [YOUR_STARTING_BLOCK_NUMBER] \
--proposal-output-count [YOUR_OUTPUTS_PER_PROPOSAL] \
--output-block-span [YOUR_BLOCKS_PER_OUTPUT] \
\
--collateral-amount [YOUR_COLLATERAL_AMOUNT] \
--verifier-contract [RISC_ZERO_VERIFIER_ADDRESS] \
--challenge-timeout [YOUR_CHALLENGE_PERIOD] \
--proof-permit-timeout [YOUR_FAULT_PROVING_LOCK_TIMEOUT] \
--proof-permit-delay [YOUR_FAULT_PROVING_LOCK_DELAY]
\
--deployer-key [YOUR_DEPLOYER_KEY] \
--owner-key [YOUR_OWNER_KEY] \
--guardian-key [YOUR_GUARDIAN_KEY] \
\
--vanguard-address [YOUR_VANGUARD_ADDRESS] \
--vanguard-advantage [YOUR_VANGUARD_ADVANTAGE] \
\
--respect-kailua-proposals
Endpoints
The first three parameters to this command are the L1 and L2 RPC endpoints:
eth-rpc-url: The endpoint for the parent chain.op-geth-url: The endpoint for the rollup execution client.op-node-url: The endpoint for the rollup consensus client.
Sequencing
The next three parameters configure sequencing:
starting-block-number: The rollup block number to immediately finalize and start sequencing from.proposal-output-count: The number of intermediate output commitments published per proposal.output-block-span: The number of rollup blocks each intermediate output commitment must cover.
The sequencing state at the block starting-block-number as reported by the op-node will be finalized without delay.
Fault Proving
The following parameters configure fault proving:
collateral-amount: The amount of collateral (in wei) a proposer has to stake before publishing proposals.verifier-contract: (Optional) The address of the existing RISC Zero verifier contract to use. If this argument is omitted, a new set of verifier contracts will be deployed.- If you wish to use an already existing verifier, you must provide this argument, even if the
configcommand had located a verifier. - If you are deploying a new verifier contract and wish to support fake proofs generated in dev mode (insecure), make sure to set
RISC0_DEV_MODE=1in your environment before invoking thefast-trackcommand.
- If you wish to use an already existing verifier, you must provide this argument, even if the
challenge-timeout: The timeout (in seconds) for a sequencing proposal to be contradicted.proof-permit-timeout: The timeout (in seconds) after which a fault proving lock expires.proof-permit-delay: The delay (in seconds) after acquisition before a fault proving lock becomes active.
Ethereum Transactions
The next three parameters are the private keys for the respective parent chain wallets:
deployer-key: Private key for the EOA used to deploy the new Kailua contracts.owner-key: Private key for the sole EOA controlling the Owner "Safe" contract.guardian-key: Private key for the EOA used as the "Guardian" of the optimism portal.
KMS Support
Instead of raw private keys, you can use either AWS or GCP for obtaining transaction signatures from a KMS.
- AWS: Specify a corresponding
[EOA]-aws-key-idparameter. The remainder of the AWS configuration is handled by the AWS SDK.- Example:
deployer-aws-key-id.
- Example:
- GCP: Specify the corresponding
[EOA]-google-project-id,[EOA]-google-location,[EOA]-google-keyringand[EOA]-google-key-nameparameters.- Example:
owner-google-project-id,owner-google-location,owner-google-keyringandowner-google-key-name.
- Example:
Vanguard Proposer
The next two (optional) parameters define the Vanguard proposer advantage:
vanguard-address: The address of the designated Vanguard.vanguard-advantage: The amount of time (in seconds) to grant proposal exclusivity to the Vanguard.- If unspecified, this defaults to
1152921504606846975(a practically indefinite advantage of 36 Billion years).
- If unspecified, this defaults to
Withdrawals
Changing the respected game type to Kailua may crash the op-proposer provided by optimism depending on its version.
This should be inconsequential because you'll need to run the Kailua proposer for further sequencing to take place anyway.
The final argument configures withdrawals in your rollup:
respect-kailua-proposals: (if present) will allow withdrawals using sequencing proposals finalized by Kailua.
If you've successfully completed fast-track migration using the tool, you may now skip to the Off-chain page.
On-chain Proof Verification
If you've successfully performed fast-track migration, you do not need to follow the steps on this page.
The cryptographic fault proofs generated by Kailua require the existence of a RISC Zero verifier contract on the parent chain (ethereum).
Use the Kailua CLI config command to retrieve the address of the officially deployed RISC Zero verifier for your
parent chain.
If one is available, you can use that address as your verifier and skip the steps in this section.
This section explains how to deploy a verifier suited to your needs, but you'll need to maintain it in case of adopting any updates to Kailua that change the RISC Zero zkVM version used for proving.
Verifier Contracts
This section describes the contracts that make up the on-chain proof verification pipeline.
Verifier Router
The RISCZeroVerifierRouter contract routes proofs from different sources to their correct verifier. This allows your application (and Kailua) to leverage proofs generated using different zkVM versions, or through the Boundless proving network, while delegating the complexity of managing the verifier version to the router contract.
Depending on your needs, you may need to deploy and manage your own verifier instead of relying on the pre-deployed router managed by RISC Zero.
The verifier deployment made by Kailua does not yet utilize the Emergency stop contract in the pre-deployed RISC Zero verifier, which allows anyone to permissionlessy disable a verification backend in the router by proving a false statement.
Groth16 Verifier
The RISCZeroGroth16Verifier only accepts valid Groth16 proofs generated using its hardcoded RISC Zero zkVM version determined by the control root and id constructor parameters. This verifier is intended to be deployed and then added as a possible backend to a router instead of being used directly on its own. However, it is possible to call and use this verifier directly if the prover and verifier versions align.
Fake Proof Verifier (INSECURE)
This verifier accepts fake proofs generated while running the RISC Zero zkVM in "dev mode". It is only useful for testing out the zkVM in a development environment without being delayed by proving time.
Deployment
This section will walk you through creating your own verifier deployment.
The commands below will be using Foundry's forge and cast utilities, which you should have installed as part of the
foundry prerequisite.
The below foundry commands expect both a parameter that determines the wallet to use and the rpc endpoint of the parent
chain.
You will have to add these two parameters manually to every command below.
For more information, refer to forge create --help, cast call --help, and cast send --help
First, change your working directory to crates/contracts/foundry for forge to work:
cd crates/contracts/foundry
Router
constructor(address admin) Ownable(admin)
The RISCZeroVerifierRouter constructor requires a single admin address, which is an account authorised to modify
the router in one of two ways after it is deployed:
- Add a new verification backend through
addVerifier: - Permanently disable a verification backend through
removeVerifier:
To deploy a new router, invoke the following command
forge create RiscZeroVerifierRouter --constructor-args [ADMIN_ADDRESS]
On success, you should see output similar to the following:
Deployer: [YOUR_DEPLOYER_WALLET_ADDRESS]
Deployed to: [YOUR_DEPLOYED_ROUTER_CONTRACT]
Transaction hash: [YOUR_DEPLOYMENT_TRANSACTION_HASH]
Make sure to note down the YOUR_DEPLOYED_ROUTER_CONTRACT address.
We will use this in the following commands when adding proving backends.
The router cannot verify any proofs on its own.
Groth16 Verifier
The Groth16 verifier validates stand-alone cryptographic proofs generated using the RISC Zero zkVM and compressed using the RISC Zero STARK-to-SNARK wrapper.
This verifier can be deployed as follows using the control root and id from the kailua-cli config command output:
forge create RiscZeroGroth16Verifier --constructor-args \
[YOUR_CONTROL_ROOT] \
[YOUR_CONTROL_ID]
The output should again give you the relevant addresses:
Deployer: [YOUR_DEPLOYER_WALLET_ADDRESS]
Deployed to: [YOUR_DEPLOYED_GROTH16_VERIFIER_CONTRACT]
Transaction hash: [YOUR_DEPLOYMENT_TRANSACTION_HASH]
We again need to query this verifier's selector:
cast call [YOUR_DEPLOYED_GROTH16_VERIFIER_CONTRACT] \
"SELECTOR() returns (bytes4)"
Yielding another 4-byte selector:
0xc101b42b
And finally we need to add this verifier to our router:
cast send \
[YOUR_DEPLOYED_ROUTER_CONTRACT] \
"addVerifier(bytes4 selector, address verifier)" \
[YOUR_GROTH16_SELECTOR] \
[YOUR_DEPLOYED_GROTH16_VERIFIER_CONTRACT]
On-chain Dispute Resolution
If you've successfully performed fast-track migration, you do not need to follow the steps on this page.
Kailua's on-chain dispute mechanism is powered by its own custom contracts that define a novel ZK dispute game. Each rollup has to deploy its own set of dispute resolution contracts, and this section will guide you through that process.
The commands below will be using Foundry's forge and cast utilities, which you should have installed as part of the
foundry prerequisite.
The below foundry commands expect both a parameter that determines the wallet to use and the rpc endpoint of the parent
chain.
You will have to add these two parameters manually to every command below.
For more information, refer to forge create --help, cast call --help, and cast send --help
First, change your working directory to crates/contracts/foundry for forge to work:
cd crates/contracts/foundry
The parameters used to deploy the contracts below are immutable. Any changes will require redeployment and reconfiguration.
KailuaVerifier
This contract can safely be deployed behind a proxy contract in order to allow for easy updates to the verification configuration.
Deployment
Deployment of this contract is via the command below:
forge create KailuaVerifier --constructor-args \
[YOUR_RISC_ZERO_VERIFIER] \
[YOUR_FPVM_IMAGE_ID] \
[YOUR_ROLLUP_CONFIG_HASH] \
[YOUR_LOCK_EXPIRY_TIME] \
[YOUR_LOCK_ACTIVATION_DELAY_TIME]
Deploying the contract successfully should yield similar output to the following:
Deployer: [YOUR_DEPLOYER_WALLET_ADDRESS]
Deployed to: [YOUR_DEPLOYED_KAILUA_VERIFIER_CONTRACT]
Transaction hash: [YOUR_DEPLOYMENT_TRANSACTION_HASH]
Take note of the contract address since we'll need it later.
KailuaTreasury
constructor(
KailuaVerifier _kailuaVerifier,
uint64 _proposalOutputCount,
uint64 _outputBlockSpan,
GameType _gameType,
OptimismPortal2 _optimismPortal,
Claim _rootClaim,
uint64 _l2BlockNumber
)
This contract stores the collateral bonds required for proposers to publish their proposal, and also stores the first sequencing proposal for Kailua as a fault dispute game in your rollup.
Each published proposal on the L1 will cover proposalOutputCount × outputBlockSpan L2 blocks, and require
publication of proposalOutputCount 32-byte commitments on the DA layer.
Anchor Point
First, you will need to choose the rollup block number from which Kailua sequencing should start.
Then, you need to query your op-node for the outputRoot at that block number as follows:
cast rpc --rpc-url [YOUR_OP_NODE_ADDRESS] \
"optimism_outputAtBlock" \
$(cast 2h [YOUR_STARTING_L2_BLOCK_NUMBER])
Deployment
Deployment of this contract is via the command below:
forge create KailuaTreasury --constructor-args \
[YOUR_DEPLOYED_KAILUA_VERIFIER_CONTRACT] \
[YOUR_PROPOSAL_OUTPUT_COUNT] \
[YOUR_OUTPUT_BLOCK_SPAN] \
[YOUR_KAILUA_GAME_TYPE] \
[YOUR_OPTIMISM_PORTAL] \
[YOUR_OUTPUT_ROOT_CLAIM] \
[YOUR_L2_BLOCK_NUMBER]
Deploying the contract successfully should yield similar output to the following:
Deployer: [YOUR_DEPLOYER_WALLET_ADDRESS]
Deployed to: [YOUR_DEPLOYED_TREASURY_CONTRACT]
Transaction hash: [YOUR_DEPLOYMENT_TRANSACTION_HASH]
Take note of the contract address since we'll need it later.
If your rollup owner account is controlled by a Safe contract, or some other multi-sig contract, you can use
cast calldata to get the necessary input that your wallet contract should forward.
KailuaGame
constructor(
IKailuaTreasury _kailuaTreasury,
uint256 _genesisTimeStamp,
uint256 _l2BlockTime,
Duration _maxClockDuration
)
This contract is used by the optimism DisputeGameFactory to instantiate every Kailua sequencing proposal after the
initial one in the KailuaTreasury.
Deployment is fairly similar to the treasury via the command below:
forge create KailuaGame --evm-version cancun --constructor-args \
[YOUR_DEPLOYED_TREASURY_CONTRACT] \
[YOUR_GENESIS_TIMESTAMP] \
[YOUR_BLOCK_TIME] \
[YOUR_MAX_CLOCK_DURATION]
Deploying the contract successfully should yield similar output to the following:
Deployer: [YOUR_DEPLOYER_WALLET_ADDRESS]
Deployed to: [YOUR_DEPLOYED_GAME_CONTRACT]
Transaction hash: [YOUR_DEPLOYMENT_TRANSACTION_HASH]
Note down this contract's address, we'll use it later.
On-chain State Anchoring
If you've successfully performed fast-track migration, you do not need to follow the steps on this page.
In this section you will be integrating the KailuaTreasury contract with your rollup's DisputeGameFactory.
This will finalize the initial sequencing proposal from which Kailua will start.
The commands below will be using Foundry's cast utility, which you should have installed as part of the
foundry prerequisite.
The below foundry commands expect both a parameter that determines the wallet to use and the rpc endpoint of the parent
chain.
You will have to add these two parameters manually to every command below.
For more information, refer to cast call --help, and cast send --help
If your rollup owner account is controlled by a Safe contract, or some other multi-sig contract, you can use
cast calldata to get the necessary input that your wallet contract should forward.
Clear DGF Kailua Bond
Optimism's DisputeGameFactory is design to require a bond value for each sequencing proposal.
The KailuaTreasury instead requires a constant bond value for a proposer to make any number of proposals.
To ensure that the Kailua proposer operates as expected, we will need to set this value to zero for Kailua proposals
if it is non-zero.
You can check the value as follows:
cast call [YOUR_DISPUTE_GAME_FACTORY] \
"initBonds(uint32) returns (uint256)" \
[YOUR_KAILUA_GAME_TYPE]
If the returned value is non-zero, you must reset it through setInitBond using your rollup owner wallet:
cast send [YOUR_DISPUTE_GAME_FACTORY] \
"setInitBond(uint32, uint256)" \
[YOUR_KAILUA_GAME_TYPE] \
0
Set KailuaTreasury Implementation
The next step is to update the implementation for the Kailua game type stored in the DisputeGameFactory contract to
point towards the KailuaTreasury contract deployed in the last section.
This can be done as follows using your owner wallet:
cast send [YOUR_DISPUTE_GAME_FACTORY] \
"setImplementation(uint32, address)" \
[YOUR_KAILUA_GAME_TYPE] \
[YOUR_DEPLOYED_TREASURY_CONTRACT]
Anchor Instantiation
Once the implementation is set, the next step is to create a dispute game instance using the treasury. This step is only to be done once in order to create a starting point for sequencing using Kailua.
This step will publish and immediately resolve (finalize) a single sequencing proposal with no chance for dispute.
Recall the rollup block number and its output root from the deployment in the previous section.
cast rpc --rpc-url [YOUR_OP_NODE_ADDRESS] \
"optimism_outputAtBlock" \
$(cast 2h [YOUR_STARTING_L2_BLOCK_NUMBER])
Once you have the outputRoot value you wish to start sequencing from, the next step is to call propose on KailuaTreasury using the owner wallet:
cast send [YOUR_DEPLOYED_TREASURY_CONTRACT] \
"propose(bytes32, bytes)" \
[YOUR_OUTPUT_ROOT] \
$(cast abi-encode --packed "f(uint64,address)" [YOUR_STARTING_L2_BLOCK_NUMBER] [YOUR_DEPLOYED_TREASURY_CONTRACT])
To get the address of this new game instance, use the games function on the DisputeGameFactory:
cast call [YOUR_DISPUTE_GAME_FACTORY] \
"games(uint32, bytes32, bytes) returns (address, uint64)" \
[YOUR_KAILUA_GAME_TYPE] \
[YOUR_OUTPUT_ROOT] \
$(cast abi-encode --packed "f(uint64,address)" [YOUR_STARTING_L2_BLOCK_NUMBER] [YOUR_DEPLOYED_TREASURY_CONTRACT])
With this instance address, the last step is to call resolve() on it using the owner wallet:
cast send [YOUR_GAME_INSTANCE_ADDRESS] \
"resolve()"
On-chain Sequencing Proposal
If you've successfully performed fast-track migration, you do not need to follow the steps on this page.
In this section you will be integrating the KailuaGame contract with your rollup's DisputeGameFactory.
This will allow Kailua proposers to submit new proposals!
The commands below will be using Foundry's cast utility, which you should have installed as part of the
foundry prerequisite.
The below foundry commands expect both a parameter that determines the wallet to use and the rpc endpoint of the parent
chain.
You will have to add these two parameters manually to every command below.
For more information, refer to cast call --help, and cast send --help
If your rollup owner account is controlled by a Safe contract, or some other multi-sig contract, you can use
cast calldata to get the necessary input that your wallet contract should forward.
Set Collateral Requirement
Before allowing sequencing proposals past the anchor state, you'll need to set the bond value (in wei) required for proposers.
This is done by calling the setParticipationBond function on the treasury contract using the owner wallet for your
rollup.
For example, if your bond value is 12 eth, first convert this to wei using cast:
cast to-wei 12
12000000000000000000
Then, configure the bond as follows using the rollup owner wallet:
cast send \
[YOUR_DEPLOYED_TREASURY_CONTRACT] \
"setParticipationBond(uint256 amount)" \
12000000000000000000
Set KailuaGame Implementation
The next step is to update the implementation for the Kailua game type stored in the DisputeGameFactory contract to
point towards the KailuaGame contract deployed previously.
This can be done as follows using your owner wallet:
cast send [YOUR_DISPUTE_GAME_FACTORY] \
"setImplementation(uint32, address)" \
[YOUR_KAILUA_GAME_TYPE] \
[YOUR_DEPLOYED_GAME_CONTRACT]
Designate Vanguard (Optional)
To assign a Vanguard, you'll need to call assignVanguard on the anchoring game instance you created.
This can be done as follows using your owner wallet:
cast send [YOUR_GAME_INSTANCE_ADDRESS] \
"assignVanguard(address, uint64)" \
[YOUR_VANGUARD_ADDRESS] \
[YOUR_VANGUARD_ADVANTAGE]
Enable Withdrawals (Optional)
To enable your users to perform withdrawals using Kailua sequencing proposals, you will need to call
setRespectedGameType on your OptimismPortal2 contract using your guardian wallet.
This action may cause your optimism op-proposer agent to crash.
However, you will later run the Kailua proposer agent for sequencing anyway.
cast send [YOUR_OPTIMISM_PORTAL] \
"setRespectedGameType(uint32)" \
[YOUR_KAILUA_GAME_TYPE]
FPVM Upgrade
This section describes how to upgrade the KailuaVerifier contract behind a proxy to a new implementation, for example when the FPVM image ID changes.
Architecture
When Kailua is deployed using Deploy.s.sol, the KailuaVerifier contract is placed behind an OP Stack Proxy (EIP-1967).
The proxy address is what KailuaTreasury and KailuaGame reference. Upgrading the implementation behind the proxy changes the FPVM image ID and other configuration values without redeploying any downstream contracts.
The faultProofPermits mapping lives in proxy storage and is preserved across upgrades.
Configuration values (FPVM_IMAGE_ID, RISC_ZERO_VERIFIER, ROLLUP_CONFIG_HASH, PERMIT_DURATION, PERMIT_DELAY) are Solidity immutables embedded in each implementation's bytecode and change when the implementation changes.
Prerequisites
- Access to the proxy admin private key. By default, the proxy admin is the
DisputeGameFactoryowner. - The address of the deployed
KailuaVerifierproxy (KAILUA_VERIFIER_PROXY). - The new parameter values to change (e.g.
FPVM_IMAGE_ID). Any parameters not specified will be read from the current implementation.
The upgradeTo call must be sent by the proxy admin. If the admin is a multisig or Safe, you will need to route the transaction through the appropriate signing workflow.
Running the Upgrade
Change your working directory to crates/contracts/foundry:
cd crates/contracts/foundry
Set the required environment variables:
export PRIVATE_KEY=[PROXY_ADMIN_PRIVATE_KEY]
export KAILUA_VERIFIER_PROXY=[DEPLOYED_PROXY_ADDRESS]
Set only the parameters you want to change. Any omitted parameters will be read from the current implementation:
# Example: upgrading only the FPVM image ID
export FPVM_IMAGE_ID=[NEW_FPVM_IMAGE_ID]
The full set of optional parameters is:
FPVM_IMAGE_ID: The RISC Zero image ID of the fault proof program.RISC_ZERO_VERIFIER: The address of the RISC Zero verifier contract.ROLLUP_CONFIG_HASH: The hash of the rollup configuration.PERMIT_DURATION: The duration (in seconds) after which a fault proof permit expires.PERMIT_DELAY: The duration (in seconds) after which a fault proof permit becomes active.
Run the upgrade script:
forge script UpgradeVerifierScript --rpc-url [YOUR_ETH_RPC_URL] --broadcast
Verification
After the upgrade, verify the new implementation's values through the proxy:
# Check the new FPVM image ID
cast call [KAILUA_VERIFIER_PROXY] "FPVM_IMAGE_ID() returns (bytes32)" --rpc-url [YOUR_ETH_RPC_URL]
# Check the RISC Zero verifier address
cast call [KAILUA_VERIFIER_PROXY] "RISC_ZERO_VERIFIER() returns (address)" --rpc-url [YOUR_ETH_RPC_URL]
# Check the rollup config hash
cast call [KAILUA_VERIFIER_PROXY] "ROLLUP_CONFIG_HASH() returns (bytes32)" --rpc-url [YOUR_ETH_RPC_URL]
# Check the version
cast call [KAILUA_VERIFIER_PROXY] "version() returns (string)" --rpc-url [YOUR_ETH_RPC_URL]
Off-chain Agents
Once your chain contracts are upgraded to integrate Kailua, this section describes what you need to do for sequencing,
withdrawals, and fault proving to take place in your rollup using Kailua.
Kailua provides two agents to take on the role of the standard Optimism op-proposer and op-challenger agents, and
one RPC server to facilitate dispute game contract selection during withdrawals.
Without a Vanguard, sequencing in Kailua is fully permissionless at all time. Anyone can run these Kailua agents for your rollup and publish proposals when possible.
Just like their optimism counterparts, the Kailua Proposer and Validator must remain online and their wallets sufficiently funded to guarantee the safety and liveness of your rollup.
When using the proveWithdrawalTransaction/finalizeWithdrawalTransaction functions in OptimismPortal2 with Kailua
games, you cannot just use any sequencing proposal that has a valid root claim and assume it can be eventually resolved.
The Kailua RPC can be used to query which dispute game contract can be used to initiate a prove a withdrawal transaction.
Kailua Proposer
The Kailua proposer agent takes care of publishing your local op-node's view of transaction sequencing to Ethereum in
a format that is compatible with the Kailua ZK fault dispute mechanism.
It also attempts to resolve any finalizeable proposals.
Usage
Starting the Kailua proposer is straightforward:
Usage: kailua-cli propose [OPTIONS] --op-node-url <OP_NODE_URL> --op-geth-url <OP_GETH_URL> --eth-rpc-url <ETH_RPC_URL> --beacon-rpc-url <BEACON_RPC_URL>
Remote Endpoints
The mandatory arguments specify the endpoints that the proposer should use for sequencing:
eth-rpc-url: The parent chain (ethereum) endpoint for reading/publishing proposals.beacon-rpc-url: The DA layer (eth-beacon chain) endpoint for retrieving published proposal data.op-geth-url: The rollupop-gethendpoint to read configuration data from.op-node-url: The rollupop-nodeendpoint to read sequencing proposals from.
RPC Calls
To fine-tune the interaction with the above endpoints, the following additional parameters can be specified:
op-rpc-delay: Number of L2 blocks to delay observation by (default: 0).rpc-poll-interval: Time (in seconds) between successive RPC polls (default: 6).op-node-timeout: Timeout (seconds) for an OP-NODE RPC request (default: 5).op-geth-timeout: Timeout (seconds) for an OP-GETH RPC request (default: 2).eth-rpc-timeout: Timeout (seconds) for an ETH RPC request (default: 2).beacon-rpc-timeout: Timeout (seconds) for a BEACON RPC request (default: 20).
Cache Directory
The proposer saves data to disk as it tracks on-chain proposals. This allows it to restart quickly.
data-dir: Optional directory to save data to.- If unspecified, a tmp directory is created.
Kailua Deployment
These arguments manually determine the Kailua contract deployment to use and the termination condition.
kailua-game-implementation: TheKailuaGamecontract address.kailua-anchor-address: Address of the first proposal to synchronize from.final-l2-block: The last L2 block number to reach and then stop.
Telemetry
Telemetry data can be exported to an OTLP Collector.
otlp-collector: The OTLP collector endpoint.
Rollup Config
These arguments tell Kailua how to read the rollup configuration.
bypass-chain-registry: This flag forces the rollup configuration to be fetched fromop-nodeandop-geth.
Wallet
The proposer requires a funded wallet to be able to publish new sequencing proposals on-chain.
proposer-key: The private key for the proposer wallet.proposer-aws-key-id: AWS KMS Key IDproposer-google-project-id: GCP KMS Project IDproposer-google-location: GCP KMS Locationproposer-google-keyring: GCP KMS Keyring Nameproposer-google-key-name: GCP KMS Key name
proposer-key can be replaced with the corresponding AWS/GCP parameters as described here.
The Kailua proposer wallet is critical for security. You must keep your proposer's wallet well funded to guarantee the safety and liveness of your rollup.
Transactions
You can control transaction publication through the following parameters:
txn-timeout: A timeout in seconds for transaction broadcast (Default 120)exec-gas-premium: An added premium percentage to estimated execution gas fees (Default 25)blob-gas-premium: An added premium percentage to estimated blob gas fees (Default 25).eip-7594: Whether to apply EIP-7594 to EIP-4844 blob publications (i.e. Fusaka) (Default false).
The premium parameters increase the internally estimated fees by the specified percentage.
Upgrades
If you re-deploy the KailuaTreasury/KailuaGame contracts to upgrade your fault proof system, you will need to restart
your proposer (and validator).
By default, the proposer (and validator) will use the latest contract deployment available upon start up, and ignore any
proposals not made using them.
If you wish to start a proposer for a past deployment, you can explicitly specify the deployed KailuaGame contract
address using the optional kailua-game-implementation parameter.
When running on an older deployment, the proposer will not create any new proposals, but will finalize any old ones once possible.
Proposal Data Availability
By default, Kailua uses the beacon chain to publish blobs that contain the extra data required for proposals.
Kailua Validator
The Kailua validator watches your rollup for sequencing proposals that contradict each other and generates a ZK fault proof to settle the dispute between them.
The Kailua validator agent requires access to an archive op-geth rollup node to retrieve data during proof generation.
Node software other than op-geth is not as reliable for the necessary debug namespace rpc calls.
Usage
Starting the Kailua validator is straightforward:
kailua-cli validate [OPTIONS] --op-node-url <OP_NODE_URL> --op-geth-url <OP_GETH_URL> --eth-rpc-url <ETH_RPC_URL> --beacon-rpc-url <BEACON_RPC_URL>
Remote Endpoints
The mandatory arguments specify the endpoints that the validator should use to resolve disputes:
eth-rpc-url: The parent chain (ethereum) endpoint for reading proposals.beacon-rpc-url: The DA layer (eth-beacon chain) endpoint for retrieving rollup data.op-geth-url: The rollupop-gethendpoint to read configuration data from.op-node-url: The rollupop-nodeendpoint to read sequencing proposals from.
RPC Calls
To fine-tune the interaction with the above endpoints, the following additional parameters can be specified:
op-rpc-concurrency: Number of concurrent RPC requests to allow (default: 64).op-rpc-delay: Number of L2 blocks to delay observation by (default: 0).rpc-poll-interval: Time (in seconds) between successive RPC polls (default: 6).op-node-timeout: Timeout (seconds) for an OP-NODE RPC request (default: 5).op-geth-timeout: Timeout (seconds) for an OP-GETH RPC request (default: 2).eth-rpc-timeout: Timeout (seconds) for an ETH RPC request (default: 2).beacon-rpc-timeout: Timeout (seconds) for a BEACON RPC request (default: 20).
Cache Directory
The validator saves data to disk as it tracks on-chain proposals. This allows it to restart quickly.
data-dir: Optional directory to save data to.- If unspecified, a tmp directory is created.
Kailua Deployment
These arguments manually determine the Kailua contract deployment to use and the termination condition.
kailua-game-implementation: TheKailuaGamecontract address.kailua-anchor-address: Address of the first proposal to synchronize from.final-l2-block: The last L2 block number to reach and then stop.
Telemetry
Telemetry data can be exported to an OTLP Collector.
otlp-collector: The OTLP collector endpoint.
Rollup Config
These arguments tell Kailua how to read the rollup configuration.
bypass-chain-registry: This flag forces the rollup configuration to be fetched fromop-nodeandop-geth.
Prover
The validator proving behavior can be customized through the following arguments:
kailua-cli: The optional path of the external binary to call for custom proof generation.num-concurrent-provers: Number of provers to run simultaneously (Default: 1).num-concurrent-preflights: Number of threads per prover to use for fetching preflight data (Default: 4).num-concurrent-proofs: Number of threads per prover to use for computing sub-proofs (Default: 1).num-concurrent-witgens: How many threads to use for witness generation per prover.num-concurrent-r0vm: How many threads to use for zkvm executors per prover.segment-limit: ZKVM Proving Segment Limit (Default 21).max-witness-size: Maximum input data byte size per single proof (Default 2.5 GB).max-proof-stitches: Maximum number of derivation proofs to aggregate per stitching proof.max-derivation-length: Maximum number of blocks in a continuous derivation proof sequencemax-block-derivations: Maximum number of blocks to derive per single proof.max-block-executions: Maximum number of blocks to execute per single proof.num-block-partials: Number of partial execution proofs to compute per block (Default 0). Only effective inexperimentalbuilds.num-tail-blocks: Rate of growth of tail proofs in L1 blocks (Default 10).enable-experimental-witness-endpoint: Enables the use ofdebug_executePayloadto collect the execution witness from the execution layer.max-fault-proving-delay: The maximum amount of seconds to wait before starting to compute a fault proof (Default 900).max-validity-proving-delay: The maximum amount of seconds to wait before starting to compute a validity proof (Default 0).clear-cache-data: Whether to clear cache data after successful completion (Default false).export-profile-csv: Whether to export a CSV file with proving performance data (Default false).
Fault Proving Permits
The validator can optionally acquire fault proving permits before generating fault proofs.
fault-proving-permit: Whether acquisition of permits before proving faults is skipped / optional / mandatory (Defaultoptional).fault-proving-permit-expiry: Minimum amount of time (seconds) left on a permit to consider it unexpired (Default 600).min-validity-proving-timestamp: The minimum UNIX timestamp after which computed validity proofs can be submitted (Default 0).
| Value | Behavior |
|---|---|
SKIPPED | Never acquire permits |
OPTIONAL (default) | Acquire if available; proceed without if acquisition fails |
MANDATORY | Halt proving if permit cannot be acquired |
For most operators, the default OPTIONAL policy is recommended.
Use MANDATORY if you are the sole validator and want guaranteed exclusive rewards.
Use SKIPPED if the permit system is deactivated for your deployment or you do not care about rewards being frontrun.
Alt DA
The following additional parameters are required if an alternative DA method is used:
eigenda-proxy-address: URL of the EigenDA RPC endpoint.celestia-connection: Connection to celestia network.celestia-auth-token: Token for the Celestia node connection.celestia-namespace: Celestia Namespace to fetch data from.
Wallet
The validator requires a funded wallet to be able to publish fault proofs on chain, and an (optional) alternative address to direct fault proof submission payouts towards. This wallet can be specified directly as a private key or as an external AWS/GCP signer.
validator-key: The private key for the validator wallet.payout-recipient-address: The ethereum address to use as the recipient of fault proof payouts.validator-aws-key-id: AWS KMS Key IDvalidator-google-project-id: GCP KMS Project IDvalidator-google-location: GCP KMS Locationvalidator-google-keyring: GCP KMS Keyring Namevalidator-google-key-name: GCP KMS Key name
validator-key can be replaced with the corresponding AWS/GCP parameters as described here.
You must keep your validator's wallet well funded to guarantee the liveness of your rollup and prevent faulty proposals from delaying the finality of honest sequencing proposals.
Running kailua-cli validate should monitor your rollup for any disputes and generate the required proofs!
Transactions
You can control transaction publication through the two following parameters:
txn-timeout: A timeout in seconds for transaction broadcast (default 120)exec-gas-premium: An added premium percentage to estimated execution gas fees (Default 25)
The premium parameter increases the internally estimated fees by the specified percentage.
Upgrades
If you re-deploy the KailuaTreasury/KailuaGame contracts to upgrade your fault proof system, you will need to restart
your validator (and proposer).
By default, the validator (and proposer) will use the latest contract deployment available upon start up, and ignore any
proposals not made using them.
If you wish to start a validator for a past deployment, you can explicitly specify the deployed KailuaGame contract
address using the optional kailua-game-implementation parameter.
The validator will not generate any proofs for proposals made using a different deployment than the one used at start up.
Validity Proof Generation
Instead of only generating fault proofs, the validator can be instructed to generate a validity proof for every correct canonical proposal it encounters to fast-forward finality until a specified block height. This is configured using the below parameters:
fast-forward-target: The L2 block height until which validity proofs should be computed.fast-forward-start: Block height to start fast-forwarding finality.
To indefinitely power a validity-proof only rollup, this value can be specified to the maximum 64-bit value of
18446744073709551615.
Running kailua-cli validate with the above parameter should generate a validity proof as soon as a correct proposal
is made by an honest proposer!
Delegated Proof Generation
Extra parameters and environment variables can be specified to determine exactly where the RISC Zero proof generation takes place. Running using only the parameters above will generate proofs using the local RISC Zero prover available to the validator. Alternatively, proof generation can be delegated to an external service such as Bonsai, or to the decentralized Boundless proving network.
All data required to generate the proof can be publicly derived from the public chain data available for your rollup, making this process safe to delegate.
Bonsai
Enabling proving using Bonsai requires you to set the following two environment variables before running the validator:
BONSAI_API_KEY: Your Bonsai API key.BONSAI_API_URL: Your Bonsai API url.
Optionally, the polling cadence can be tuned through a third environment variable:
BONSAI_POLL_INTERVAL_MS: Time in milliseconds between proving session status polls (Default 1000).
Running kailua-cli validate with these two environment variables should now delegate all validator proving to Bonsai!
Boundless
When delegating generation of Kailua Fault proofs to the decentralized Boundless proving network, for every fault proof, a proof request is submitted to the network, where it goes through the standard proof life-cycle on Boundless, before being published by your validator to settle a dispute.
Pricing, timing, and collateral for proof requests can either be computed from static wei-based
parameters (the default — see Legacy Pricing below) or delegated to the
Boundless SDK by passing
--boundless-dynamic-pricing. The SDK determines appropriate prices from market data and gas
costs, sets cycle-aware timeouts, and uses chain-specific collateral defaults. See the
auction parameter guide for
details on how the reverse Dutch auction works.
This functionality requires some additional parameters when starting the validator. These parameters can be passed in as CLI arguments or set as environment variables.
Connection
boundless-rpc-url: The RPC endpoint of the L1 chain where the Boundless network is deployed.boundless-wallet-key: The wallet private key to use to send proof request transactions.boundless-order-stream-url: (Optional) The URL to use for off-chain order submission.boundless-chain-id: EIP-155 chain ID of the network hosting Boundless.boundless-verifier-router-address: Address of the RiscZeroVerifierRouter contract.boundless-set-verifier-address: The address of the RISC Zero verifier supporting aggregated proofs for order validation.boundless-market-address: The address of the Boundless market contract.boundless-collateral-token-address: Address of the stake collateral ERC-20 contract.
Execution Estimation
boundless-look-back: Whether to inspect for duplicates before making a new proof request.boundless-assume-cycle-count: Skip preflighting execution and assume the given cycle count.boundless-assume-cycles-per-gas: Skip preflighting and assume a fixed cycle count per gas.boundless-assume-cycles-per-byte: Skip preflighting and assume a fixed cycle count per input byte.boundless-assume-cycles-per-snark: Skip preflighting and assume a fixed cycle count per recursive snark.
Dynamic Pricing (SDK)
Pass --boundless-dynamic-pricing to delegate pricing to the Boundless SDK based on market
conditions and gas costs. The following optional parameters allow you to override the SDK defaults
(they require --boundless-dynamic-pricing to be set):
boundless-min-price-per-cycle: Minimum price per cycle, e.g."0.00001 USD"or"0.0000001 ETH". Requires a unit. If unset, the SDK uses market pricing from the price provider.boundless-max-price-per-cycle: Maximum price per cycle, same format. If unset, the SDK uses a market-calibrated default plus a gas cost buffer.boundless-max-price-cap: Hard cap on total order price (e.g."0.5 ETH","100 USD"). Safety mechanism to prevent excessive spending.boundless-dynamic-pricing-timeout-modifier: Multiplier applied to both the SDK-computed lock timeout and overall order timeout. E.g.2.0doubles both, preserving the post-lock fulfillment window proportionally. If unset, the SDK-computed values are used unchanged.boundless-dynamic-pricing-ramp-up-modifier: Multiplier applied to the SDK-computed price ramp-up period. If unset, the SDK-computed value is used unchanged.
Retry Escalation
When a proof request expires without being fulfilled, it is automatically resubmitted with increased pricing and timeouts:
boundless-expired-price-inc-perc: Percentage to increase the price by per retry attempt (Default 10).boundless-expired-time-inc-perc: Percentage to increase timeouts by per retry attempt (Default 4).
Order Submission
boundless-order-submission-cooldown: Time in seconds between attempts to submit new orders (Default 12).boundless-order-check-interval: (Defaults to12) Time in seconds between attempts to check order status.boundless-enable-upload-caching: Whether to enable image/input upload caching (Defaulttrue).
Funding
boundless-order-funding-mode: Funding mode for order submission. One ofnever,always,available-balance, orbelow-threshold(Defaultnever).boundless-order-funding-threshold: Threshold (wei) forbelow-thresholdfunding mode.
Legacy Pricing
Legacy static wei-based pricing is the default path. The following parameters tune it. They are
hidden from --help and are rejected when --boundless-dynamic-pricing is set.
boundless-cycle-min-wei: Starting price (wei) per cycle (Default200000000).boundless-cycle-max-wei: Maximum price (wei) per cycle (Default600000000).boundless-mega-cycle-min: Minimum megacycles per proving order (Default 250).boundless-mega-cycle-collateral: Collateral (ZKC) per megacycle (Default2500000000000000).boundless-order-min-collateral: Minimum collateral (ZKC) per order (Default5000000000000000000).boundless-order-bid-delay-factor: Multiplier for delay before price ramp-up starts (Default 0.5).boundless-order-min-bid-delay: Minimum bid delay in seconds (Default 120).boundless-order-ramp-up-factor: Multiplier for price ramp-up duration (Default 1.0).boundless-order-min-ramp-up: Minimum ramp-up time in seconds (Default 600).boundless-order-lock-timeout-factor: Multiplier for lock timeout (Default 3.0).boundless-order-min-lock-timeout: Minimum lock timeout in seconds (Default 1200).boundless-order-expiry-factor: Multiplier for order expiry (Default 1.0).boundless-order-min-expiry: Minimum expiry time in seconds (Default 900).
Storage Uploader
The below second set of parameters determine where the proven executable and its input are stored:
storage-uploader: One ofs3,gcs,pinata, orfile.aws-access-key-id: Thes3access key.aws-secret-access-key: Thes3secret key.s3-bucket: Thes3bucket.s3-url: Thes3url.s3-use-presigned: Use presigned URLs for S3.aws-region: Thes3region.gcs-bucket: The GCS bucket name.gcs-url: The GCS endpoint URL (optional, for emulators).gcs-credentials-json: GCS service account credentials JSON (optional, uses ADC if not set).pinata-jwt: The privatepinatajwt.pinata-api-url: Thepinataapi URL.ipfs-gateway-url: Thepinatagateway URL.file-path: The file storage provider path.r2-domain: Custom domain for file retrieval. Currently used to upload with a custom prefix and replace the download URL with this domain.
Running kailua-cli validate with the above extra arguments should now delegate all validator proving to the Boundless proving network!
Advanced Settings
When manually computing individual proofs, the following parameters (or equiv. env. vars) take effect:
SKIP_AWAIT_PROOF: Skips waiting for the proving process to complete on Bonsai/Boundless.SKIP_DERIVATION_PROOF: Skips provably deriving L2 transactions using L1 data.L1_HEAD_JUMP_BACK: The number of l1 heads to jump back when initially proving.KAILUA_FORCE_RECURSION: Forces stitched sub-proofs to be verified inside the guest program as explicit input instead of through zkVM assumption resolution (testing only).
Kailua RPC
The Kailua RPC watches a Kailua chain deployment for proposals and keeps track of the canonical dispute game contracts that can be safely used to initiate withdrawals on OP Stack knowing that they are guaranteed to eventually be resolved.
Methods
The kailua RPC namespace contains the following method:
kailua_gameAddressForBlockByNumber: Returns the address of the earliest Kailua dispute game contract that can be safely used to prove/finalize a withdrawal in the Optimism portal for any withdrawal initiated at the given L2 block number. (Returns null if no such contract yet exists.)
Using the local devnet deployment, the RPC can be queried as follows:
cast rpc -r http://127.0.0.1:1337 kailua_gameAddressForBlockByNumber 200
Usage
Starting the Kailua RPC is straightforward:
kailua-cli rpc [OPTIONS] --op-node-url <OP_NODE_URL> --op-geth-url <OP_GETH_URL> --eth-rpc-url <ETH_RPC_URL> --beacon-rpc-url <BEACON_RPC_URL>
Remote Endpoints
The mandatory arguments specify the endpoints that the RPC should use to track sequencing proposals:
eth-rpc-url: The parent chain (ethereum) endpoint for reading proposals.beacon-rpc-url: The DA layer (eth-beacon chain) endpoint for retrieving rollup data.op-geth-url: The rollupop-gethendpoint to read configuration data from.op-node-url: The rollupop-nodeendpoint to read sequencing proposals from.
RPC Calls
To fine-tune the interaction with the above endpoints, the following additional parameters can be specified:
op-rpc-delay: Number of L2 blocks to delay observation by (default: 0).rpc-poll-interval: Time (in seconds) between successive RPC polls (default: 6).op-node-timeout: Timeout (seconds) for an OP-NODE RPC request (default: 5).op-geth-timeout: Timeout (seconds) for an OP-GETH RPC request (default: 2).eth-rpc-timeout: Timeout (seconds) for an ETH RPC request (default: 2).beacon-rpc-timeout: Timeout (seconds) for a BEACON RPC request (default: 20).
RPC Endpoint
These optional arguments configure the endpoint that the RPC server listens on:
socket-addr: Socket for http or ws connections.disable-http: Disables listening for RPC requests over HTTP.disable-ws: Disables listening for RPC requests over WS.
Cache Directory
The RPC saves data to disk as it tracks on-chain proposals. This allows it to restart quickly.
data-dir: Optional directory to save data to.- If unspecified, a tmp directory is created.
Kailua Deployment
These arguments manually determine the Kailua contract deployment to use and the termination condition.
kailua-game-implementation: TheKailuaGamecontract address.kailua-anchor-address: Address of the first proposal to synchronize from.final-l2-block: The last L2 block number to reach and then stop.
Telemetry
Telemetry data can be exported to an OTLP Collector.
otlp-collector: The OTLP collector endpoint.
Rollup Config
These arguments tell Kailua how to read the rollup configuration.
bypass-chain-registry: This flag forces the rollup configuration to be fetched fromop-nodeandop-geth.
Dependency Upgrades
This chapter is for Kailua maintainers (and forks) who need to upgrade Kailua to a newer release of
Kona, Hokulea, or
Hana, along with the alloy/op-alloy/revm/reth dependency tree that
rides along with them.
Upgrading these dependencies is unlike upgrading dependencies in a normal Rust project, because most of the affected code is compiled into RISC Zero zkVM guest programs whose image IDs are consensus-critical:
- A successful upgrade always changes the FPVM image IDs, which requires an on-chain upgrade before the new binaries can be used in production.
- A careless upgrade can introduce a soundness gap (e.g. an unhashed configuration field) that no compiler error or test failure will surface unless you follow the audit steps below.
Do not treat a green build as a finished upgrade. The soundness re-audit in Step 3 is mandatory on every upgrade.
How the Pieces Fit Together
Recall from the project structure that each supported DA option has a wrapper crate and a corresponding zkVM guest workspace:
| Guest program | Guest workspace | Compiled-in crates |
|---|---|---|
kailua-fpvm-kona | build/risczero/kona | kailua-kona |
kailua-fpvm-hokulea | build/risczero/hokulea | kailua-kona + kailua-hokulea |
kailua-fpvm-hana | build/risczero/hana | kailua-kona + kailua-hana |
Three properties drive the entire upgrade procedure:
- Each guest workspace is an independent Cargo workspace with its own
Cargo.toml,Cargo.lock, and[patch]section. Version and tag changes must be applied to each of them, not just the root workspace. - Guest builds are reproducible Docker builds (
RISC0_USE_DOCKER=1,--locked) over vendored sources. Reproducibility comes from the pinned RISC Zero Docker toolchain image plus the committed lockfiles — not from your local build cache. - Crate versions feed into image IDs. Cargo passes each crate's version into symbol mangling (
-C metadata), so bumping the version of any crate that compiles into a guest changes that guest's image ID — even with zero source changes. This is whykailua-kona,kailua-hokulea, andkailua-hanadeliberately do not inherit the workspace version: it lets you bump the workspace (host) version without churning guest image IDs, and bump an individual wrapper crate to churn only the guests that depend on it.
Standard vs. Experimental Builds
Every guest program additionally exists in two variants, selected by the experimental cargo feature:
- The standard build already supports proof decomposition down to the block level: derivation-only proofs, execution-only proofs, and the recursive stitching that combines such proofs into a complete proposal proof.
- The experimental build extends decomposition below the block level, allowing a single block's execution to
be proven through partial (per-transaction-chunk) execution traces. Inside the zkVM it also swaps the EVM's
precompile cryptography for RISC Zero's accelerated implementations (the
r0vm_cryptomodule, backed by therisc0-crypto-evmdependency).
Mechanically this is one codebase, not two: bin/cli's experimental feature fans out to kailua-kona,
kailua-prover, and kailua-validator, and build/risczero/build.rs forwards the flag into the guest builds. The
same source tree therefore produces two program families — six committed guest binaries in total, with two sets of
image ID constants (build/risczero/src/fpvm.rs and build/risczero/src/fpvm-experimental.rs) and two
configuration blocks in Setup.
This split has consequences for every step of an upgrade:
- Everything in this chapter happens twice. The clippy/test matrix, the ELF bakes, the image ID constant updates, the documentation values, and (where deployed) the on-chain rollout each have a standard and an experimental leg. Skipping the experimental leg does not produce a stale-but-working experimental build — it produces one that no longer matches the host code.
- A given
kailua-clibinary embeds exactly one family, chosen at compile time. Theexperimentalfeature adds fields to the witness types (pe_witness,partial_executionsincrates/kona/src/witness.rs), so the serialized witness format itself differs between the two builds. Host and guest must be built with the same feature — a standard host cannot drive an experimental guest or vice versa, and there is no runtime toggle. - Some experimental code compiles only inside the guest bake.
r0vm_cryptois gated onexperimentalandtarget_os = "zkvm", so no host build, clippy invocation, or test ever compiles it — the experimental FPVM build is its only compile check, and an error in it surfaces half an hour into the Docker bake. Since it reimplements revm'sCryptotrait, diff it against the vendoredrevm-precompileinterface on everyrevmbump before baking. - The experimental EVM wrappers track upstream trait surfaces.
crates/kona/src/evm/wraps the OP EVM in adapters implementing upstream hook traits (e.g.alloy-op-evm's post-execution hooks). When an upgrade adds methods to those traits, the wrappers gain mandatory delegations. New hooks are usually inert under Kailua's single-pass execution, but verify that for each one and delegate to the inner EVM rather than stubbing a default.
Step 1: Align Versions
The source of truth for the entire dependency tree is the lockfile of the Kona release you are upgrading to:
rust/Cargo.lock in the optimism monorepo at the new
kona-client/vX.Y.Z tag. Kailua's workspace dependencies must resolve to the same versions Kona itself was built
and tested against.
Update the pinned tags and revisions in all of the following files:
Cargo.toml(root): thekona-*,hokulea-*/canoe-*/eigenda-*, andhana-*dependency blocks, and the[patch.crates-io]section (theop-alloy-*,alloy-op-evm, andop-revmpatches must point at the same tag).build/risczero/kona/Cargo.toml,build/risczero/hokulea/Cargo.toml,build/risczero/hana/Cargo.toml: each guest manifest repeats the relevant[patch]entries. These also pin the RISC Zero accelerated forks ofblstandc-kzg— when the new dependency tree raises itsc-kzg/blstrequirements, matching fork releases must exist and be bumped here too.build/risczero/.cargo/config.toml: the[source."git+..."]replacement blocks name the tags/revisions explicitly and must match, or the vendored Docker build will fetch nothing.
Then align the shared crates (alloy-*, op-alloy-*, op-revm, revm, alloy-evm, alloy-op-evm, ...) in the
root [workspace.dependencies] to the versions in Kona's lockfile. Two traps deserve special attention:
Pin the alloy 2.x family with exact version requirements (=2.0.5), not caret ranges.
The host workspace depends on the alloy meta-crate, which exact-pins its whole family — so a host-only build
will look fine either way. But the guest workspaces reference the individual alloy-* crates directly, and a caret
range there floats up to whatever newer minor release exists on crates.io, which Kona predates. This failure mode
only appears inside the FPVM build, long after the host compiles.
After updating, verify the guest lockfiles (e.g. build/risczero/kona/Cargo.lock) resolve to the exact intended
versions.
Kona and alloy-op-evm consume some crates (notably op-revm) from inside the optimism monorepo via path
dependencies. The crates.io release with the same name and version is a different crate to Cargo, so types like
OpSpecId will not unify across the two copies and you will get baffling trait-bound errors. The fix is the
[patch.crates-io] entry pointing that crate at the optimism git tag — in the root manifest and in each of the
three guest manifests.
Exact pins cut both ways: a stale exact pin left over from the previous upgrade forces Cargo to keep the old
version alongside the new one, and two coexisting copies of the same crate produce baffling "expected Evm, found
Evm" trait-bound errors. Every exact pin must be revisited on every upgrade.
Finally, expect release lag. Kona typically releases first, and Hokulea, Hana, and host-only dependencies that track
alloy (e.g. risc0-steel, boundless-market) may not yet have cut a release against the new tree. It is normal to
temporarily pin these to interim git revisions and swap to proper tags in a follow-up once upstream catches up —
the v1.5.2 upgrade shipped exactly that way.
Step 2: Migrate the Source
With versions aligned, work through the compile errors host-first, since host iteration is much faster:
RISC0_SKIP_BUILD=true cargo clippy --bin kailua-cli --locked --all-targets -- -D warnings
RISC0_SKIP_BUILD=true skips the guest builds so you can iterate on crates/kona, crates/hokulea, crates/hana,
and the host crates. Repeat with the feature combinations used by just clippy (including
-F devnet -F eigen -F celestia -F experimental), then check each guest workspace via its own manifest path.
A few hard-won rules for this phase:
- Grep the right checkout. When verifying how an upstream struct changed, resolve the tag to its commit hash via
Cargo.lockand read that exact checkout under~/.cargo/git/checkouts/(or the vendored copy underbuild/risczero/vendor/once vendored). Similarly-named checkouts of forks can contain phantom fields. - The compiler, not grep, is ground truth for field existence. Upstream fields can be
#[cfg(feature = ...)]-gated behind features Kailua does not enable (e.g.RollupConfig::fjord_max_sequencer_drift), so a field visible in source may be absent from the compiled struct. rkyvmirrors must round-trip new fields, not default them.crates/kona/src/rkyv/mirrors upstream types for witness serialization. When an upstream type gains a field, add it to the mirror and its round-trip test. Note thatrkyvonly implements its traits for tuples up to arity 13 — nest a sub-tuple when a mirror outgrows that, following the existing pattern.- Anything serialized into the witness must also be bound by the precondition hash. When a mirrored type gains
a field, check the corresponding
flatten_*helpers: a field that is rkyv-encoded but not hashed is attacker-controlled. - Audit new host-side configuration fields, not just removed ones. When an upstream struct Kailua constructs
(e.g. Kona's
SingleChainHost) gains a field, filling it withDefault::default()compiles but silently adopts whatever new behavior upstream chose. In the v1.5.2 upgrade, Kona's newdata_formatfield defaulted to a new directory-based preimage store format while Kailua's own stores remained RocksDB — preimages seeded through one would have been invisible to the other if this were not examined. Review what each new field does and choose its value deliberately.
Step 3: Re-audit the Config Hash
kailua_kona::config::config_hash (crates/kona/src/config.rs) commits to the rollup and L1 chain configuration a
fault proof is about. Every field of every hashed struct must be included in the hash — an omitted field means
two semantically different configurations collide to the same on-chain commitment, letting a prover prove under a
configuration the verifier never pinned.
On every upgrade, re-audit field coverage of all hashed structs: RollupConfig, SystemConfig, HardForkConfig,
AltDAConfig, BaseFeeConfig, ChainGenesis, BlobParams, and L1ChainConfig.
Most of these are compiler-protected: test_config_hash constructs them with exhaustive struct literals (no
..Default::default()), so a new upstream field breaks compilation until it is named — and hopefully hashed.
L1ChainConfig is an alias for alloy_genesis::ChainConfig and is constructed via ::default() in the tests, so
a new field added upstream compiles silently and silently escapes l1_config_hash. On every alloy bump, diff the full
field list of ChainConfig in the vendored sources against the fields consumed by l1_config_hash, one by one.
Audit against the vendored sources (build/risczero/vendor/) — that is exactly the code the guest compiles.
Remember that adding any field to the hash changes the hash value for all configurations (even those where the
new field is None), which changes the on-chain ROLLUP_CONFIG_HASH alongside the image IDs.
Step 4: Vendor the Guest Dependencies
The Docker guest builds compile from vendored sources. Refresh them with:
just vendor
This runs cargo vendor across the three guest workspaces into build/risczero/vendor and then stages the out-of-crate
files that cargo vendor omits (e.g. the NUT bundle JSONs read by kona-hardforks' build script — see
scripts/stage-nut-bundles.sh).
cargo vendor prints a complete [source] replacement list at the end of its run. Do not paste it wholesale
into build/risczero/.cargo/config.toml. Crates that compile C sources living outside their own crate directory
(e.g. the RISC Zero blst fork builds ../../src/server.c) cannot be vendored — cargo vendor never copies the
out-of-crate C files, and the vendored build fails with "No such file or directory". Keep the replacement list
minimal (the kona/hokulea/hana/reth trees and pure-Rust git dependencies) and let the C-source crates remain
git-fetched inside Docker.
Step 5: Bump Versions and Lockfiles
Decide which crate versions to bump based on which guests actually changed:
- Bump the root
[workspace.package]version — all host-side crates inherit it, and it never affects image IDs. - Bump
crates/konaonly if the Kona-level code changed (this churns all three guest image IDs). - Bump
crates/hokulea/crates/hanafor changes scoped to those wrappers (churns only that guest's image ID). - Mirror any bumped guest-member versions into the corresponding guest
Cargo.tomlandCargo.lockfiles — the Docker build runs with--lockedand will refuse a stale lockfile.
Finally, refresh the root lockfile with cargo update -w (or targeted cargo update -p invocations) and re-run the
host checks.
Step 6: Rebake the FPVM Binaries
The reproducible ELFs are built inside Docker and committed to the repository together with their image IDs.
A rebake is required not only for Kona/Hokulea/Hana changes: bumping the RISC Zero toolchain or the risc0-*
crates also changes every image ID, and a zkVM version bump can additionally change the CONTROL_ROOT/CONTROL_ID
and the required on-chain verifier — check the RISC Zero verifier documentation
when the RISC0_VERSION reported by kailua-cli config changes.
Each variant's bake leaves tens of gigabytes of buildkit cache in the Docker VM, and a full upgrade bakes at least
two variants back-to-back. Check docker system df first and reclaim space with docker builder prune -f if
needed — a full VM disk surfaces as an opaque risc0-build panic ("docker build failed") with the real
"no space left on device" error buried in the log. Losing the cache only costs time; reproducibility comes from the
pinned toolchain image and --locked, not the cache.
The pinned risczero/risc0-guest-builder images are amd64-only (and, as of r0.1.97.0, published as manifest
lists that buildkit platform-matches strictly), so build/risczero/build.rs sets
DOCKER_DEFAULT_PLATFORM=linux/amd64 for the docker build unless the caller already set it — on arm64 hosts the
build runs under emulation, which is slower but produces the same reproducible output. The builder tag itself is
pinned in the same file (docker_container_tag), not by the guest rust-toolchain.toml files, which only govern
native (non-Docker) builds.
For the standard build:
just build-fpvm # reproducible Docker build of all three guests (~30 min)
just export-fpvm # writes build/risczero/src/bin/*.bin and prints each image ID
Then manually paste the printed [u32; 8] image IDs into the constants in build/risczero/src/fpvm.rs.
Repeat for the experimental build: just build-fpvm-experimental, just export-fpvm (the experimental CLI writes
the *-experimental.bin files), and update build/risczero/src/fpvm-experimental.rs.
just export-fpvm runs target/release/kailua-cli export, which writes out the guest binaries embedded in that
CLI binary — the variant is decided by how the CLI was built, not by any export flag. Interleave strictly per
variant: bake standard → export → paste IDs → bake experimental → export → paste IDs. If a bake fails, the
previously built CLI remains in target/release, and running export anyway will silently re-export the other
variant's binaries without any error.
Two verification habits are worth keeping:
- Byte-identical binaries are your regression check. If a guest was not supposed to change (e.g. you bumped only
crates/hokulea), its rebaked.binmust be byte-identical to the committed one —git statussimply won't list it. If an "untouched" guest's binary changed, something leaked into it; find out what before proceeding. - The ID constants and the
.binfiles must come from the same bake.kailua-cli configasserts at runtime that the computed image ID of each embedded ELF matches its hardcoded constant, so a mismatched paste fails loudly — but only once someone runs it.
Finally, update the example image IDs in Setup. Note the format difference: fpvm.rs stores each ID as
[u32; 8] words, while the config command output shown in the book displays the RISC Zero digest form — each word
serialized as little-endian bytes and concatenated. The just export-fpvm log prints both representations' inputs;
the easiest path is to run kailua-cli config against a live rollup and copy its output.
Step 7: Test
Run the full local suite:
just fmt
just clippy # host (default + full-featured) and all three guest workspaces, -D warnings
just test # cargo tests with RISC0_DEV_MODE=1
Run the kailua-kona library tests both with and without -F experimental — the partial-execution and EVM-wrapper
code paths described in Standard vs. Experimental Builds are only compiled and
exercised under the flag (except r0vm_crypto, which only the experimental bake itself compiles). Be patient: the
flag also enables additional stitching tests that iterate over pairs of boot configurations against the ~290 MiB
testdata fixture, so the suite runs many times longer than the standard one — a long-quiet test process is
grinding, not deadlocked. For the same reason, just coverage deliberately omits the flag; the experimental suite runs in the
regular CI test jobs instead.
The recorded-block replay tests in crates/kona re-derive and re-execute real blocks against a committed preimage
fixture (crates/kona/testdata). A Kona upgrade can change which preimages the pipeline requests, failing these
tests with missing-key errors even though the code is correct; in that case the fixture needs to be regenerated by
re-running the recorded proofs in dev mode (RISC0_DEV_MODE=1 kailua-cli prove --native --data-dir ./crates/kona/testdata ... with the boot parameters pinned by the tests), not the code reverted. When regenerating,
flush RocksDB's write-ahead log into the .sst files before committing — preimages that only exist in the trailing
*.log WAL file open fine locally but are matched by .gitignore's *.log rule, producing a fixture that passes
on your machine and fails in CI.
Kona's host backend retries a failing or unresolvable preimage hint indefinitely. Whether in the replay tests, a devnet proving run, or production, a preimage that was seeded into the wrong store (or never seeded) presents as a silent infinite hang at the affected proving phase — not as an error. If a proving pipeline stalls after an upgrade, suspect a missing or misrouted preimage before anything else.
Then validate end-to-end on the local devnet:
just devnet-fetch
just devnet-build-fpvm # debug build with locally-rebuilt guests
just devnet-up
just devnet-upgrade # deploy contracts against the new image IDs
just devnet-propose # run a proposer...
just devnet-validate # ...and a validator
just devnet-fault 1 [PARENT] # publish a faulty proposal and watch it get disproven
A faulty proposal being successfully disproven, and honest proposals resolving, exercises the full pipeline: new guests, new config hash, and new contracts working together.
just devnet-build-fpvm* compiles the guests locally (not in Docker), so the resulting image IDs will not match
the committed reproducible ones — that is expected, and the devnet deploys against whatever it built. Only the
Docker bake from Step 6 produces the canonical IDs.
Step 8: Roll Out On-chain
For an already-migrated rollup, the new image IDs (and, if the config hash computation changed, the new
ROLLUP_CONFIG_HASH) must be pushed on-chain by upgrading the KailuaVerifier implementation as described in
FPVM Upgrade. Fresh migrations pick the new values up automatically through the standard
on-chain migration flow.
Testing Tools
Beyond the agents described in the migration guide, the Kailua CLI ships commands for measuring proving performance, testing fault proofs, and recovering delegated proofs. None of these are required to operate Kailua, but they are useful when evaluating it, dimensioning provers, or exercising a test deployment.
Validity Proving Demo
kailua-cli demo continuously computes validity proofs for a running OP Stack rollup without requiring any Kailua
contract deployment.
This is the quickest way to measure real proving costs and latency for your rollup before migrating.
kailua-cli demo \
--eth-rpc-url [YOUR_ETH_RPC_URL] \
--beacon-rpc-url [YOUR_BEACON_RPC_URL] \
--op-geth-url [YOUR_OP_GETH_URL] \
--op-node-url [YOUR_OP_NODE_URL] \
--num-blocks-per-proof [BLOCKS_PER_PROOF]
num-blocks-per-proof: The number of L2 blocks each proof must cover.starting-block-height: (Optional) The L2 block to start proving from. Defaults tonth-proof-to-processtimesnum-blocks-per-proofblocks before the latest safe block.nth-proof-to-process: Compute only every n-th proof (Default 1), allowing multiple demo instances to split one workload.num-concurrent-provers: Number of provers to run simultaneously (Default 1).kailua-cli: The optional path of the external binary to call for custom proof generation.data-dir: Optional directory to save proving data to.
The prover, delegated proving, and
telemetry parameters of the validator also apply.
The just demo recipe wraps this command.
Benchmarking
kailua-cli benchmark measures proving cost and performance over past L2 blocks.
It scans the bench-range blocks starting at bench-start, selects the bench-count candidate blocks with the
highest transaction counts, and proves the bench-length-block sequence starting at each candidate.
bench-start: The first L2 block number to scan from.bench-range: The number of L2 blocks to scan as benchmark candidates.bench-count: The number of top candidate blocks to benchmark.bench-length: The number of consecutive L2 blocks each benchmark proof must cover.seq-window: The sequencing window size to use for proving.random-select: Select candidates pseudorandomly instead of by highest transaction count (Default false).export-bench-csv: Whether to export a CSV file with the benchmark results (Default false).num-concurrent-provers: Number of provers to run simultaneously (Default 1).
The same endpoint, prover, and delegated proving parameters as demo apply.
The just bench recipe wraps this command.
Fault Injection
kailua-cli test-fault publishes a deliberately faulty sequencing proposal, so that a running validator can be
observed disputing and defeating it.
It accepts all proposer parameters, plus:
fault-offset: The offset of the faulty intermediate output commitment within the published proposal data.fault-parent: The index of the proposal to build the faulty proposal on top of.
The faulty proposal commits to a garbage output root at the chosen offset. An offset within the proposal's output count yields a proposal refutable by an output fault proof, while an offset pointing into the zero-padded trail yields one refutable by a trail fault proof.
The faulty proposal is staked with the proposer wallet's real collateral, which is slashed once the fault is proven. Only use this command against test deployments.
On the local devnet, just devnet-fault [OFFSET] [PARENT] wraps this command.
Receipt Recovery
When proof generation is delegated to a remote service, the finished receipt can be lost if the requesting process
dies before retrieving it (or was run with skip-await-proof).
These two commands download the finished receipt, verify it, and save it to the same proof file a local proving run
would have produced:
kailua-cli bonsai --session-id [SESSION_ID]retrieves a proving session's receipt from Bonsai, using theBONSAI_API_KEY/BONSAI_API_URLenvironment variables.kailua-cli boundless --boundless-rpc-url [RPC_URL] --request-id [REQUEST_ID]retrieves the fulfilled proof for a request from the Boundless market.
Manual Proving
kailua-cli prove is the command the validator internally invokes to compute individual proofs, and it can also be
called manually (see the prove and devnet-prove justfile recipes for example invocations).
Beyond the shared prover and delegated proving
parameters, three parameters bind the computed proof to a proposal's published data instead of a bare block range:
precondition-params: Three comma-separated values: the proposal's starting L2 block number, its published intermediate commitment count, and the number of blocks covered per commitment.precondition-block-hashes: The comma-separated hashes of the L1 blocks at which each of the proposal's data blobs was published.precondition-blob-hashes: The comma-separated versioned hashes of the proposal's data blobs.
When set, the resulting proof can only settle disputes involving proposals that published that exact data. The validator supplies these parameters automatically when generating proofs for on-chain disputes.
Local Devnet
For end-to-end testing, the repository ships a Kurtosis-based local devnet with recipes covering the full lifecycle:
just devnet-up # fetch, patch, and launch the devnet (Docker + Kurtosis required)
just devnet-upgrade # deploy the Kailua contracts (uses RISC0_DEV_MODE)
just devnet-propose # run a proposer against the devnet
just devnet-validate # run a validator against the devnet
just devnet-fault 1 0 # publish a faulty proposal for the validator to defeat
just devnet-down # tear the devnet down
just devnet-clean # tear down and delete all devnet artifacts
The recipes resolve endpoints and prefunded wallet keys automatically from the devnet descriptor produced by
devnet-up.
The underlying scripts — including the EigenDA devnet variant — are described in scripts/README.md.
Sequencing
This chapter is the normative specification of Kailua's sequencing protocol: how the canonical state of a rollup is advanced through proposals, how contradictory proposals compete, and how a proposal becomes finalized. The key words must, must not, should, and may are to be interpreted as requirements on any conforming implementation.
The rules below define the protocol in the abstract.
The KailuaTreasury, KailuaGame, and KailuaTournament contracts constitute the current implementation instance
of these rules on Ethereum, and the proposer agent is the reference implementation of the
proposer role.
Implementation notes throughout this chapter map each abstract rule to its current enforcement point, but the abstract
rules — not the contracts — are the source of intended behavior.
Protocol Parameters
A sequencing deployment is parameterized by the following values, fixed at deployment time:
| Symbol | Name | Meaning |
|---|---|---|
N | proposal output count | The number of output commitments each proposal must publish. |
S | output block span | The number of L2 blocks covered by each output commitment. |
T | challenge timeout | The period during which a proposal remains contestable. |
B | participation bond | The collateral a proposer must lock before proposing. |
V, A | vanguard, advantage | An optional privileged proposer and the duration of its priority window. |
G, t | genesis time, block time | The timestamp of the L2 genesis block and the L2 block interval. |
Each proposal advances the chain by exactly N × S blocks.
These parameters are immutable values of the deployed contract instance: PROPOSAL_OUTPUT_COUNT,
OUTPUT_BLOCK_SPAN, MAX_CLOCK_DURATION, participationBond (owner-adjustable), vanguard/vanguardAdvantage
(owner-assignable), and GENESIS_TIME_STAMP/L2_BLOCK_TIME. See deployment parameters.
Proposals
The protocol maintains a tree of proposals rooted at a trusted anchor: a starting state assumed valid by all participants.
--- title: Example Proposal Tree (N × S = 64 blocks per proposal) --- graph LR; A[Anchor: 0] --> B[B: 64]; A --> B'[B': 64]; B --> C[C: 128]; B --> C'[C': 128]; C --> D[D: 192];
Siblings with different identities — such as B and B' above — implicitly dispute one another, with no separate
challenge step. The tournament rules below determine which child of each parent, here along the path
Anchor → B → C → D, may carry finality forward.
A proposal extends exactly one parent (the anchor or an earlier proposal) and consists of:
- A claim: an output commitment to the rollup state at height
h = h_parent + N × S. - Intermediate commitments: output commitments at each of the
N − 1heightsh_parent + S, h_parent + 2S, …, published to a data availability layer such that each individual commitment can later be provably read back on the settlement layer. The claim itself serves as theN-th commitment. - A derivation anchor: a reference to the settlement-layer (L1) state from which the proposal's rollup data is derivable.
- A duplication counter: an ordinal distinguishing repeated publications of otherwise identical proposal data.
Publication space not occupied by intermediate commitments (trailing data) must be zero-valued.
Every proposal has an identity: a binding commitment to its claim together with the entirety of its published data — intermediate commitments and trailing data alike. Two proposals with equal identities are duplicates: they assert exactly the same state transitions and the protocol treats them as sharing one fate.
A proposal's identity deliberately excludes its derivation anchor. Whether a proposal's commitments are correct is independent of which L1 view they are derived from — this is eventually demonstrated by a proof against some published derivation anchor.
Proposals are KailuaGame instances created through the rollup's DisputeGameFactory; the anchor is the
KailuaTreasury instance. Intermediate commitments are 32-byte output roots reduced to BLS12-381 scalar field
elements (hash_to_fe) and published as EIP-4844 blobs — ceil((N − 1) / 4096) blobs per proposal — with KZG
openings as the read-back mechanism. The claim is the game's rootClaim, the derivation anchor its l1Head, and the
identity is signature = sha256(rootClaim ‖ proposalBlobHashes).
Proposal Creation Rules
A conforming implementation must reject any proposal that violates one of the following rules.
Proposer eligibility:
- The proposer must not have been eliminated at any prior point.
- The proposer must have at least
Bcollateral locked with the protocol at creation time. - Successive proposals by the same proposer must strictly increase in height.
Structural integrity:
- Proposals must enter the protocol only through its sanctioned creation path, and be initialized exactly once.
- A proposal's encoding must be canonical: two encodings of the same proposal data must not be able to coexist as distinct proposals.
- Duplication counters must be assigned sequentially: a proposal with counter
d > 0is valid only if a proposal with identical claim data and counterd − 1exists. - The proposal's height must equal its parent's height plus exactly
N × S. - All published data the proposal commits to must be available at creation time.
Tree consistency:
- The parent must itself be a proposal known to the protocol (or the anchor).
- The proposal's identity must still be viable in the parent's tournament: an identity proven faulty, or one that contradicts an identity proven valid, must not be (re-)introduced.
- The parent's tournament must not already have produced a finalized successor.
Timing:
- A proposal must not be created before the earliest time its claimed height can exist according to the rollup's
block schedule:
G + h × t. - If a vanguard
Vis assigned, onlyVmay create the first child of any parent until the advantage durationAhas elapsed beyond the time given by rule 12. Other proposers may always create counter-proposals to an existing first child.
The duplication counter (rules 5–6) exists so that a correct claim can be re-proposed when all prior proposals carrying it are disqualified — for example, when the original was published by a since-eliminated proposer, or alongside faulty intermediate commitments (which alter the identity but not the claim).
- Constant collateral (rules 1–3). One bond of
Bbacks a proposer's entire chain of proposals, with at most one proposal per tournament. An honest proposer's collateral requirement never grows with the number or size of attacks against it — the resource-exhaustion resistance described in the introduction. - Exhaustive accounting (rules 4–6). A single creation path and a canonical encoding make the protocol's record of children and duplicates complete and unambiguous, so tournament outcomes can be computed from that record alone.
- A connected, judgeable tree (rules 7–11). Every proposal attaches at exactly one known parent and height, with its full data available for judgement from the moment of creation, and nothing can attach below a decided tournament — settled state can never be re-opened.
- Meaningful clocks (rules 12–13). A challenge window that opens before the claimed blocks can even exist burns dispute time while there is nothing yet to check; rule 12 prevents this, and rule 13 grants the vanguard only a bounded head start that can never block counter-proposals to an existing first child.
Rules 1–3 and 13 are enforced by KailuaTreasury.propose (BadAuth, IncorrectBondAmount, BlockNumberMismatch,
VanguardError); rules 4–12 by KailuaGame.initialize (Blacklisted/AlreadyInitialized/UnknownGame,
BadExtraData — via a fixed 0x72-byte calldata length that removes encoding malleability —
InvalidDuplicationCounter, BlockNumberMismatch, BlobHashMissing, InvalidParent, ProvenFaulty,
ClaimAlreadyResolved, ProposalGapRemaining).
Tournaments
The children of a proposal compete in a tournament to become its unique finalized successor. The tournament is decided by the following match rules, evaluated over children in order of creation:
- The earliest child whose identity is still viable (creation rule 10) and whose proposer is not eliminated is the current contender.
- Each later child (opponent) is compared against the contender:
- An opponent created after the contender's challenge window (
Tfrom the contender's creation) had already closed is disregarded, and the contender wins the tournament outright. Late counter-proposals cannot re-open a settled claim. - An opponent from an eliminated proposer is skipped.
- An opponent with the same identity is a duplicate of the contender and shares its fate.
- An opponent whose identity was proven faulty is disqualified, and its proposer eliminated.
- An opponent with a different but still-viable identity blocks the tournament: a match between two contradictory, unproven identities must not be decided by anything other than a proof.
- An opponent created after the contender's challenge window (
- If the contender's own identity becomes unviable, the contender and all of its recorded duplicates are disqualified — their proposers eliminated — and the contender search resumes from the next child.
---
title: Match Evaluation Against the Contender
---
graph TD;
O[next opponent] --> L{created after the contender's challenge window?};
L -- yes --> W[contender wins the tournament];
L -- no --> E{proposer eliminated?};
E -- yes --> S[opponent skipped];
E -- no --> I{same identity as contender?};
I -- yes --> D[recorded as duplicate];
I -- no --> F{proven faulty?};
F -- yes --> X[opponent disqualified];
F -- no --> P[tournament blocked until a proof decides];
A tournament may be played out incrementally, but its outcome must be a deterministic function of the recorded proof outcomes, independent of who evaluates it.
- Safety without deadlines. A match between contradictory identities is only ever decided by a proof, and no proof can convict a correct proposal. An honest proposal therefore survives every match it is drawn into without its proposer ever having to respond — attackers who flood a tournament with contradictions only queue up their own eliminations, at no added cost to the defender (see Sybil identities).
- Bounded delay. Because opponents arriving after the contender's challenge window are disregarded, an adversary cannot keep a settled claim contested by continuously re-proposing against it. Once a correct contender's window closes, finality waits only on proofs against the contradictions created inside that window — a workload that shrinks with proving power rather than growing with the attacker's persistence (see Withdrawal delay).
Resolution
A proposal becomes finalized — its claim accepted as the canonical rollup state — only when all of the following hold:
- Its parent is finalized. Finality proceeds strictly from the anchor outward.
- Either its full challenge timeout
Thas elapsed since creation, or its identity has been proven valid (finality fast-forward). - It has won its parent's tournament as the surviving contender.
The anchor itself is finalized directly by deployment governance, which is how a new deployment is activated.
--- title: Proposal Lifecycle --- graph LR; C[Created] -- challenge window T elapses --> R[Resolvable]; C -- identity proven valid --> R; R -- parent finalized, tournament won --> F[Finalized]; C -- proven faulty, or contradicts a valid identity --> X[Disqualified];
Condition 1 exists because a proposal's correctness is only ever conditional on its parent: proofs judge the transition starting from the parent's claim. Finalizing strictly outward from the anchor turns this chain of conditional statements into an unconditional one — and gives faults a free cascade: once a proposal is eliminated, its entire descendant subtree simply never finalizes, without a single proof against any descendant.
Tournaments are evaluated by KailuaTournament.pruneChildren (progress persisted in
contenderIndex/opponentIndex/contenderDuplicates; a viable contradiction reverts with NotProven).
Resolution conditions are enforced by KailuaGame.resolve (OutOfOrderResolution, ClockNotExpired/ProvenFaulty,
NotProven), and anchor finalization by the factory-owner-gated KailuaTreasury.resolve.
Elimination and Collateral
Elimination permanently disqualifies a proposer:
- A proposer is eliminated when one of their proposals is disqualified in a tournament match — its identity proven faulty, or contradicting a proven-valid identity.
- Elimination happens at most once per proposer, and only through a tournament of a finalized parent.
- The eliminated proposer's locked collateral is slashed and split three ways: one share to a prover beneficiary — resolved by the payout precedence — one share to the proposer of the tournament's eventual winner, and one share burned.
- From the offending proposal onward, all of the proposer's proposals are skipped in every tournament, and the proposer may never propose again. Proposals made before the offending one remain in play.
--- title: Slashed Bond Distribution --- graph LR; B[slashed bond B] --> P[⅓ prover reward]; B --> W[⅓ tournament winner]; B --> X[⅓ burned];
Each share of the split serves a distinct purpose: the prover share is the bounty that funds permissionless proving, so that every fault pays for its own conviction; the winner share compensates the proposer whose correct counter-proposal kept the chain live while its collateral was locked in the dispute; and the burned share guarantees that every proven fault carries a strictly positive cost — even a proposer who convicts themselves through colluding prover and winner identities recovers at most two thirds of their bond.
Honest proposers recover their collateral in full: a proposer who was never eliminated may withdraw their bond once every tournament containing one of their proposals has produced a finalized successor.
The one-bond-many-proposals design means a single proven fault forfeits the proposer's entire bond, no matter how many correct proposals they have in flight. There is no partial slashing.
KailuaTreasury.eliminate enforces rule 2 (Blacklisted, NotProposed, AlreadyEliminated) and performs the
1/3 prover / 1/3 winner / 1/3 burn split; the winner's share accrues per-tournament and is credited upon resolution.
Bond recovery is claimProposerBond.
The Proposer Role
Any party may act as a proposer. For the rollup to remain both safe (no incorrect state finalizes) and live (correct state keeps finalizing), at least one honest, well-collateralized proposer must exist, and it must behave as follows:
- Verify before extending. An honest proposer must validate every observed proposal — claim, each intermediate commitment, and the zeroness of trailing data — against its own trusted view of the rollup, and extend only the canonical tip: the highest correct proposal made by a non-eliminated proposer.
- Propose only settled data. Proposals must be assembled exclusively from L2 data the proposer's view considers final, so published commitments can never be invalidated by an L2 reorg.
- Respect timing. The proposer must wait out rule 12's minimum creation time, and — when it is not the vanguard — rule 13's advantage window, rather than submit proposals that will be rejected.
- Handle duplicates deliberately. When its intended claim has already been proposed, the proposer must locate the lowest unused duplication counter, skipping duplicates that are faulty or authored by eliminated proposers — and must not re-propose at all if a correct duplicate by a non-eliminated proposer is already live, since doing so locks additional collateral without changing any outcome.
- Maintain collateral. The proposer must ensure its locked collateral meets
B(topping up exactly the shortfall) before proposing, and must keep its wallet funded — an unfunded honest proposer is a liveness failure. - Drive resolution. The proposer should finalize resolvable proposals along the canonical chain: waiting out timeouts (or acting on recorded validity proofs), advancing tournament evaluation, and resolving the surviving successor. Where evaluation is blocked by an undecided match, resolution must wait for a proof.
- Monitor its own standing. A proposer observing its own elimination must alert its operator: its key is compromised or its data sources fed it incorrect outputs. It must also verify it is targeting the deployment's current implementation instance before proposing.
kailua-cli propose implements this role: it validates all proposals against op-node output roots, gates proposals
on the finalized L2 head, performs the duplication-counter search and honest-duplicate skip, tops up bond shortfalls
in the propose call, issues incremental tournament-evaluation transactions in bounded batches, and raises telemetry
alarms upon self-elimination. See Proposer for operation.
A proposer's safety reduces to the integrity of its rollup view.
An honest implementation connected to a divergent or lagging op-node will publish provably faulty proposals and
forfeit its collateral; the protocol cannot distinguish it from a malicious proposer.
Validating
This chapter is the normative specification of Kailua's validation protocol: how disputes between contradictory
sequencing proposals are settled by proofs, and how proposals are proven valid ahead of their
challenge timeout.
As in the previous chapter, the key words must, must not, should, and may denote
requirements on any conforming implementation, and the rules below define the protocol in the abstract — with the
KailuaTournament and KailuaVerifier contracts as the current implementation instance and the
validator agent as the reference implementation of the validator role.
Proof submission is permissionless and non-interactive: any party may settle any dispute with a single submission, and proofs attach to a proposal's identity rather than to any individual proposal instance.
Transition Proofs
The foundation of validation is the transition proof: a succinct, verifiable attestation of one application of the rollup's state transition function. A transition proof establishes a claim tuple:
| Component | Meaning |
|---|---|
| beneficiary | The party to be credited for the proof. |
| precondition | An optional commitment binding the proof to externally published data (see validity proofs). |
| derivation anchor | The settlement-layer (L1) state from which all rollup inputs were derived. |
| agreed output | The starting output commitment, assumed correct. |
| computed output | The output commitment reached by applying the state transition function. |
| computed height | The rollup height of the computed output. |
| configuration | A binding commitment to the rollup configuration under which the transition was computed. |
| program | A binding commitment to the state transition program itself. |
A conforming verification procedure must accept a claim tuple only if starting from the agreed output, deriving the rollup using only data reachable from the derivation anchor yields the computed output at the computed height, under the committed configuration and program.
--- title: The Statement Established by a Transition Proof --- graph LR; DA[derivation anchor] --> STF[state transition program]; AO((agreed output)) --> STF; CFG[configuration] --> STF; STF --> CO((computed output at computed height));
The protocol imposes two constraints on how claim tuples may be formed:
- The derivation anchor must be one committed by a proposal registered in the protocol. Provers cannot introduce an arbitrary L1 context; they may only derive from a view that some proposal has staked on.
- The computed height must equal the parent proposal's height plus a whole number of output-span (
S) steps, so proofs always align with the commitment schedule of the proposals they judge.
There is no separate "fault program" and "validity program": the state transition program always computes the correct output. A fault is established by showing the proven-correct output differs from what a proposal published; validity is established by showing it equals the proposal's claim. Fault proofs exhibiting agreement are rejected (output fault rule 3) — a proof can never convict a correct proposal.
Transition proofs are RISC Zero zkVM receipts for the Kailua FPVM program (a Kona-based derivation client).
The claim tuple is the FPVM's journal; KailuaVerifier.verify reconstructs the expected journal — substituting its
immutable ROLLUP_CONFIG_HASH and FPVM_IMAGE_ID for the configuration and program commitments — and delegates seal
verification to the deployed RISC Zero verifier contract. Constraint 1 is enforced by requiring the anchor to be the
l1Head of a treasury-registered proposal (UnknownGame); constraint 2 by having the tournament contract itself
compute the claim height.
Proof Classes
Three classes of proof can be recorded against a child identity in a tournament:
| Proof class | Needs a transition proof? | Establishes |
|---|---|---|
| Validity | Yes | Every commitment made by the proposal is correct. |
| Output fault | Yes | One published output commitment contradicts the derivable state. |
| Trail fault | No | The proposal's published data violates the required format. |
For every proof class, a conforming implementation must enforce:
- Proofs may be accepted for or against a child only until its parent's tournament produces a finalized successor.
- At most one proof outcome is ever recorded per identity; the outcome, the prover, and the proving time are recorded permanently.
- Proof submission must remain open to any party for as long as rule 1 permits.
- Sybil resistance. Because proofs attach to identities and are recorded exactly once, duplicating a faulty proposal multiplies the attacker's locked collateral without adding a single unit of proving work for the defense — one proof settles every copy (see Sybil identities).
- No proving deadlines. The challenge timeout limits only the creation of contradictions (tournaments), never their resolution (rule 3): however large the attack, each pending match waits indefinitely for its proof, so proving power determines how fast disputes settle — never whether they settle correctly (see Resource exhaustion).
- Permissionless settlement. Any party — not only the disputants — can supply the deciding proof, so settlement liveness does not rest on the parties whose collateral is at stake.
Enforced by KailuaTournament for all classes: GameNotInProgress, ClaimAlreadyResolved, and AlreadyProven; the
recorded outcome is proofStatus[signature] with prover and provenAt, announced by the Proven event.
Validity Proofs
A validity proof vouches for a child's entire commitment set in one step. It must consist of a transition proof
whose agreed output is the parent's claim and whose computed output is the child's claim, at the child's full height —
covering all N × S blocks.
--- title: Validity Proof Coverage (N = 4) --- graph LR; P((parent claim)) ==4 × S blocks proven==> c((claim)); P -.-> o0((o₀)) -.-> o1((o₁)) -.-> o2((o₂)) -.-> c;
When a proposal publishes more than one output commitment (N > 1), the transition proof must additionally commit —
through its precondition — to the equivalence of the derived intermediate outputs and the proposal's published
intermediate commitments. A validity proof thereby vouches for the published data, not merely the final claim.
Recording a validity proof makes the proven identity the tournament's unique valid identity, with these effects:
- Every other child identity in the tournament immediately becomes unviable; contradictory siblings are disqualified by the tournament rules — their proposers eliminated — without individual fault proofs.
- No proposal contradicting the valid identity may be created from that point on (creation rule 10).
- Children bearing the valid identity no longer wait out the challenge timeout: they finalize as soon as their parent is finalized — the finality fast-forward of resolution rule 2.
KailuaTournament.proveValidity stores validChildSignature and passes the parent claim, child claim, and full
output count to verification. The precondition is
sha256(parent height ‖ N ‖ S ‖ blobsHash) over the child's blob hashes; the FPVM enforces it by checking each
derived intermediate output against the corresponding blob field element.
Output Fault Proofs
An output fault proof convicts a child by exhibiting a single divergent output commitment.
For a disputed commitment at position i (where the proposal's commitments occupy positions 0 through N − 1, the
claim being the last), a conforming implementation must require:
iaddresses one of the proposal'sNoutput commitments.- Agreed output. For
i = 0, the transition proof's agreed output must be the parent's claim. Fori > 0, the submitter must demonstrate — via the data availability layer's read-back mechanism — that the proposal itself published the agreed output as its commitment at positioni − 1. - Divergence. The proven computed output must contradict the proposal's commitment at position
i: for the final position, it must differ from the proposal's claim; for intermediate positions, the submitter must demonstrate what the proposal published at positioniand that it differs from the computed output. Proofs exhibiting agreement must be rejected. - The transition proof must cover exactly the
Sblocks from positioni − 1(or the parent claim) to positioni, with no precondition.
--- title: Output Fault Proof Against Position 2 (N = 4) --- graph LR; P((parent claim)) -.S.-> o0((o₀)) -.S.-> o1((o₁ agreed)) ==S blocks proven==> x((computed ≠ o₂)) -.S.-> c((claim));
Rule 2 is what makes disputes cheap: because the agreed output is read from the faulty proposal's own publication, a
fault proof only ever derives the S blocks between two adjacent commitments — never the whole proposal.
The choice of N and S thus bounds the worst-case proving work for any single dispute, as illustrated in the
design overview.
Note that the agreed output need not be correct — position i − 1 may itself be faulty. The proof only establishes
that the proposal is inconsistent with its own published data at position i, which suffices: whatever the truth,
an identity that diverges from the derivable state at any position is not the correct one.
A recorded fault makes the identity permanently unviable: the proposal and all duplicates can never finalize, and the tournament rules disqualify them, eliminating their proposers.
KailuaTournament.proveOutputFault (InvalidDisputedClaimIndex, NoConflict). Read-back of published commitments is
by KZG opening against the blob hashes fixed at creation, checked with the point-evaluation precompile
(bad acceptedOutput kzg / bad proposedOutput kzg); comparisons use the field-element reduction of output roots,
and the precompile inherently rejects non-canonical field elements.
Trail Fault Proofs
A trail fault proof convicts a child of violating the publication format, and requires no transition proof: the submitter demonstrates, via the read-back mechanism, that the proposal published a non-zero value in its trailing (zero-padding) region. Implementations must reject trail fault claims that address the commitment region or exhibit a zero value. A recorded trail fault has the same effect as an output fault.
Trail faults close the last gap in decidability. A proposal whose N output commitments are all correct but whose
trailing data is non-zero still contradicts the honest identity — yet no output fault proof can convict it (there is
no divergence to exhibit), and no validity proof can vouch for it (transition proofs enforce zero trailing data
through their precondition). Without trail faults, such an identity would block its tournament forever. With them, the
protocol upholds the invariant that every pair of contradictory identities is decidable by some proof: two
identities can only differ in an output commitment (an output fault against at least one) or in the padding (a trail
fault). Equivalently, the zero-padding rule gives correct data exactly one identity, so honest proposals always converge on a
single identity that shares one proof and one fate.
KailuaTournament.proveTrailFault (InvalidDisputedClaimIndex, NoConflict, InvalidDataRemainder — the disputed
position must fall in the final blob, which loses no generality since earlier blobs are fully packed with
commitments).
Payouts and Permits
When a proposer is eliminated, the prover's share of the slashed bond is paid to a beneficiary resolved in this order of precedence:
- The holder of the sole fault proving permit for the convicted identity — if exactly one permit was ever acquired for it, and that permit was active when the convicting fault proof was recorded.
- The recorded prover of the convicted identity.
- The recorded prover of the tournament's valid identity, when elimination resulted from a validity proof.
---
title: Prover Share Beneficiary
---
graph TD;
F[proposer eliminated] --> Q{sole permit, active at fault proof time?};
Q -- yes --> H[permit holder];
Q -- no --> R{fault proof recorded for the convicted identity?};
R -- yes --> V[its recorded prover];
R -- no --> W[prover of the valid identity];
Permits give provers an exclusive reward window in exchange for locked collateral. A conforming permit mechanism must enforce:
- Permits may only be acquired for identities that are still viable.
- The permit collateral must cover at least twice the prover's elimination share, so that abandoning a permit costs more than the reward it protects.
- Permit issuance capacity must be bounded by expiry: at most one more permit than twice the number of already-expired permits may exist for an identity at any time.
- A permit becomes active only after its activation delay, expires after its total duration, and its collateral is returned when released after a proof recorded within its lifetime. Holders whose permits expired before the proof forfeit their collateral to the holders active at proving time.
- Constant cost to participate, exponential cost to monopolize. Every permit costs the same fixed collateral (rule 2) no matter how many were issued before it, so entering the reward race never grows more expensive. Capacity, however, compounds through expiry (rule 3): one permit may exist initially, three once it expires, seven once those do, and so on — and every expired permit forfeits its collateral (rule 4). An actor attempting to hold all permits for a fault must therefore burn collateral that doubles with each expiry cycle, while any competitor can always claim a newly opened slot at the constant price — squatting on exclusivity to stall honest proving becomes exponentially expensive (see Denial-of-Service).
- Every active prover is paid, despite a single proposer bond. The eliminated proposer posted one bond, whose prover share can reward only one beneficiary — yet expiries may leave several permits simultaneously active when the fault proof lands. The scheme makes these payouts self-funding: each holder active at proving time recovers its own collateral plus an equal share of the collateral forfeited by expired permits. Because active permits can outnumber expired ones by at most one (rule 3), and each expired permit forfeited twice the prover share (rule 2), this pool pays every active holder at least a full prover reward — funded by the forfeited collateral alone, not the bond. The sole-permit case needs no pool: its holder is paid the bond's prover share directly, per the precedence above.
KailuaVerifier.acquireFaultProofPermit (ProvenFaulty, IncorrectBondAmount, ClockNotExpired) and
releaseFaultProofPermit (NotProven), parameterized by the immutable PERMIT_DELAY and PERMIT_DURATION;
beneficiary resolution is getPayoutRecipient in KailuaTournament.
The Validator Role
Any party may act as a validator. The protocol's safety argument requires that every dispute eventually receives a deciding proof; for this, at least one honest validator must exist and behave as follows:
- Prove only provable statements. A validator must confirm that the deployment's committed program and configuration are ones it can execute and vouch for, before dedicating any resources to the deployment.
- Judge from a trusted view. A validator must assess every observed proposal — claim, each intermediate commitment, and trailing data — against its own trusted view of the rollup, and must defer judgement on any proposal whose height its view has not yet settled. It must never derive a fault verdict from unverifiable data.
- Target the first divergence. For an incorrect proposal, the validator should convict at the cheapest sufficient
point: a format violation (trail fault) needs no transition proof and takes precedence; otherwise the earliest
divergent commitment, so the transition proof spans a single
S-block step whose agreed output is still shared with the canonical chain. - Avoid redundant work. A validator should prove each identity at most once across all duplicates, skip disputes already settled (re-checking immediately before submission, since other validators race for the same disputes), decline to fault-prove when a validity proof already settles the tournament, and de-correlate its proving schedule from other validators (e.g., by randomized delay) where redundancy is not desired.
- Use registered derivation anchors. A validator must construct claim tuples only over registered derivation anchors (constraint 1): beginning with the disputed proposal's own, and — if its L1 view proves insufficient to derive the disputed span — retrying with successively later ones.
- Verify before submitting. A validator should verify its own proofs and cross-check every claim-tuple component and read-back opening against the settlement layer before submission, and re-buffer rather than discard proofs whose submission fails.
- Fast-forward when appropriate. A validator may prove the validity of correct proposals to accelerate finality.
On deployments where
N = 1, it should respond to any conflict with a validity proof of the correct sibling: a single such proof both accelerates the honest proposal and eliminates every contradiction, and there is no cheaper intermediate fault to exhibit. - Honor the permit protocol. A validator using permits must acquire them only when capacity is available, wait for activation before submitting the associated proof so the exclusive window applies, and release them after the proof is recorded to reclaim collateral.
kailua-cli validate implements this role: it refuses deployments whose FPVM_IMAGE_ID is not baked into its
binary, gates all judgement on op-node finality, classifies faults as trail-first-then-earliest-output, re-checks
proofStatus/isViableSignature at queueing, dispatch, and submission time, retries proof generation across
successive proposal l1Heads, locally verifies receipts and dry-runs KZG openings before spending gas, and manages
permits per the configured policy. See Validator for operation.
A validator's effectiveness reduces to the integrity of its rollup view (op-node and archive op-geth).
The proof system prevents a misinformed validator from harming the protocol — incorrect verdicts yield unprovable
statements or rejected submissions — but not from wasting proving effort or failing to convict real faults.