# Groth16 on BLS12-381

This page explains the specific zero-knowledge proof system TONado Cash uses, and why we made each choice.

## Groth16, very briefly

Groth16 is a zk-SNARK construction published by Jens Groth in 2016. It has two compelling properties:

* **Tiny proofs.** A Groth16 proof is 3 group elements, about 192 bytes for BLS12-381.
* **Fast verification.** Verifying a proof is 3 pairing operations plus a few group additions, regardless of how complex the underlying circuit is. On a chain with native pairing support, that's a small handful of opcodes.

The cost is:

* **A trusted setup per circuit.** Groth16 is not "universal"; each new circuit needs its own ceremony. We need two ceremonies: one for the withdraw circuit, one for the root-update circuit. See [The trusted setup](/how-it-works/trusted-setup.md).
* **No succinct recursion.** Groth16 proofs cannot be efficiently composed of other Groth16 proofs. We don't need this for TONado Cash.

For TONado Cash, the tradeoff is worth it. Verification cost matters because every withdrawal runs the verifier on-chain. Setup is a one-time investment.

## Why not PLONK / Halo2 / STARKs?

| System      | Pros                                                                      | Cons for TONado Cash                                |
| ----------- | ------------------------------------------------------------------------- | --------------------------------------------------- |
| **Groth16** | Smallest proofs, cheapest on-chain verification, mature tooling (snarkjs) | Per-circuit trusted setup                           |
| **PLONK**   | Universal setup (one ceremony for many circuits)                          | Larger proofs, more expensive verification          |
| **Halo2**   | No trusted setup                                                          | Much larger proofs, expensive verification          |
| **STARKs**  | No trusted setup, post-quantum                                            | Very large proofs (\~100KB), expensive verification |

On EVM, the size and verification cost difference makes Groth16 the clear winner for high-throughput verification, which is exactly why Tornado-EVM uses it.

On TON, the same logic applies. We additionally have a tool chain advantage: `export-ton-verifier` generates a Tolk verifier directly from a snarkjs Groth16-BLS12-381 zkey, so the path from circuit to deployment is well-trodden.

## Why BLS12-381 and not BN254

Tornado-EVM uses BN254 (also called `alt_bn128`). BN254 is what Ethereum's precompiles support, so it's the natural choice on EVM.

TON instead supports **BLS12-381** as a native pairing curve. TVM has dedicated opcodes (`BLS_G1_*`, `BLS_G2_*`, `BLS_PAIRING`) that implement BLS12-381 operations efficiently.

If we used BN254 on TON, we'd have to implement pairing in pure Tolk. That means simulating \~10,000 finite-field operations per pairing check, in software, well within a single transaction's gas budget. **It doesn't fit.**

So the curve choice is forced: use what TVM supports natively, and use it well. BLS12-381 it is.

BLS12-381 also has a slightly nicer security profile than BN254:

* **128-bit security level** (vs. \~110-bit for BN254 under the TNFS attack).
* **Better-understood**, used in production by Ethereum's beacon chain, Filecoin, Zcash Sapling, and others.

## Why Groth16 specifically over BLS12-381 (and not, say, PLONK over BLS12-381)

snarkjs has first-class BLS12-381 Groth16 support, including phase-1 ptau files (downloadable from public ceremonies) and phase-2 zkey generation. The `export-ton-verifier` toolchain consumes those outputs directly.

PLONK over BLS12-381 is possible in principle but doesn't have the same battle-tested tooling for TON. Adopting it would mean writing significant verifier code from scratch.

## The proof itself

A Groth16 proof is a tuple `(A, B, C)`:

* `A` is a point on the G1 group of BLS12-381, a pair of field elements, \~96 bytes serialized.
* `B` is a point on G2, \~192 bytes serialized.
* `C` is a point on G1, \~96 bytes serialized.

Plus 6 public inputs (`root`, `nullifierHash`, `recipient`, `relayer`, `fee`, `refund`), each a 256-bit field element.

Total on-chain message size for a withdrawal: a few hundred bytes for the proof + public inputs, plus the standard message header.

## On-chain verification cost

The Tolk verifier computes:

```
e(A, B) == e(α, β) · e(vk_x, γ) · e(C, δ)
```

where `α, β, γ, δ` are constants baked into the verifier (the *verification key*), and `vk_x` is a linear combination of public inputs.

This is **3 pairings** plus some G1 additions. On TVM with native pairing opcodes, this costs a few hundred thousand gas per withdrawal, small enough that withdrawal fits comfortably in a single transaction.

For a deposit, the root-update verifier does the same operation against a different verification key. Two pairings per deposit-withdraw cycle.

## Proving cost

Proving is heavier. The prover runs snarkjs in WASM, processing \~30,000 R1CS constraints for the withdraw circuit:

* 1 Poseidon(2) for the commitment.
* 1 Poseidon(1) for the nullifier hash.
* 20 Poseidon(2) for Merkle inclusion (one per tree level).
* 20 bit-decomposition checks (one per path-index bit).
* 4 squarings to bind recipient/relayer/fee/refund.

Wall-clock proving time on a modern laptop: 5–15 seconds. This is **client-side**: the relayer never participates in proving. See [Withdrawing](/using-tonado-cash/withdraw-walkthrough.md).

## What goes wrong if any of this is broken

A break in Groth16's underlying cryptographic assumptions (either the **q-strong Diffie-Hellman** assumption or the **generic group model**) would let an attacker forge proofs without knowing any deposit's pre-image. The pool would pay attackers.

In practice, q-SDH on BLS12-381 has held up against years of cryptanalysis. The protocol team is not in a position to improve on that; we rely on the broader crypto community's continuous scrutiny.

A break in the trusted setup (i.e., one specific person knowing the "toxic waste") would let *that person* forge proofs. This is why the setup is run as a multi-party ceremony with `≥5` contributors: any one honest contributor preserves soundness. See [The trusted setup](/how-it-works/trusted-setup.md).

## What we have, exactly

* **Curve:** BLS12-381.
* **Proving system:** Groth16.
* **Prover:** snarkjs ≥ 0.7, called from Node.js (or from a browser via WASM).
* **On-chain verifier:** auto-generated by `npx export-ton-verifier` from the final zkey. The verifier source is [`apps/contracts/contracts/verifier.tolk`](https://github.com/tonadocash/monorepo/blob/main/apps/contracts/contracts/verifier.tolk) for the withdraw circuit and [`verifier_merkle_update.tolk`](https://github.com/tonadocash/monorepo/blob/main/apps/contracts/contracts/verifier_merkle_update.tolk) for the deposit root-update circuit.
* **Pairing opcodes:** TVM native `BLS_*` family.

The exact circuit signal layout and constraint counts are in [Circuit spec](/protocol-reference/circuit-spec.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tonadocash.com/how-it-works/groth16-on-bls12-381.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
