TON DocsTON Docs
Techniques

How to verify Groth16 examples on TON

Run Groth16 verifier examples from zk-examples/zk-ton-examples via Circom, Noname, Gnark, and Arkworks, then confirm the proof both locally and on-chain in TON.

For the underlying export flow, see Zero-knowledge proofs on TON.

Objective

The goal is run one of these upstream circuits on TON:

  • Multiplier from Circom
  • Sudoku from Noname
  • cubic-gnark from Gnark
  • MulCircuit from Arkworks

Prerequisites

Clone the examples repository

git clone https://github.com/zk-examples/zk-ton-examples.git
cd zk-ton-examples
npm install

Choose an example

ExampleStackCircuitWhat the proof exposes publiclyTolk test
MultiplierCircomcircuits/Multiplierc = a * bnpx blueprint test Verifier_multiplier_tolk
SudokuNonamecircuits/SudokuA partially filled gridnpx blueprint test Verifier_sudoku_tolk
cubic-gnarkGnarkcircuits/cubic-gnarky = x^3 + x + 5npx blueprint test Verifier_cubic_tolk
MulCircuitArkworkscircuits/Arkworks/MulCircuitz = x * ynpx blueprint test Verifier_ark_tolk

Run locally first

This page follows the upstream Tolk verifier tests and runs in the Blueprint sandbox by default, not on TON Mainnet. When adapting the examples to TON Testnet or TON Mainnet, start on TON Testnet with small amounts. The trusted-setup commands are for local tests; production circuits need a dedicated multi-party ceremony with fresh high-entropy contributions.

Run the Multiplier example

Select Multiplier for a minimal end-to-end Groth16 verifier flow with two private inputs and one public output. The proof is generated at test time from Multiplier_js/Multiplier.wasm and Multiplier_final.zkey.

cd circuits/Multiplier
circom Multiplier.circom --r1cs --wasm --sym --prime bls12381
PTAU_ENTROPY="$(openssl rand -hex 32)"
ZKEY_ENTROPY="$(openssl rand -hex 32)"
npx snarkjs powersoftau new bls12-381 10 pot10_0000.ptau -v
npx snarkjs powersoftau contribute pot10_0000.ptau pot10_0001.ptau --name="First contribution" -v -e="$PTAU_ENTROPY"
npx snarkjs powersoftau prepare phase2 pot10_0001.ptau pot10_final.ptau -v
npx snarkjs groth16 setup Multiplier.r1cs pot10_final.ptau Multiplier_0000.zkey
npx snarkjs zkey contribute Multiplier_0000.zkey Multiplier_final.zkey --name="1st Contributor" -v -e="$ZKEY_ENTROPY"
npx snarkjs zkey export verificationkey Multiplier_final.zkey verification_key.json
cd ../..
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.tolk --tolk --contract-name multiplierVerifier
npx blueprint test Verifier_multiplier_tolk

Expected output

PASS tests/Verifier_multiplier_tolk.spec.ts

The checked-in upstream Tolk snapshot records Verify = 88665 gas and code size 5829 bits / 11 cells.

Run the Sudoku example

Select Sudoku for a larger verification key and many public inputs. The Sudoku source checks rows, columns, and diagonals. It does not check 3x3 boxes (subgrids).

cd circuits/Sudoku
noname check
noname run --backend r1cs-bls12-381 --private-inputs "$SOLUTION_JSON" --public-inputs "$GRID_JSON"
PTAU_ENTROPY="$(openssl rand -hex 32)"
ZKEY_ENTROPY="$(openssl rand -hex 32)"
npx snarkjs powersoftau new bls12-381 14 pot14_0000.ptau -v
npx snarkjs powersoftau contribute pot14_0000.ptau pot14_0001.ptau --name="First contribution" -v -e="$PTAU_ENTROPY"
npx snarkjs powersoftau prepare phase2 pot14_0001.ptau pot14_final.ptau -v
npx snarkjs groth16 setup Sudoku.r1cs pot14_final.ptau Sudoku_0000.zkey
npx snarkjs zkey contribute Sudoku_0000.zkey Sudoku_final.zkey --name="1st Contributor" -v -e="$ZKEY_ENTROPY"
npx snarkjs zkey export verificationkey Sudoku_final.zkey verification_key.json
npx snarkjs groth16 prove Sudoku_final.zkey Sudoku.wtns proof.json public.json
cd ../..
npx export-ton-verifier ./circuits/Sudoku/Sudoku_final.zkey ./contracts/verifier_sudoku.tolk --tolk
npx blueprint test Verifier_sudoku_tolk

Define placeholders

  • SOLUTION_JSON — JSON object with the private solved grid in the format expected by circuits/Sudoku/src/main.no
  • GRID_JSON — JSON object with the public partially filled grid in the format expected by circuits/Sudoku/src/main.no

Expected output

PASS tests/Verifier_sudoku_tolk.spec.ts

The checked-in upstream Tolk snapshot records Verify = 857057 gas and code size 48111 bits / 79 cells.

Run the cubic-gnark example

Select cubic-gnark when the proving stack is Go. The upstream main.go proves the public relation y = x^3 + x + 5 and exports proof.json plus verification_key.json in snarkjs format.

cd circuits/cubic-gnark
go run main.go
cd ../..
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.tolk --tolk --contract-name Cubic
npx blueprint test Verifier_cubic_tolk

Expected output

PASS tests/Verifier_cubic_tolk.spec.ts

The checked-in upstream Tolk snapshot records Verify = 88665 gas and code size 5829 bits / 11 cells.

Run the MulCircuit example

Select MulCircuit when the proving stack is Rust. The upstream main.rs proves a multiplication circuit over Bls12_381 and exports json/proof.json plus json/verification_key.json.

cd circuits/Arkworks/MulCircuit
cargo run
cd ../../..
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.tolk --tolk --contract-name arkVerifier
npx blueprint test Verifier_ark_tolk

Expected output

PASS tests/Verifier_ark_tolk.spec.ts

The checked-in upstream Tolk snapshot records Verify = 88665 gas and code size 5829 bits / 11 cells.

Verify

Run-time verification succeeds when all the following are true:

  • snarkjs.groth16.verify(...) returns true
  • The unnamed Sudoku verifier exposes a getVerify() getter, while named verifiers expose a suffixed getter, such as getVerifyMultiplierVerifier() — both should return true
  • npx blueprint test ... exits with code 0

Not runnable. Partial snippet from the upstream Tolk tests:

const okLocal = await snarkjs.groth16.verify(verificationKey, publicSignals, proof);
expect(okLocal).toBe(true);

const { pi_a, pi_b, pi_c, pubInputs } = await groth16CompressProof(proof, publicSignals);

expect(await verifier.getVerify({ pi_a, pi_b, pi_c, pubInputs })).toBe(true);

const verifyResult = await verifier.sendVerify(deployer.getSender(), {
  pi_a,
  pi_b,
  pi_c,
  pubInputs,
  value: toNano('0.15'),
});

Troubleshoot

  • If npx blueprint test ... fails because the Tolk contract is missing, rerun the matching npx export-ton-verifier ... --tolk command for that example.
  • If snarkjs.groth16.verify(...) returns false, confirm that the proof, public signals, and verification key come from the same circuit build.
  • If export-ton-verifier rejects the input file, confirm that the example uses Groth16 over bls12-381.

See also

On this page