Gnark is a Go library for zero-knowledge proof circuits. This cheatsheet provides a quick reference for common operations and patterns.

Note: In the code examples, Var is used as an abbreviation for frontend.Variable.

Gnark cheatsheet PDF preview

Prefer a printable PDF version?

Find the PDF in the GitHub repository.

View PDF & Repo

Getting Started

Basic setup and usage of Gnark

Installing Gnark

go get github.com/consensys/gnark@latest

- frontend.Variable is abbreviated as Var - In-circuit code vs out-circuit code distinction is important

Define Circuit

import "github.com/consensys/gnark/frontend"
type Circuit struct {
    PreImage Var `gnark:",secret"`
    Hash     Var `gnark:"hash,public"`
}
func (c *Circuit) Define(
           api frontend.API) error {
    m, _ := mimc.NewMiMC(api)
    m.Write(c.PreImage)
    api.AssertIsEqual(c.Hash, m.Sum())
}

Basic circuit definition with secret and public inputs

Compile

var mimcCircuit Circuit
cur := ecc.BN254.ScalarField()
r1cs, err := frontend.Compile(
  cur, r1cs.NewBuilder, &mimcCircuit)
vals := &Circuit { Hash: "161...469", PreImage: 35 }
w, _ := frontend.NewWitness(vals, cur)
pubw, _ := w.Public()

Compiling the circuit and creating witnesses

Prove: Groth16

pk, vk, _ := groth16.Setup(cs)
proof, _ := groth16.Prove(cs, pk, w)
err := groth16.Verify(proof, vk, pubw)

Generate and verify a Groth16 proof

Prove: PlonK

srs, lag, _ := unsafekzg.NewSRS(cs)
pk, vk, _ := plonk.Setup(cs, srs, lag)
proof, _ := plonk.Prove(cs, pk, w)
err := plonk.Verify(proof, vk, pubw)

Generate and verify a PlonK proof

API

Core API functions for building circuits

Assertions

// fails if i1 != i2
AssertIsEqual(i1, i2 Var)
// fails if i1 == i2
AssertIsDifferent(i1, i2 Var)
// fails if v != 0 and v != 1
AssertIsBoolean(i1 Var)
// fails if v ∉ {0,1,2,3}
AssertIsCrumb(i1 Var)
// fails if v > bound.
AssertIsLessOrEqual(v Var, bound Var)

Common assertion functions in gnark

Arithmetics

// = i1 + i2 + ... in
Add(i1, i2 Var, in ...Var) Var
// a = a + (b * c)
MulAcc(a,b, c Var) Var
Neg(i1 Var) Var // -i. 
// = i1 - i2 - ... in
Sub(i1, i2 Var, in ...Var) Var
// = i1 * i2 * ... in
Mul(i1, i2 Var, in ...Var) Var
// i1 /i2. =0 if i1 = i2 = 0
DivUnchecked(i1, i2 Var) Var
Div(i1, i2 Var) Var // = i1 / i2
Inverse(i1 Var) Var // = 1 / i1

Arithmetic operations in gnark

Binary Operations

// unpacks to binary (lsb first)
ToBinary(i1 Var, n ...int) []Var
// packs b to element (lsb first)
FromBinary(b ...Var) Var
// following a and b must be 0 or 1
Xor(a, b Var) Var // a ^ b
Or(a, b Var) Var // a | b
And(a, b Var) Var // a & b

Binary operations in gnark

Flow Control

// performs a 2-bit lookup
Lookup2(b0,b1 Var,i0,i1,i2,i3 Var) Var
// if b is true, yields i1 else i2
Select(b Var, i1, i2 Var) Var
// returns 1 if a is zero, 0 otherwise
IsZero(i1 Var) Var
// 1 if i1>i2, 0 if i1=i2, -1 if i1<i2
Cmp(i1, i2 Var) Var

Flow control operations in gnark

Debug

Println(a ...Var) //like fmt.Println

Run the program with -tags=debug to display a more verbose stack trace

