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