Compute proofs

How to read and verify a Groth16 receipt

Every proof-bearing kernel publishes the cryptographic receipt for its own result in the same file as its own code, in a public repository under a CC BY 4.0 license. This page explains what each field of a receipt means, reads one real receipt from the top, and shows four ways to verify one.

Where the receipts live

Every receipt sits in the public repository

Each receipt is a JSON object embedded in the published shard file that also carries the node's metadata, schema references, and conformance fixtures. The whole set lives in the public repository, one file per node, so a receipt and the logic it attests can be read side by side. For example, the receipt for the Basel 3.1 reporting delta node sits inside its node file, under the compute_proof key.

receiptFormat
groth16-bn254: the Groth16 proving scheme (Groth, EUROCRYPT 2016) instantiated over the BN254 elliptic curve. The same receipt carries a system: risc0 field naming the zkVM that produced it.
Why this format Groth16 proof size and verification cost stay small even for large computations, and BN254 verification is cheap enough for a smart contract to run.
imageId
A SHA-256 digest identifying the exact guest program binary the zkVM executed. This is the identity the seal is bound to: a receipt made by a different binary carries a different imageId.
Binding each imageId is also published in the node's compute_images list, and the coverage gate refuses a receipt whose imageId is missing from that list.
seal
The Groth16 proof itself, base64-encoded: a few hundred bytes of cryptographic material that verifies against RISC Zero's published verifying key. It is the compressed proof that a valid execution produced this journal.
Verified against the RISC Zero verifying key, which our in-browser verifier and the on-chain recipe both pin. Verification has two outcomes: the pairing check passes, or the receipt fails.
journal
The public output committed by that execution. It carries the digest of the kernel source that ran (kernel_digest) and the output payload itself, which the coverage gate requires to equal the node's published output byte for byte.
Checked a journal that commits an error fails the gate, since a valid seal over a failed run would prove only that the failure happened. The coverage gate has rejected that case structurally since August 2026, when it was observed once in practice.
The claim

What the seal establishes

How one receipt is madeFive stages left to right. The published kernel source and this node's inputs go into a zkVM guest, identified by its imageId. The guest run produces a STARK receipt of a journal and a seal. A Groth16 wrap over the BN254 curve compresses that into a constant 256-byte seal. Below, the journal carries the kernel digest and an output equal to the node's published output_payload. Proving runs offline; the live surfaces only verify. How one receipt is made kernel and inputsthe published kernelsource and this node's inputs zkVM guestimageId sha256:a1a0bc89one pinned guest program STARK receipta journal and a sealfrom that one execution Groth16 wrapbn254 curvecompresses the proof seal256 bytesverifies in milliseconds journalkernel_digest sha256:bb655f6fits committed output equals the node's published output_payload proving happens offlinethe browser tool, the Worker and CIonly ever verify a finished receipt
Each stage happens once, before anything is published. The journal and the seal are what land in the node file, and every check on this page works from those two values and the imageId. Section 18 of the specification sets the required fields; section 18.2 is the rule that proving stays off the live surfaces.

Verifying a receipt is a local cryptographic check: given the seal, the journal, and the imageId, anyone can confirm that the seal is a valid Groth16 proof, under the published verifying key, for an execution of that exact guest binary that halted normally and committed exactly this journal. Running the check requires no trust in us, in the server that served the page, or in the deployment pipeline. The published result provably came from running the published logic.

The check is also cheap: a browser verifies in milliseconds, and Ethereum has shipped native BN254 pairing checks since EIP-196 and EIP-197 went live in the Byzantium hard fork (October 2017), so a smart contract can verify too. Our on-chain verification recipe uses exactly that path, one read-only call against RISC Zero's deployed verifier contract.

Publishing every receipt in a versioned public repository puts them where tampering is visible, the same transparency pattern Certificate Transparency standardized for certificates (RFC 9162) and SLSA standardized for build provenance. The deploy side of that pattern, which binds the served bytes of this site to a named commit and workflow run, is described on the Methods page.

Worked example

One receipt read from the top

This is the receipt from the Basel 3.1 reporting delta node file, the same object a verifier consumes. The seal is shortened to a head and tail of the real value and the asset-class table is trimmed to one row; every other value is verbatim from the file.