Standard Library

Common cryptographic primitives

MiMC Hash

import "github.com/consensys/gnark/std/hash/mimc"
fMimc, _ := mimc.NewMiMC()
fMimc.Write(circuit.Data)
h := fMimc.Sum()

MiMC hash implementation in gnark

EdDSA Signature

import t "github.com/consensys/gnark-crypto/ecc/twistededwards"
import te "github.com/consensys/gnark/std/algebra/native/twistededwards"
type Circuit struct {
    pub eddsa.PublicKey
    sig eddsa.Signature
    msg frontend.Variable
}
cur, _ := te.NewEdCurve(api, t.BN254)
eddsa.Verify(cur, c.sig, c.msg, c.pub, &fMimc)

EdDSA signature verification in gnark

Merkle Proof

import "github.com/consensys/gnark/std/accumulator/merkle"
type Circuit struct {
	M    merkle.MerkleProof
	Leaf frontend.Variable
}
c.M.VerifyProof(api, &hFunc, c.Leaf)

Merkle tree proof verification in gnark

Selector Package

Functions for selecting and manipulating arrays

Slice Operations

// out[i] = i ∈ [s, e) ? in[i] : 0
Slice(s, e Var, in []Var) []Var
// out[i] = rs ? (i ≥ p ? in[i] : 0)
//             : (i < p ? in[i] : 0)
Partition(p Var, rs bool, in []Var) []Var
// out[i] = i < sp ? sv : ev
stepMask(outlen int, sp, sv, ev Var) []Var

Slice operations in the selector package

Multiplexer Operations

// out = in[b[0]+b[1]*2+b[2]*4+...]
BinaryMux(selBits, in []Var) Var
// out = vs[i] if ks[i] == qkey
Map(qkey Var, ks, vs []Var) Var
// out = in[sel]
Mux(sel Var, in ...Var) Var
// out[i] = ks[i] == k ? 1 : 0
KeyDecoder(k Var, ks []Var) []Var
// out[i] = i == s ? 1 : 0
Decoder(n int, sel Var) []Var
// out = a1*b1 + a2*b2 + ...
dotProduct(a, b []Var) Var

Multiplexer operations in the selector package

Serialization

Serializing and deserializing circuits and witnesses

Constraint System

// Serialize
var buf bytes.Buffer
cs.WriteTo(&buf)

// Deserialize
cs := groth16.NewCS(ecc.BN254)
cs.ReadFrom(&buf)

Serialize and deserialize a constraint system

Witness

// Serialize
w, _ := frontend.NewWitness(&assignment, ecc.BN254)
data, _ := w.MarshalBinary()
json, _ := w.MarshalJSON()

// Deserialize
w, _ := witness.New(ecc.BN254)
err := w.UnmarshalBinary(data)
w, _ := witness.New(ecc.BN254, ccs.GetSchema())
err := w.UnmarshalJSON(json)
pubw, _ := witness.Public()

Serialize and deserialize a witness

Smart Contract Integration

Exporting proofs and verifiers to Solidity

Export to Solidity

f, _ := os.Create("verifier.sol")
err = vk.ExportSolidity(f)

Export a verifier key to Solidity

Export Plonk Proof

_p, _ := proof.(interface{MarshalSolidity() []byte})
str := "0x" + hex.EncodeToString(
  _p.MarshalSolidity())

Export a PlonK proof for use in Solidity

Export Groth16 Proof

buf := bytes.Buffer{}
_, err := proof.WriteRawTo(&buf)
b := buf.Bytes()
var p [8]string
for i := 0; i < 8; i++ {
  p[i] = new(big.Int).SetBytes(
    b[32*i : 32*(i+1)]).String()
}
str := "["+strings.Join(p[:],",")+"]"

Export a Groth16 proof for use in Solidity

External Library Usage

Using gnark-crypto outside of circuits

MiMC Hash

import "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc"
fMimc := mimc.NewMiMC()
fMimc.Write(buf)
h := fMimc.Sum(nil)

