Proofs
Citrea's proof system is the backbone of its security model. By generating zero-knowledge proofs of correct state transitions, Citrea ensures that anyone can verify the rollup's integrity without re-executing every transaction. This page provides a technical deep dive into the proof architecture, covering prover nodes, circuits, and the verification pipeline.
Proof System Overview
Citrea employs a two-tier proving architecture:
Batch Proofs: Prove the correct execution of sequencer commitments (L2 state transitions)
Light Client Proofs: Aggregate batch proofs and verify Bitcoin consensus, forming a recursive proof chain
Both proof types are generated using the RISC-0 zkVM, which executes RISC-V programs and produces STARK proofs that are then wrapped in Groth16 SNARKs for succinct verification by full nodes and the Light Client Prover. Proofs are inscribed on Bitcoin for permanent availability. The design enables full nodes to verify proofs directly, while the Light Client Prover produces a single recursive proof that external verifiers (including the Clementine bridge) can check without processing the entire chain history.

In addition to these two proof types, Citrea's bridge Clementine uses two more proof types:
Bridge Proofs: Proves the honesty of the bridge operator by recursively verifying light client proofs and proving that withdrawals are paid out correctly.
Header chain proofs: Prove the correctness of the Bitcoin header chain by verifying the difficulty adjustments, timestamps, and previous block hash linkage.
Header chain proofs are used by Watchtowers to challenge a bridge operator by proving the total work of the longest chain, later the challenged operator will need to prove it's honesty by providing a light client proof with more work than the Watchtower's proof. More details about the challenge process can be found in the Clementine documentation.
Node Types
Batch Prover
The Batch Prover is a dedicated node that monitors Bitcoin for finalized sequencer commitments and generates proofs of correct L2 execution. It maintains a replica of the rollup state and coordinates with the zkVM to produce succinct proofs.
Core Responsibilities:
Syncs L1 (Bitcoin) blocks to track finalized sequencer commitments
Syncs L2 blocks from the sequencer to maintain state
Partitions commitments into provable batches
Generates circuit inputs and coordinates proof generation
Submits verified proofs to Bitcoin
Batch Prover Architecture
The Batch Prover consists of three interdependent modules:
L1 Syncer:
Tracks finalized Bitcoin blocks via the DA service
Extracts sequencer commitments from Bitcoin blocks
Stores short header proofs for verifying
setBlockInfosystem transactionsRecords block height-to-hash mappings and commitment indices
Signals the prover when new L1 blocks are processed
L2 Syncer:
Fetches L2 blocks from the sequencer's RPC endpoint
Applies blocks to the state transition function (STF)
Maintains current state root and L2 block hash
Validates state against sequencer claims
Prover Core:
Receives signals from both syncers
Manages proving session lifecycle
Creates circuit inputs from commitment partitions
Coordinates with parallel proving service
Handles proof verification and submission
Light Client Prover
The Light Client Prover (LCP) produces recursive proofs that encapsulate the entire rollup state. Each proof builds on the previous one, verifying both the batch proofs and Bitcoin's consensus rules. Verifying a single proof attests that, for the given Bitcoin chain up to that DA block, the L2 state produced by the circuit is correct, verified, and consistent with the on-chain commitments. For a detailed explanation, see the light client circuit documentation.
Core Responsibilities:
Processes each Bitcoin block containing Citrea data
Verifies Bitcoin header chain (proof-of-work, difficulty adjustments)
Validates and aggregates batch proofs
Maintains a Jellyfish Merkle Tree (JMT) state for commitment tracking
Produces recursive proofs for external verification
The Light Client Prover's output feeds directly into Clementine. The bridge uses the LCP proof in BitVM to verify rollup state without trusting any single party.
Circuits
Citrea and Clementine circuits run as RISC Zero guest programs and each cover a distinct part of the verification pipeline. Citrea proves L2 execution and light-client state, while Clementine provides the Header Chain, Work-Only, and Bridge circuits for Bitcoin header verification, compact work extraction, and bridge payout validation.
Batch Proof Circuit
The Batch Proof Circuit verifies that a sequence of L2 blocks was executed correctly, producing a valid state transition from the previous proven state.
Circuit Inputs:
initial_state_root
State root before batch execution
final_state_root
State root after batch execution (host-side reference; not consumed by the V3 guest input split)
sequencer_commitments
Commitments being proven
l2_blocks
L2 blocks grouped by commitment (as VecDeque<Vec<L2Block>>)
state_transition_witnesses
State and offchain witnesses per block (as VecDeque<Vec<(Witness, Witness)>>)
short_header_proofs
Bitcoin header proofs for verifying SetBlockInfo system transactions
previous_sequencer_commitment
Prior commitment (None if this is the first batch proof)
prev_hash_proof
Merkle proof for verifying the prev_hash of the first L2 block
cache_prune_l2_heights
L2 heights at which to prune witness cache (zkVM memory management)
last_l1_hash_witness
Witness for reading the last L1 hash from the Bitcoin light client contract
Circuit Execution:
Deserializes circuit input (split into two parts to manage zkVM memory)
Initializes the State Transition Function with storage runtime
Verifies L2 block chain continuity using
prev_hash_proofReplays L2 blocks against the initial state, applying transactions
Validates intermediate state roots at commitment boundaries
Accumulates state diffs across all processed blocks
Verifies short header proofs for system transactions (e.g.,
setBlockInfo)Commits output (state roots, state diff, commitment range) to the journal
Light Client Circuit
The Light Client Circuit verifies Bitcoin consensus and aggregates batch proofs into a single recursive proof. It maintains persistent state via a Jellyfish Merkle Tree, enabling flexible proof chaining even when proofs arrive out of order.
Circuit Inputs:
da_block_header
Bitcoin block header being processed
inclusion_proof
Merkle proof of transaction inclusion (wtxids, coinbase tx, merkle proof)
completeness_proof
Raw transactions matching relevant wtxid prefixes
previous_light_client_proof
Prior LCP proof bytes (None for the first proof)
witness
JMT state hints for verification
light_client_proof_method_id
Current circuit version identifier (256 bits)
Circuit Execution:
Verifies the previous light client proof (if exists) using the method ID and extracts its output
Validates the Bitcoin header chain against consensus rules (PoW, difficulty, timestamps, continuity)
Verifies transaction inclusion and completeness proofs against the block header
Inserts the current Bitcoin block hash into the JMT state
Processes relevant transactions from the block:
Chunks: Stored in JMT by wtxid for later aggregation
Complete proofs: Decompressed, verified against method ID, state transitions recorded
Aggregates: Chunks retrieved by wtxid, concatenated, decompressed, verified
Sequencer commitments: Stored in JMT by index (sender verified)
Method ID upgrades: 3-of-5 security council signatures verified, chain ID checked, new IDs recorded
Chains verified state transitions to advance L2 state root (processes in order by commitment index)
Computes and validates JMT state root transition
Outputs new LCP proof with updated L2 state, LCP state root, and DA state
Bitcoin Verification in the Light Client Circuit
The Light Client Circuit performs Bitcoin header-chain verification, ensuring that proofs are anchored to valid Bitcoin blocks:
Proof-of-Work Validation:
Verifies block hash is below the current difficulty target
Converts compact bits to full target representation
Accumulates total work across the header chain
Difficulty Adjustment:
Detects epoch boundaries (every 2016 blocks on mainnet)
Calculates new target based on actual vs expected time (14 days expected)
Enforces minimum and maximum target bounds
Header Continuity:
Validates previous block hash linkage
Ensures consecutive block heights
Checks timestamp validity against median time past
SegWit Commitment Verification:
Locates witness commitment in coinbase output (script starting with
0x6a24aa21a9ed)Computes wtxid merkle root from all block transactions
Validates commitment equals
sha256(sha256(wtxid_merkle_root || witness_reserved_value))Ensures witness data integrity for inscribed proofs
Header Chain Circuit
The Header Chain Circuit verifies Bitcoin block headers against Bitcoin consensus rules and commits an updated chain state. It supports recursive proof chaining: the first proof starts from a genesis ChainState, and subsequent proofs verify the previous Header Chain proof before applying more headers.
State is maintained in ChainState, which includes the current Bitcoin height, accumulated work, best block hash, difficulty state, previous 11 timestamps, and a Merkle Mountain Range (MMR) of verified block hashes. The MMR lets later circuits prove that a Bitcoin block hash belongs to the verified chain without replaying every header.
Circuit Inputs:
method_id
Header Chain circuit version identifier. It must match any prior proof's method ID.
prev_proof
Either GenesisBlock(ChainState) for the first proof or PrevProof(BlockHeaderCircuitOutput) for recursive chaining.
block_headers
Contiguous Bitcoin headers, ordered from the next expected block after the current chain state.
Circuit Execution:
Reads
HeaderChainCircuitInputfrom the zkVM host.Initializes state from either the genesis
ChainStateor a verified previousBlockHeaderCircuitOutput.If a previous proof is provided, checks that its
method_idmatches the current input and recursively verifies it withguest.verify.Initializes the active target from the current chain state and initializes a local accumulated-work value.
Iterates over every header in
block_headers. For each header:Advances the working block height by one.
Selects the target, expected
bits, and work increment for that height and network.Computes the Bitcoin block hash.
Checks chain continuity by requiring
prev_block_hashto equal the currentbest_block_hash.Validates the header
bitsfield against the expected network difficulty.Validates proof of work by checking the block hash against the selected target.
Enforces median-time-past by requiring the block timestamp to be greater than the median of the previous 11 timestamps.
Appends the verified block hash to the MMR.
Updates the working best block hash, accumulated work, timestamp ring buffer, epoch start time when applicable, and the previous-header timestamp used by Testnet4's minimum-difficulty rule.
After the last block of a non-regtest difficulty epoch, recomputes and stores the target bits to use for the next epoch.
After all provided headers have been processed, writes the final accumulated work back into
chain_state.total_work.Commits one
BlockHeaderCircuitOutputfor the whole batch.
Work-Only Circuit
The Work-Only Circuit verifies a Header Chain proof and extracts only the data needed by watchtowers and the Bridge Circuit: accumulated Bitcoin work and the genesis state hash. It makes watchtower challenges compact by converting the Header Chain proof's 256-bit total work into a 128-bit value that can be included alongside a compressed Groth16 proof.
Circuit Inputs:
header_chain_circuit_output
Public output of a Header Chain Circuit proof.
Circuit Execution:
Reads
WorkOnlyCircuitInputfrom the zkVM host.Uses the Header Chain method ID baked into the circuit for the compile-time
BITCOIN_NETWORK.Checks that
header_chain_circuit_output.method_idmatches the expected Header Chain method ID.Verifies the Header Chain proof with
env::verify.Reads
chain_state.total_workfrom the verified Header Chain output and converts it from a 256-bit value to a 128-bit value by keeping the lower 128 bits.Commits
WorkOnlyCircuitOutputcontaining the 128-bit work value and the Header Chaingenesis_state_hash.
Bridge Circuit
The Bridge Circuit verifies that an operator is entitled to reimburse a withdrawal from the bridge vault during the BitVM challenge flow. It combines the operator's Header Chain proof, watchtower challenge data, payout transaction SPV proof, and a Citrea Light Client proof. The light-client output provides the Citrea L2 state root, which is then used to verify Clementine bridge contract storage proofs for the withdrawal and deposit data.
The circuit's public output is a 32-byte journal hash. That hash commits to the payout block hash, the operator's latest verified Bitcoin block hash, which watchtowers sent valid challenges, and a deposit constant derived from deposit-specific bridge data.
Circuit Inputs:
kickoff_tx
Kickoff transaction for the reimbursement flow; it contains the reimbursement connector and connector outputs used by challenge, Assert, and Disprove paths.
all_tweaked_watchtower_pubkeys
Registered tweaked x-only public keys for watchtower challenge outputs.
watchtower_inputs
Watchtower challenge transactions, prevouts, witnesses, input indexes, watchtower indexes, and optional annex digests.
hcp
Operator's BlockHeaderCircuitOutput from the Header Chain Circuit.
payout_spv
SPV proof for the payout transaction, including the payout transaction, block inclusion proof, block header, and MMR inclusion proof.
payout_input_index
Input index in the payout transaction that should spend the user's verified withdrawal outpoint.
lcp
Citrea light-client proof journal bytes. The Bridge Circuit verifies the journal against the configured light-client image ID, then deserializes it as the light-client output.
sp
Storage proof input for Clementine bridge contract data on Citrea: withdrawal UTXO txid, withdrawal vout, and deposit move txid.
watchtower_challenge_connector_start_idx
Kickoff output index where watchtower challenge connector outputs begin.
Circuit Execution:
Reads
BridgeCircuitInputfrom the zkVM host.Checks that
hcp.method_idmatches the expected Header Chain method ID for the compile-timeBITCOIN_NETWORK.Verifies the operator's Header Chain proof with
guest.verify.Verifies each watchtower challenge input:
The challenge input must spend the kickoff output at
watchtower_challenge_connector_start_idx + watchtower_idx * 2.The spent kickoff output must be P2TR.
The output x-only key must match the registered watchtower key.
The Taproot key-path Schnorr signature must verify under BIP341 sighash rules.
Records a 160-bit bitmap of watchtowers that sent valid challenge transactions.
Parses each valid watchtower challenge commitment in one of two forms:
A first
OP_RETURNoutput containing 128 bytes of compressed Groth16 proof plus 16 bytes of total work.First and second P2TR outputs containing the first 64 proof bytes, followed by a third
OP_RETURNoutput containing the remaining 64 proof bytes plus 16 bytes of total work.
Sorts valid watchtower commitments by claimed total work in descending order.
Verifies compressed Groth16 Work-Only proofs until the first valid proof is found. This produces
max_total_work.Compares the operator's Header Chain total work against
max_total_work; the operator must have at least as much verified work as the strongest valid watchtower challenge.Verifies the payout SPV proof:
Computes the payout transaction mid-state txid.
Reconstructs the Bitcoin Merkle root using the mid-state inclusion proof.
Checks the reconstructed Merkle root against the
merkle_rootfield ofpayout_spv.block_header.Checks the payout block hash against the Header Chain MMR inclusion proof.
Verifies the Citrea light-client journal against the network light-client image ID.
Deserializes the light-client output and checks its method ID against the configured image ID.
Requires the light-client latest DA block hash to equal the payout SPV block hash.
Verifies Clementine contract storage proofs against the light-client L2 state root:
withdrawal outpoint txid,
withdrawal vout,
deposit move txid.
Requires the payout transaction input at
payout_input_indexto spend the verified withdrawal outpoint and vout.Reads the operator x-only public key from the first
OP_RETURNoutput of the payout transaction.Computes
deposit_constantfrom the operator key, watchtower keys, watchtower connector start index, move txid, kickoff round outpoint, and Header Chain genesis state hash.Truncates the payout block hash and latest Header Chain block hash to their final 20 bytes.
Computes the final journal hash:
inner = blake3(payout_tx_blockhash || latest_blockhash || challenge_sending_watchtowers)journal_hash = blake3(deposit_constant || inner)
Commits the 32-byte journal hash.
Circuit Relations
ZKVM and Proof Generation
RISC-0 zkVM
Citrea uses the RISC-0 zkVM as its primary proving backend. RISC-0 executes RISC-V programs and generates STARKs that are wrapped in Groth16 SNARKs for succinct verification by nodes.
Guest Programs (ELF Binaries):
batch-proof-bitcoin: Batch proof circuit for Bitcoin DAlight-client-proof-bitcoin: Light client circuit for Bitcoin DAheader-chain-guest: Clementine Header Chain circuitwork-only-guest: Clementine Work-Only circuitbridge-circuit-guest: Clementine Bridge Circuit
Proving Modes:
Skip: Skip proving entirelyExecute: Execute the zkVM without generating proofs (testing/development)ProveWithSampling: The prover runs the rollup verification logic in the zkVM and produces a zk proofProveWithSamplingWithFakeProofs(N): The prover runs the rollup verification logic in the zkVM and produces a zk/fake proof. If proof_sampling_number is 0, then we always produce real proofs Otherwise we prove with a probability of 1/proof_sampling_number
Proof Pipeline:

