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.

Proof System Architecture

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 setBlockInfo system transactions

  • Records 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:

Field
Description

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:

  1. Deserializes circuit input (split into two parts to manage zkVM memory)

  2. Initializes the State Transition Function with storage runtime

  3. Verifies L2 block chain continuity using prev_hash_proof

  4. Replays L2 blocks against the initial state, applying transactions

  5. Validates intermediate state roots at commitment boundaries

  6. Accumulates state diffs across all processed blocks

  7. Verifies short header proofs for system transactions (e.g., setBlockInfo)

  8. Commits output (state roots, state diff, commitment range) to the journal

Batch Proof Output Structure

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:

Field
Description

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:

  1. Verifies the previous light client proof (if exists) using the method ID and extracts its output

  2. Validates the Bitcoin header chain against consensus rules (PoW, difficulty, timestamps, continuity)

  3. Verifies transaction inclusion and completeness proofs against the block header

  4. Inserts the current Bitcoin block hash into the JMT state

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

  6. Chains verified state transitions to advance L2 state root (processes in order by commitment index)

  7. Computes and validates JMT state root transition

  8. Outputs new LCP proof with updated L2 state, LCP state root, and DA state

Light Client Output Structure

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:

Field
Description

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:

  1. Reads HeaderChainCircuitInput from the zkVM host.

  2. Initializes state from either the genesis ChainState or a verified previous BlockHeaderCircuitOutput.

  3. If a previous proof is provided, checks that its method_id matches the current input and recursively verifies it with guest.verify.

  4. Initializes the active target from the current chain state and initializes a local accumulated-work value.

  5. 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_hash to equal the current best_block_hash.

    • Validates the header bits field 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.

  6. After all provided headers have been processed, writes the final accumulated work back into chain_state.total_work.

  7. Commits one BlockHeaderCircuitOutput for the whole batch.

Header Chain Output Structure

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:

Field
Description

header_chain_circuit_output

Public output of a Header Chain Circuit proof.

Circuit Execution:

  1. Reads WorkOnlyCircuitInput from the zkVM host.

  2. Uses the Header Chain method ID baked into the circuit for the compile-time BITCOIN_NETWORK.

  3. Checks that header_chain_circuit_output.method_id matches the expected Header Chain method ID.

  4. Verifies the Header Chain proof with env::verify.

  5. Reads chain_state.total_work from the verified Header Chain output and converts it from a 256-bit value to a 128-bit value by keeping the lower 128 bits.

  6. Commits WorkOnlyCircuitOutput containing the 128-bit work value and the Header Chain genesis_state_hash.

Work-Only Output Structure

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:

Field
Description

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:

  1. Reads BridgeCircuitInput from the zkVM host.

  2. Checks that hcp.method_id matches the expected Header Chain method ID for the compile-time BITCOIN_NETWORK.

  3. Verifies the operator's Header Chain proof with guest.verify.

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

  5. Records a 160-bit bitmap of watchtowers that sent valid challenge transactions.

  6. Parses each valid watchtower challenge commitment in one of two forms:

    • A first OP_RETURN output 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_RETURN output containing the remaining 64 proof bytes plus 16 bytes of total work.

  7. Sorts valid watchtower commitments by claimed total work in descending order.

  8. Verifies compressed Groth16 Work-Only proofs until the first valid proof is found. This produces max_total_work.

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

  10. 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_root field of payout_spv.block_header.

    • Checks the payout block hash against the Header Chain MMR inclusion proof.

  11. Verifies the Citrea light-client journal against the network light-client image ID.

  12. Deserializes the light-client output and checks its method ID against the configured image ID.

  13. Requires the light-client latest DA block hash to equal the payout SPV block hash.

  14. Verifies Clementine contract storage proofs against the light-client L2 state root:

    • withdrawal outpoint txid,

    • withdrawal vout,

    • deposit move txid.

  15. Requires the payout transaction input at payout_input_index to spend the verified withdrawal outpoint and vout.

  16. Reads the operator x-only public key from the first OP_RETURN output of the payout transaction.

  17. Computes deposit_constant from the operator key, watchtower keys, watchtower connector start index, move txid, kickoff round outpoint, and Header Chain genesis state hash.

  18. Truncates the payout block hash and latest Header Chain block hash to their final 20 bytes.

  19. Computes the final journal hash:

    • inner = blake3(payout_tx_blockhash || latest_blockhash || challenge_sending_watchtowers)

    • journal_hash = blake3(deposit_constant || inner)

  20. Commits the 32-byte journal hash.

Bridge Circuit Committed Output

Circuit Relations

Diagram showing the relationships between Citrea and Clementine circuits
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 DA

  • light-client-proof-bitcoin: Light client circuit for Bitcoin DA

  • header-chain-guest: Clementine Header Chain circuit

  • work-only-guest: Clementine Work-Only circuit

  • bridge-circuit-guest: Clementine Bridge Circuit

Proving Modes:

  • Skip: Skip proving entirely

  • Execute: Execute the zkVM without generating proofs (testing/development)

  • ProveWithSampling: The prover runs the rollup verification logic in the zkVM and produces a zk proof

  • ProveWithSamplingWithFakeProofs(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:

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

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:

Condition
Description

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:

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

Type
Description

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

  1. Compression: Proof is serialized and compressed

  2. Chunking: Compressed data split into segments fitting within MAX_TX_BODY_SIZE

  3. Inscription: Each chunk inscribed as DataOnDa::Chunk in a separate transaction

  4. Aggregation: Parent transaction (DataOnDa::Aggregate) lists chunk wtxids for retrieval

  5. Reconstruction: Light Client retrieves chunks by wtxid from JMT state, concatenates them

  6. Decompression: Combined chunks decompressed to original proof

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

On-Chain Verification Flow

Full Node Verification

Full nodes verify proofs as they arrive on Bitcoin:

  1. Fetch: Retrieve proof transaction from Bitcoin block

  2. Decompress: Extract and decompress proof blob

  3. Verify SNARK: Check Groth16 proof against method ID

  4. Validate Output: Confirm the L1 hash is known, commitment hashes match, and the pre-state root matches ledger state

  5. 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:

  1. LCP Proof Generation: Light Client produces recursive proof of current state

  2. BitVM Verification: Bridge operators can be challenged using LCP proof

  3. Dispute Resolution: Invalid claims disproven via proof verification in BitVM

  4. Settlement: Valid withdrawals proceed once verification completes

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: ProveWithSamplingWithFakeProofs mode enables testing with occasional real proofs based on configurable probability

  • Recovery 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:

Component
Role

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?