Gnark Cheatsheet
Circuit cheatsheet for gnark
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
.

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 |