To create a zk-SNARK circuit, you must:
- Define a struct that implements the
frontend.Circuit interface by providing a Define(api frontend.API) error method. - Use struct tags (e.g.,
gnark:"x" or gnark:",public") to define circuit variables. - Compile the circuit using
frontend.Compile. - Perform a setup (e.g.,
groth16.Setup) to generate proving and verification keys. - Create a witness using
frontend.NewWitness with an assignment struct. - Generate a proof using
groth16.Prove and verify it using groth16.Verify.
package main
import (
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
)
// CubicCircuit defines a simple circuit.
// x**3 + x + 5 == y
type CubicCircuit struct {
X frontend.Variable `gnark:"x"`
Y frontend.Variable `gnark:",public"`
}
// Define declares the circuit constraints.
func (circuit *CubicCircuit) Define(api frontend.API) error {
x3 := api.Mul(circuit.X, circuit.X, circuit.X)
api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5))
return nil
}
func main() {
var circuit CubicCircuit
ccs, _ := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit)
pk, vk, _ := groth16.Setup(ccs)
assignment := CubicCircuit{X: 3, Y: 35}
witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField())
publicWitness, _ := witness.Public()
proof, _ := groth16.Prove(ccs, pk, witness)
_ = groth16.Verify(proof, vk, publicWitness)
}