Documentation ¶
Overview ¶
Package groth16 provides in-circuit Groth16 verifier.
Example (Emulated) ¶
Example of verifying recursively BN254 Groth16 proof in BN254 Groth16 circuit using field emulation
package main import ( "fmt" "math/big" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/algebra" "github.com/consensys/gnark/std/algebra/emulated/sw_bn254" "github.com/consensys/gnark/std/math/emulated" stdgroth16 "github.com/consensys/gnark/std/recursion/groth16" ) // InnerCircuitNative is the definition of the inner circuit we want to // recursively verify inside an outer circuit. The circuit proves the knowledge // of a factorisation of a semiprime. type InnerCircuitNative struct { P, Q frontend.Variable N frontend.Variable `gnark:",public"` } func (c *InnerCircuitNative) Define(api frontend.API) error { // prove that P*Q == N res := api.Mul(c.P, c.Q) api.AssertIsEqual(res, c.N) // we must also enforce that P != 1 and Q != 1 api.AssertIsDifferent(c.P, 1) api.AssertIsDifferent(c.Q, 1) return nil } // computeInnerProof computes the proof for the inner circuit we want to verify // recursively. In this example the Groth16 keys are generated on the fly, but // in practice should be genrated once and using MPC. func computeInnerProof(field *big.Int) (constraint.ConstraintSystem, groth16.VerifyingKey, witness.Witness, groth16.Proof) { innerCcs, err := frontend.Compile(field, r1cs.NewBuilder, &InnerCircuitNative{}) if err != nil { panic(err) } // NB! UNSAFE! Use MPC. innerPK, innerVK, err := groth16.Setup(innerCcs) if err != nil { panic(err) } // inner proof innerAssignment := &InnerCircuitNative{ P: 3, Q: 5, N: 15, } innerWitness, err := frontend.NewWitness(innerAssignment, field) if err != nil { panic(err) } innerProof, err := groth16.Prove(innerCcs, innerPK, innerWitness) if err != nil { panic(err) } innerPubWitness, err := innerWitness.Public() if err != nil { panic(err) } err = groth16.Verify(innerProof, innerVK, innerPubWitness) if err != nil { panic(err) } return innerCcs, innerVK, innerPubWitness, innerProof } // OuterCircuit is the generic outer circuit which can verify Groth16 proofs // using field emulation or 2-chains of curves. type OuterCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { Proof stdgroth16.Proof[G1El, G2El] VerifyingKey stdgroth16.VerifyingKey[G1El, G2El, GtEl] InnerWitness stdgroth16.Witness[FR] } func (c *OuterCircuit[FR, G1El, G2El, GtEl]) Define(api frontend.API) error { curve, err := algebra.GetCurve[FR, G1El](api) if err != nil { return fmt.Errorf("new curve: %w", err) } pairing, err := algebra.GetPairing[G1El, G2El, GtEl](api) if err != nil { return fmt.Errorf("get pairing: %w", err) } verifier := stdgroth16.NewVerifier(curve, pairing) err = verifier.AssertProof(c.VerifyingKey, c.Proof, c.InnerWitness) return err } // Example of verifying recursively BN254 Groth16 proof in BN254 Groth16 circuit using field emulation func main() { // compute the proof which we want to verify recursively innerCcs, innerVK, innerWitness, innerProof := computeInnerProof(ecc.BN254.ScalarField()) // initialize the witness elements circuitVk, err := stdgroth16.ValueOfVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl](innerVK) if err != nil { panic(err) } circuitWitness, err := stdgroth16.ValueOfWitness[sw_bn254.ScalarField](innerWitness) if err != nil { panic(err) } circuitProof, err := stdgroth16.ValueOfProof[sw_bn254.G1Affine, sw_bn254.G2Affine](innerProof) if err != nil { panic(err) } outerAssignment := &OuterCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ InnerWitness: circuitWitness, Proof: circuitProof, VerifyingKey: circuitVk, } // the witness size depends on the number of public variables. We use the // compiled inner circuit to deduce the required size for the outer witness // using functions [stdgroth16.PlaceholderWitness] and // [stdgroth16.PlaceholderVerifyingKey] outerCircuit := &OuterCircuit[sw_bn254.ScalarField, sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl]{ InnerWitness: stdgroth16.PlaceholderWitness[sw_bn254.ScalarField](innerCcs), VerifyingKey: stdgroth16.PlaceholderVerifyingKey[sw_bn254.G1Affine, sw_bn254.G2Affine, sw_bn254.GTEl](innerCcs), } // compile the outer circuit ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, outerCircuit) if err != nil { panic("compile failed: " + err.Error()) } // create Groth16 setup. NB! UNSAFE pk, vk, err := groth16.Setup(ccs) // UNSAFE! Use MPC if err != nil { panic("setup failed: " + err.Error()) } // create prover witness from the assignment secretWitness, err := frontend.NewWitness(outerAssignment, ecc.BN254.ScalarField()) if err != nil { panic("secret witness failed: " + err.Error()) } // create public witness from the assignment publicWitness, err := secretWitness.Public() if err != nil { panic("public witness failed: " + err.Error()) } // construct the groth16 proof of verifying Groth16 proof in-circuit outerProof, err := groth16.Prove(ccs, pk, secretWitness) if err != nil { panic("proving failed: " + err.Error()) } // verify the Groth16 proof err = groth16.Verify(outerProof, vk, publicWitness) if err != nil { panic("circuit verification failed: " + err.Error()) } }
Output:
Example (Native) ¶
Example of verifying recursively BLS12-377 Groth16 proof in BW6-761 Groth16 circuit using chain of curves. It is significantly more efficient than using field emulation, but requires a specific chain of inner and outer curves.
// compute the proof which we want to verify recursively innerCcs, innerVK, innerWitness, innerProof := computeInnerProof(ecc.BLS12_377.ScalarField()) // initialize the witness elements circuitVk, err := stdgroth16.ValueOfVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](innerVK) if err != nil { panic(err) } circuitWitness, err := stdgroth16.ValueOfWitness[sw_bls12377.ScalarField](innerWitness) if err != nil { panic(err) } circuitProof, err := stdgroth16.ValueOfProof[sw_bls12377.G1Affine, sw_bls12377.G2Affine](innerProof) if err != nil { panic(err) } outerAssignment := &OuterCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ InnerWitness: circuitWitness, Proof: circuitProof, VerifyingKey: circuitVk, } // the witness size depends on the number of public variables. We use the // compiled inner circuit to deduce the required size for the outer witness // using functions [stdgroth16.PlaceholderWitness] and // [stdgroth16.PlaceholderVerifyingKey] outerCircuit := &OuterCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ InnerWitness: stdgroth16.PlaceholderWitness[sw_bls12377.ScalarField](innerCcs), VerifyingKey: stdgroth16.PlaceholderVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](innerCcs), } // compile the outer circuit. because we are using 2-chains then the outer // curve must correspond to the inner curve. For inner BLS12-377 the outer // curve is BW6-761. ccs, err := frontend.Compile(ecc.BW6_761.ScalarField(), r1cs.NewBuilder, outerCircuit) if err != nil { panic("compile failed: " + err.Error()) } // create Groth16 setup. NB! UNSAFE pk, vk, err := groth16.Setup(ccs) // UNSAFE! Use MPC if err != nil { panic("setup failed: " + err.Error()) } // create prover witness from the assignment secretWitness, err := frontend.NewWitness(outerAssignment, ecc.BW6_761.ScalarField()) if err != nil { panic("secret witness failed: " + err.Error()) } // create public witness from the assignment publicWitness, err := secretWitness.Public() if err != nil { panic("public witness failed: " + err.Error()) } // construct the groth16 proof of verifying Groth16 proof in-circuit outerProof, err := groth16.Prove(ccs, pk, secretWitness) if err != nil { panic("proving failed: " + err.Error()) } // verify the Groth16 proof err = groth16.Verify(outerProof, vk, publicWitness) if err != nil { panic("circuit verification failed: " + err.Error()) }
Output:
Index ¶
- type Proof
- type Verifier
- type VerifyingKey
- func PlaceholderVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](ccs constraint.ConstraintSystem) VerifyingKey[G1El, G2El, GtEl]
- func PlaceholderVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](ccs constraint.ConstraintSystem) VerifyingKey[G1El, G2El, GtEl]
- func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](vk groth16.VerifyingKey) (VerifyingKey[G1El, G2El, GtEl], error)
- func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](vk groth16.VerifyingKey) (VerifyingKey[G1El, G2El, GtEl], error)
- type Witness
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Proof ¶
type Proof[G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct { Ar, Krs G1El Bs G2El }
Proof is a typed Groth16 proof of SNARK. Use ValueOfProof to initialize the witness from the native proof.
func ValueOfProof ¶
func ValueOfProof[G1El algebra.G1ElementT, G2El algebra.G2ElementT](proof groth16.Proof) (Proof[G1El, G2El], error)
ValueOfProof returns the typed witness of the native proof. It returns an error if there is a mismatch between the type parameters and the provided native proof.
type Verifier ¶
type Verifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { // contains filtered or unexported fields }
Verifier verifies Groth16 proofs.
func NewVerifier ¶
func NewVerifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](curve algebra.Curve[FR, G1El], pairing algebra.Pairing[G1El, G2El, GtEl]) *Verifier[FR, G1El, G2El, GtEl]
NewVerifier returns a new Verifier instance using the curve and pairing interfaces. Use methods algebra.GetCurve and algebra.GetPairing to initialize the instances.
func (*Verifier[FR, G1El, G2El, GtEl]) AssertProof ¶
func (v *Verifier[FR, G1El, G2El, GtEl]) AssertProof(vk VerifyingKey[G1El, G2El, GtEl], proof Proof[G1El, G2El], witness Witness[FR]) error
AssertProof asserts that the SNARK proof holds for the given witness and verifying key.
type VerifyingKey ¶
type VerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { E GtEl G1 struct{ K []G1El } G2 struct{ GammaNeg, DeltaNeg G2El } }
VerifyingKey is a typed Groth16 verifying key for checking SNARK proofs. For witness creation use the method ValueOfVerifyingKey and for stub placeholder use PlaceholderVerifyingKey.
func PlaceholderVerifyingKey ¶
func PlaceholderVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](ccs constraint.ConstraintSystem) VerifyingKey[G1El, G2El, GtEl]
PlaceholderVerifyingKey returns an empty verifying key for a given compiled constraint system. The size of the verifying key depends on the number of public inputs and commitments used, this method allocates sufficient space regardless of the actual verifying key.
func PlaceholderVerifyingKeyFixed ¶
func PlaceholderVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](ccs constraint.ConstraintSystem) VerifyingKey[G1El, G2El, GtEl]
func ValueOfVerifyingKey ¶
func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](vk groth16.VerifyingKey) (VerifyingKey[G1El, G2El, GtEl], error)
ValueOfVerifyingKey initializes witness from the given Groth16 verifying key. It returns an error if there is a mismatch between the type parameters and the provided native verifying key.
func ValueOfVerifyingKeyFixed ¶
func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT](vk groth16.VerifyingKey) (VerifyingKey[G1El, G2El, GtEl], error)
ValueOfVerifyingKey initializes witness from the given Groth16 verifying key. It returns an error if there is a mismatch between the type parameters and the provided native verifying key.
type Witness ¶
type Witness[FR emulated.FieldParams] struct { // Public is the public inputs. The first element does not need to be one // wire and is added implicitly during verification. Public []emulated.Element[FR] }
Witness is a public witness to verify the SNARK proof against. For assigning witness use ValueOfWitness and to create stub witness for compiling use PlaceholderWitness.
func PlaceholderWitness ¶
func PlaceholderWitness[FR emulated.FieldParams](ccs constraint.ConstraintSystem) Witness[FR]
PlaceholderWitness creates a stub witness which can be used to allocate the variables in the circuit if the actual witness is not yet known. It takes into account the number of public inputs and number of used commitments.
func ValueOfWitness ¶
ValueOfWitness assigns a outer-circuit witness from the inner circuit witness. If there is a field mismatch then this method represents the witness inputs using field emulation. It returns an error if there is a mismatch between the type parameters and provided witness.