Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
333 changes: 333 additions & 0 deletions contract-dev/zero-knowledge.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
---
title: "Zero-knowledge proofs on TON"
sidebarTitle: "Zero-knowledge proofs"
---

import { Aside } from '/snippets/aside.jsx';

## Introduction

This guide shows how to create, compile, and test a simple Circom scheme and verify a **ZK-proof** in the **TON** blockchain using the **zk-SNARK Groth16** protocol. This approach is a starting point for more complex cases and integrations. The examples are based on a minimal multiplication scheme.

Read more about Circom and zero-knowledge proofs in the [Circom documentation](https://docs.circom.io/).

<Aside>
This guide is also applicable to circuits written in the [Noname](https://github.com/zksecurity/noname) language, since the `export-ton-verifier` library integrates with `snarkjs`, which in turn integrates with the Noname language.

Examples can be found in the [zk-ton-examples](https://github.com/zk-examples/zk-ton-examples/) repository.
</Aside>

## What this guide covers

- Basic workflow with zero-knowledge proofs in TON.
- Setting up the environment for ZK in TON.
- Creating and compiling the Circom scheme.
- Performing a simplified trusted setup (Groth16).
- Exporting the verifier for FunC, Tolk, and Tact.
- Local and on-chain proof verification.

## Prerequisites

- **Node.js** and **npm** installed
- **circom** and **snarkjs** installed
- Basic TON knowledge and Blueprint tool

## Project setup

1. Create a new project using Blueprint:

```bash
npm create ton@latest ZkSimple
cd ZkSimple
```

2. Install libraries for working with ZK proofs:

```bash
npm install snarkjs @types/snarkjs
```

3. Install the verifier export utility for TON:

```bash
npm install export-ton-verifier@latest
```

This utility exports verifier contracts for FunC, Tolk, and Tact.

## Create the Circom circuit

Create the directory `circuits/Multiplier` and the file `Multiplier.circom`:

```bash
mkdir -p circuits/Multiplier
cd circuits/Multiplier
```

```circom
pragma circom 2.2.2;

template Multiplier() {
signal input a;
signal input b;

signal output c;

c <== a*b;
}

component main = Multiplier();
```

This circuit proves knowledge of two numbers `a` and `b`, whose product is equal to the public output `c`, without revealing `a` and `b` themselves.

### Compile

Run in `circuits/Multiplier`:

```bash
circom Multiplier.circom --r1cs --wasm --sym --prime bls12381
```

After compilation, the following files will appear:

- `Multiplier.r1cs` — circuit constraints (R1CS)
- `Multiplier.sym` — symbolic signal map
- `Multiplier.wasm` — artifact for generating proof

Check constraints:

```bash
snarkjs r1cs info Multiplier.r1cs
```

Output example:

```
[INFO] snarkJS: Curve: bls12-381
[INFO] snarkJS: # of Wires: 4
[INFO] snarkJS: # of Constraints: 1
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Outputs: 1
```

<Aside>
`snarkjs` supports the **altbn-128** and **bls12-381** curves. Altbn-128 is available in Ethereum, but **bls12-381** is used for TON, so it is the one chosen in this guide.
</Aside>

## Trusted setup (Groth16)

The **trusted setup** is a one-time ceremony that generates the proving and verification keys for a circuit. It's called "trusted" because if the setup parameters are compromised, proofs could be forged. For production use, participate in a multi-party trusted setup ceremony. For local development and testing, a simplified single-party setup is sufficient.

For local tests, perform a simplified trusted setup ceremony. Parameter (`10`) affects execution time; its selection depends on the size of the scheme - the more constraints, the higher the parameter required.

```bash
# first phase
snarkjs powersoftau new bls12-381 10 pot10_0000.ptau -v
snarkjs powersoftau contribute pot10_0000.ptau pot10_0001.ptau --name="First contribution" -v -e="some random text"

# second phase (depends on the compiled scheme)
snarkjs powersoftau prepare phase2 pot10_0001.ptau pot10_final.ptau -v
snarkjs groth16 setup Multiplier.r1cs pot10_final.ptau Multiplier_0000.zkey
snarkjs zkey contribute Multiplier_0000.zkey Multiplier_final.zkey --name="1st Contributor" -v -e="some random text"

# export verification key
snarkjs zkey export verificationkey Multiplier_final.zkey verification_key.json
```

Clear up unnecessary artifacts:

```sh
rm pot10_0000.ptau pot10_0001.ptau pot10_final.ptau Multiplier_0000.zkey
```

## Export the verifier contract

```bash
# export FunC contract (default)
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.fc

# export Tolk contract
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.tolk --tolk

# export Tact contract
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.tact --tact
```

For FunC and Tolk, wrappers must be generated manually:

```bash
npx export-ton-verifier import-wrapper ./wrappers/Verifier.ts --force
```

This command generates a TypeScript wrapper file that provides type-safe methods to interact with the verifier contract.

## Testing and verification

In `tests/ZkSimple.spec.ts`:

```ts
import * as snarkjs from 'snarkjs';
import path from 'path';
import { dictFromInputList, groth16CompressProof } from 'export-ton-verifier';

// for Tact (After running `npx blueprint build --all`)
import { Verifier } from '../build/Verifier_tact/tact_Verifier';

// for FunC and Tolk
import { Verifier } from '../wrappers/Verifier';
```

Local verification:

```ts
const wasmPath = path.join(__dirname, '../circuits/Multiplier', 'Multiplier.wasm');
const zkeyPath = path.join(__dirname, '../circuits/Multiplier', 'Multiplier_final.zkey');
const verificationKey = require('../circuits/Multiplier/verification_key.json');

const input = { a: '342', b: '1245' };

const { proof, publicSignals } = await snarkjs.groth16.fullProve(input, wasmPath, zkeyPath);
const okLocal = await snarkjs.groth16.verify(verificationKey, publicSignals, proof);
```

On-chain verification:

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

// Quick check via get-method: verifies the proof locally without changing blockchain state.
expect(await verifier.getVerify({ pi_a, pi_b, pi_c, pubInputs })).toBe(true);

// Send the proof to the contract in a message
// The contract will run verification; handling the result/flow is up to the developer using this template.
await verifier.sendVerify(deployer.getSender(), { pi_a, pi_b, pi_c, pubInputs, value: toNano('0.15') });
```

<Aside type="tip">
Build the contracts before running tests. For Tact contracts, run `npx blueprint build --all` first. For FunC/Tolk, ensure the wrappers are generated.
</Aside>

## Other Languages

This tutorial follows the path **Circom → snarkjs → export-ton-verifier → TON**.
The same workflow applies to other stacks — the key requirement is to obtain a `proof` and a `verification key` in **snarkjs** format.

In the example repository — [zk-ton-examples](https://github.com/zk-examples/zk-ton-examples/) — there are already templates for **Noname**, **gnark**, and **arkworks**: proofs can be generated in any of these stacks, then converted into snarkjs format and verified both locally and on-chain in the same way.

<Aside>
Two utilities are available that help convert proofs and verification keys into a format compatible with **snarkjs**:

- [ark-snarkjs](https://github.com/mysteryon88/ark-snarkjs): use for exporting from **arkworks**
- [gnark-to-snarkjs](https://github.com/mysteryon88/gnark-to-snarkjs): use for exporting from **gnark**
</Aside>

The idea is always the same: generate `proof.json` and `verification_key.json` in **snarkjs** format, then use `export-ton-verifier` and perform verification in TON.

### Arkworks (Rust)

Use the **arkworks** library to generate the proof and verification key, then convert them into snarkjs format with **ark-snarkjs**.

1. Set up an Arkworks project:

```sh
cargo init
cargo add ark-bls12-381 ark-ff ark-groth16 ark-r1cs-std ark-relations ark-snark ark-snarkjs ark-std rand@0.8.5
```

<Aside>
The packages listed above are the core dependencies needed for most arkworks circuits. Depending on the specific circuit implementation, additional packages may be required.
</Aside>

2. Write the circuit in Rust. Implement the circuit logic using arkworks primitives, similar to how a Circom circuit would be written. Learn how to write constraints in arkworks by following the [arkworks R1CS tutorial](https://github.com/arkworks-rs/r1cs-tutorial). A working example of a simple multiplication circuit can be found in the [zk-ton-examples repository](https://github.com/zk-examples/zk-ton-examples/tree/main/circuits/Arkworks/MulCircuit).

2. Compile, generate proof, and perform trusted setup following the same workflow as in the Circom section above.

2. Export the proof and verification key to JSON using **ark-snarkjs**:

```rust
use ark_snarkjs::{export_proof, export_vk};
use ark_bls12_381::{Bls12_381, Fr};

let _ = export_proof::<Bls12_381, _>(&proof, &public_inputs, "json/proof.json");
let _ = export_vk::<Bls12_381, _>(
&params.vk,
public_inputs.len(),
"json/verification_key.json",
);
```

The directory and files will be created automatically.

5. Export the verifier contract:

```sh
# FunC contract
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.fc

# Tact contract
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.tact --tact

# Tolk contract
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.tolk --tolk
```

### Gnark (Go)

Use the **gnark** library to generate the proof and verification key, then convert them into snarkjs format with **gnark-to-snarkjs**.

1. Set up a Gnark project. You can find an example circuit in the [gnark repository](https://github.com/Consensys/gnark?tab=readme-ov-file#example). A working example of a cubic circuit can be found in the [zk-ton-examples repository](https://github.com/zk-examples/zk-ton-examples/tree/main/circuits/Cubic%20%28gnark%29).

1. Add **gnark-to-snarkjs** as a dependency:

```sh
go get github.com/mysteryon88/gnark-to-snarkjs@latest
```

3. Export the proof and verification key:

```go
{
proof_out, _ := os.Create("proof.json")
defer proof_out.Close()
_ = gnarktosnarkjs.ExportProof(proof, []string{"35"}, proof_out)
}
{
out, _ := os.Create("verification_key.json")
defer out.Close()
_ = gnarktosnarkjs.ExportVerifyingKey(vk, out)
}
```

4. Export the verifier contract:

```sh
# Tact contract
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.tact --tact

# FunC contract
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.fc

# Tolk contract
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.tolk --tolk
```

## Conclusion

This guide demonstrates a minimal example: **circuit → trusted setup → verifier export → verification in TON**.
This workflow can be extended to support more complex circuits and real-world applications.

## Useful Links

- Example repository: [zk-ton-examples](https://github.com/zk-examples/zk-ton-examples/)
- Verifier export library: [export-ton-verifier](https://github.com/mysteryon88/export-ton-verifier)
- Additional utilities:
- [ark-snarkjs](https://github.com/mysteryon88/ark-snarkjs)
- [gnark-to-snarkjs](https://github.com/mysteryon88/gnark-to-snarkjs)
- Using ZK proofs in Tact: [docs.tact](https://docs.tact-lang.org/cookbook/zk-proofs/)
- Circom: [docs.circom.io](https://docs.circom.io/)
- Noname: [zksecurity/noname](https://github.com/zksecurity/noname)
- Gnark: [Consensys/gnark](https://github.com/Consensys/gnark)
- Arkworks: [arkworks.rs](https://arkworks.rs/)
- SnarkJS: [iden3/snarkjs](https://github.com/iden3/snarkjs)
Loading