Using MiMC hash outside of circuits

EdDSA Signature

import "math/rand"
import t "github.com/consensys/gnark-crypto/ecc/twistededwards"
import "github.com/consensys/gnark-crypto/hash"
curve := t.BN254
ht := hash.MIMC_BN254
seed := time.Now().Unix()
rnd := rand.New(rand.NewSource(seed))
s, _ := eddsa.New(curve, rnd)
sig, _ := s.Sign(msg, ht.New())
pk := s.Public()
v, _ := s.Verify(sig, msg, ht.New())
c.PublicKey.Assign(curve, pk.Bytes())
c.Signature.Assign(curve, sig)

Creating and verifying EdDSA signatures outside of circuits

Merkle Proof

import mt "github.com/consensys/gnark-crypto/accumulator/merkletree"
depth := 5
num := uint64(2 << (depth - 1))
seg := 32
mod := ecc.BN254.ScalarField()
// Create tree by random data
mlen := len(mod.Bytes())
var buf bytes.Buffer
for i := 0; i < int(num); i++ {
  leaf, _:= rand.Int(rand.Reader, mod)
  b := leaf.Bytes()
  buf.Write(make([]byte, mlen-len(b)))
  buf.Write(b)
}
// build merkle tree proof and verify
hGo := hash.MIMC_BN254.New()
idx := uint64(1)
root, path, _, _ := mt.BuildReaderProof(&buf, hGo, seg, idx)
verified := mt.VerifyProof(hGo, root, path, idx, num)
c.Leaf = idx
c.M.RootHash = root
c.M.Path = make([]Var, depth+1)
for i := 0; i < depth+1; i++ {
  c.M.Path[i] = path[i]
}

Creating and verifying Merkle proofs outside of circuits

Concepts

Important concepts and terminology

Glossary

cs: constraint system
w: (full) witness
pubw: public witness
pk: proving key
vk: verifying key
r1cs: rank-1 constraint system
srs: structured reference string

Common terminology in zero-knowledge proofs

Schemas

Groth16: L·R = O

PlonK: qₗᵢaᵢ + qᵣᵢbᵢ + qₒᵢcᵢ + qₘᵢaᵢbᵢ + qcᵢ = 0

SAP(Polymath): x·y = (x/2 + y/2)² - (x/2 - y/2

Mathematical representations of different proof systems

Resources

- https://docs.gnark.consensys.io/
- https://play.gnark.io/
- https://zkshanghai.xyz/

Useful resources for learning more about gnark

Recursive Proof Schemes

Comparison of different recursive proof schemes with their performance characteristics.

Comparison is taken in MacBook Pro 13" M1 Pro with original test case in gnark repository.

Inner Scheme Outer Scheme Inner Curve Outer Curve Constraints Setup Size (MB) Pk Size (MB) Constraint Size (MB) E2E Test Time (s)
GROTH16 GROTH16 BN254 BN254 1,654,099 520.69 437.60 83.08 234.08
GROTH16 GROTH16 BW6 BN254 4,962,866 1613.27 1381.87 231.39 704.40
GROTH16 GROTH16 BLS12 BW6 19,344 16.36 9.23 7.13 10.06
PLONKwoCommit PLONK BLS12 BW6 249,260 52.76 48.14 4.62 79.68
PLONKwoCommit PLONK BLS12381 BN254 10,258,175 1255.72 1024.03 231.68 1025.29
PLONKwoCommit PLONK BW6 BN254 26,821,707 2655.37 2048.03 607.34 3304.70
PLONKwCommit PLONK BLS12 BW6 300,262 101.88 96.14 5.74 134.75
PLONKwCommit PLONK BLS12381 BN254 10,770,107 1267.43 1024.03 243.39 742.53
PLONKwCommit PLONK BN254 BN254 7,973,944 691.78 512.03 179.75 320.40
PLONKwCommit PLONK BW6 BN254 28,206,610 2686.40 2048.03 638.37 4147.34