Code Commitments (Method IDs)
Each circuit version is identified by a method ID β a 256-bit hash derived from the compiled ELF binary. Method IDs serve as code commitments, ensuring proofs were generated with the expected circuit logic.
Method ID Security:
Initial method IDs hardcoded per network at genesis
Upgrades require 3-of-5 security council signatures (ECDSA over EIP-191 prefixed, keccak256-hashed message)
Activation L2 height must exceed previous method ID's activation height
Chain ID embedded in upgrade body prevents cross-network replay attacks
Method IDs stored as
Vec<(activation_l2_height, [u32; 8])>to support versioned circuits
Method ID upgrades are protected by the security council multisig. This prevents unauthorized circuit changes while enabling protocol upgrades when needed.
Commitment Partitioning
Before proving, the Batch Prover partitions pending commitments into provable chunks. This balances proof generation efficiency with Bitcoin transaction size constraints.
Pre-Partition Filtering:
Before partitioning, the prover filters commitments:
Unsynced filter: Excludes commitments whose L2 blocks haven't been synced yet
Index gap filter: Excludes commitments that lack a known previous commitment (ensures sequential proving)
Partition Boundary Conditions:
Spec Change
Fork/specification change between commitments triggers a new partition
State Diff Size
Compressed state diff exceeds MAX_TX_BODY_SIZE (397,000 bytes) triggers a new partition
Commitment Count
Exceeds max_commitments_per_proof limit triggers a new partition
One-by-One Mode
Each commitment becomes its own partition (optional mode)
Partitioning Flow:

Proof Formats on Bitcoin
Proofs are inscribed on Bitcoin using SegWit witness data. Due to Bitcoin's transaction size limits, large proofs are split into chunks and later aggregated.
Transaction Types
Complete
Full proof fitting in a single transaction
Aggregate
References chunk txids and wtxids for reconstruction
Chunk
Partial proof segment (stored by wtxid in LCP JMT state)
BatchProofMethodId
Circuit upgrade with 3-of-5 security council signatures
SequencerCommitment
L2 block range commitment (merkle root, index, end block number)
Aggregation Mechanism
When a proof exceeds Citrea's MAX_TX_BODY_SIZE (397,000 bytes):
Compression: Proof is serialized and compressed
Chunking: Compressed data split into segments fitting within
MAX_TX_BODY_SIZEInscription: Each chunk inscribed as
DataOnDa::Chunkin a separate transactionAggregation: Parent transaction (
DataOnDa::Aggregate) lists chunk wtxids for retrievalReconstruction: Light Client retrieves chunks by wtxid from JMT state, concatenates them
Decompression: Combined chunks decompressed to original proof
Verification: Proof verified against the batch proof method ID for the relevant L2 height
The chunking mechanism keeps individual transactions under MAX_TX_BODY_SIZE (397,000 bytes) while enabling arbitrarily large proofs. Chunks are stored in the Light Client's JMT state indexed by wtxid until the aggregate transaction arrives.
Verification Pipeline
On-Chain Flow (Bitcoin)