{
  "type": "ZkVmReceipt",
  "system": "risc0",
  "receiptFormat": "groth16-bn254",
  "imageId": "sha256:a1a0bc89b5b1febaeda3519f6dbade0fa5ac16beeb143c4e1b01689573567bc6",
  "seal": "IHfBGtT0qGiU2bFEFgG3pWVb1Q94FzrdJfBcYBtr ... M7u/slkH989njTyjh/Q==",
  "journal": {
    "chaingraph_version": "0.4.0",
    "kernel_digest": "sha256:bb655f6f08ccd299198d1eff963940525ec31c073ef296bfce08503620cb28c7",
    "output": {
      "asset_class_summary": [
        {
          "id": "residential_mortgage",
          "label": "Residential Mortgage",
          "ead_bn": 100,
          "current_rwa_bn": 35,
          "basel31_rwa_bn": 20,
          "rwa_delta_bn": -15,
          "rwa_delta_pct": -42.86
        }
      ],
      "current_rwa_bn": 35,
      "basel31_rwa_bn": 20,
      "rwa_delta_bn": -15,
      "rwa_delta_pct": -42.86,
      "floor_rwa_bn": 25.375,
      "output_floor_binding": false,
      "cet1_ratio_current_pct": 15.5,
      "cet1_ratio_basel31_pct": 27.13,
      "capital_shortfall_bn": 0,
      "compliance_flags": ["OUTPUT_FLOOR_NOT_BINDING", "CET1_ADEQUATE"],
      "verdict": "MINIMAL_IMPACT"
    }
  }
}
  1. Name the scheme

    receiptFormat says which proving scheme and curve to expect: a Groth16 proof over BN254. system: risc0 says the RISC Zero zkVM produced it, so RISC Zero's published verifying key is the one that applies.

  2. Identify the program that ran

    imageId is the SHA-256 of the exact guest binary inside the zkVM. The same node file publishes that digest in its compute_images list under system: risc0 (valid from 2026-07-10), and the coverage gate fails any receipt whose imageId is absent from that list.

  3. The seal itself

    The seal is the proof, base64-encoded. Its bytes are material for the pairing check rather than anything a reader inspects by eye. Verification runs against RISC Zero's verifying key in a browser (Verification Desk) or through an Ethereum precompile (on-chain recipe).

  4. Bind the receipt to the source

    journal.kernel_digest is the hash of the kernel source that produced this output, sha256:bb655f6f.... The node publishes the same digest as its sha256-source image, so each receipt's journal carries the digest of the exact kernel it was proven over; scripts/check-s18-digest-freshness.mjs reports which live receipts predate their kernel's current bytes.

  5. Check what was committed

    journal.output is the output of that one execution: here, the Basel 3.1 output-floor recomputation over the node's recorded input, with the risk-weighted-assets delta (-15bn, -42.86%), the floor test (25.375bn floor, not binding), and the verdict MINIMAL_IMPACT. The coverage gate requires this object to equal the node's published output byte for byte, which is what makes this a receipt for the result on the page rather than for some other run of the same code.

Two questions sit outside any receipt. Whether the kernel's logic is a correct model of the Basel 3.1 output floor is answered by the specification and property evidence described on the mechanical verification page, and whether the bytes served today still match what this receipt attests is answered by the deploy manifest, because a receipt attests one execution in the past.

Verify one yourself

Four ways to check a receipt

How a receipt is checkedThree published values on the left, the imageId, the journal and the seal. The journal is serialized to canonical bytes, those bytes and the imageId form the risc0 receipt claim, and the seal is checked against the claim by a BN254 pairing check under the published verifying key. The result is a single pass or fail, computed in the browser with no network call. Below, two binding checks: the imageId appears in the node's compute_images list and the journal's output equals the node's published output_payload. How a receipt is checked imageId journal seal canonical journal bytesutf8(JCS(journal)) the risc0 receipt claimReceiptClaim::ok pairing checkon the BN254 curveunder RISC Zero'spublished verifying key verifiedone pass or fail resultin your browser with no network call two binding checks run beside the pairing checkthe imageId appears in the node's compute_images list and the journal's output equals the node's published output_payload
The reference verifier that runs this path is chaingraph/kernels/_computeproof.mjs, with the BN254 pairing math vendored into the file and no runtime dependency on the prover vendor. The Verification Desk runs exactly this in the browser.
In the browser no network
Open the Verification Desk and drop in any node's JSON file. Its compute-proof layer verifies the BN254 seal with a self-contained reference verifier vendored from the same code the suite ships, entirely in your browser.
Good for checking a receipt you have already downloaded, with zero setup and zero network calls.
On-chain one eth_call
The receipt verification recipe walks through checking a receipt against RISC Zero's deployed on-chain verifier with a single read-only eth_call, using any Ethereum RPC endpoint.
Good for a trust root outside our infrastructure entirely: the verifier contract belongs to RISC Zero.
From the repository CLI
Clone the repo and run node scripts/check-compute-proof-coverage.mjs. The gate replays the structural checks for every receipt in the estate and fails against its committed ratchet baseline if any regress.
Good for auditing the whole set at once, or the coverage gate's own behavior: the script is public, commented, and tested.
The normative text SPEC.md
Section 18 of the OpenChainGraph specification defines what a compute-integrity receipt must contain, where it lives, and which checks make one malformed. The spec, its version history, and every gate that enforces it are in the same public repository.
Good for checking the rule each gate enforces against its normative source.

Sources: Groth, On the Size of Pairing-based Non-interactive Arguments, EUROCRYPT 2016. EIP-196 and EIP-197, BN256 curve and pairing precompiles, live since October 2017. RISC Zero proof system documentation, dev.risczero.com, receipt model of journal plus seal, accessed September 2026. RFC 9162, Certificate Transparency Version 2.0, December 2021. All statements on this page are as of September 2026.