Full Node Verification
Full nodes verify proofs as they arrive on Bitcoin:
Fetch: Retrieve proof transaction from Bitcoin block
Decompress: Extract and decompress proof blob
Verify SNARK: Check Groth16 proof against method ID
Validate Output: Confirm the L1 hash is known, commitment hashes match, and the pre-state root matches ledger state
Update State: Apply state diff and advance proven tip
Light Client State Chaining
The Light Client uses state root chaining to connect batch proofs:
Key Properties:
Proofs can arrive out of order on Bitcoin
Proof ranges may overlap. A later proof can cover already-verified commitments while extending with newer ones
JMT stores verified state transitions indexed by commitment number
State root continuity ensures chain integrity
Chaining advances automatically when gaps are filled
Settlement via Clementine
The Light Client Prover's output enables trust-minimized settlement through Clementine:
LCP Proof Generation: Light Client produces recursive proof of current state
BitVM Verification: Bridge operators can be challenged using LCP proof
Dispute Resolution: Invalid claims disproven via proof verification in BitVM
Settlement: Valid withdrawals proceed once verification completes
The LCP combines Bitcoin consensus verification with batch proof aggregation into a single artifact. This enables Clementine to verify rollup state with Bitcoin-level security guarantees, without trusting the sequencer or any bridge operator.
Security Considerations
Proof Soundness
Computational Soundness: Groth16 proofs are computationally sound under standard cryptographic assumptions
Code Commitment: Method IDs bind proofs to specific circuit logic
Security Council: 3-of-5 multisig protects method ID upgrades
Liveness Guarantees
Proof Sampling:
ProveWithSamplingWithFakeProofsmode enables testing with occasional real proofs based on configurable probabilityRecovery Mode: Interrupted proving sessions tracked in ledger DB and recovered on restart
Parallel Proving: Multiple proofs generated concurrently via the parallel prover service
Partition Flexibility: Large commitment ranges automatically split to fit transaction limits
Bitcoin Anchoring
Immutable Ordering: After finality depth, sequencer commitments are treated as fixed; before that, L1 reorgs can reorder data
Data Availability: State diffs inscribed alongside proofs enable state reconstruction
Fork Resistance: Deep Bitcoin reorg required to reverse proven batches
Summary
Citrea's proof system achieves trust-minimized verification through a carefully designed architecture:
Proves L2 state transitions for sequencer commitments
Aggregates proofs and verifies Bitcoin header-chain rules
Verifies correct execution of L2 blocks
Recursive verification with Bitcoin header chain
Executes circuits and generates Groth16 proofs
Data availability layer enabling ZK verification of rollup state
Consumes LCP proofs for trust-minimized settlement
The two-tier architecture separates concerns: Batch Proofs handle high-throughput L2 verification, while Light Client Proofs provide succinct recursive verification for external consumers. Together, they enable Citrea to inherit Bitcoin's security while maintaining EVM compatibility and scalability.
Last updated
Was this helpful?