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, outer *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, stdgroth16.GetNativeProverOptions(outer, field)) if err != nil { panic(err) } innerPubWitness, err := innerWitness.Public() if err != nil { panic(err) } err = groth16.Verify(innerProof, innerVK, innerPubWitness, stdgroth16.GetNativeVerifierOptions(outer, field)) 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 { verifier, err := stdgroth16.NewVerifier[FR, G1El, G2El, GtEl](api) if err != nil { return fmt.Errorf("new verifier: %w", err) } return verifier.AssertProof(c.VerifyingKey, c.Proof, c.InnerWitness) } // 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(), 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(), ecc.BW6_761.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 ¶
- func GetNativeProverOptions(outer, field *big.Int) backend.ProverOption
- func GetNativeVerifierOptions(outer, field *big.Int) backend.VerifierOption
- type Proof
- type Verifier
- type VerifierOption
- type VerifyingKey
- func PlaceholderVerifyingKey[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 ¶
func GetNativeProverOptions ¶ added in v1.1.0
func GetNativeProverOptions(outer, field *big.Int) backend.ProverOption
GetNativeProverOptions returns Groth16 prover options for the native prover to initialize the configuration suitable for in-circuit verification.
func GetNativeVerifierOptions ¶ added in v1.1.0
func GetNativeVerifierOptions(outer, field *big.Int) backend.VerifierOption
GetNativeVerifierOptions returns Groth16 verifier options to initialize the configuration to be compatible with in-circuit verification.
Types ¶
type Proof ¶
type Proof[G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct { Ar, Krs G1El Bs G2El Commitments []pedersen.Commitment[G1El] CommitmentPok pedersen.KnowledgeProof[G1El] }
Proof is a typed Groth16 proof of SNARK. Use ValueOfProof to initialize the witness from the native proof.
func PlaceholderProof ¶ added in v1.1.0
func PlaceholderProof[G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) Proof[G1El, G2El]
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. For witness creation use the method ValueOfProof and for stub placeholder use PlaceholderProof.
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](api frontend.API) (*Verifier[FR, G1El, G2El, GtEl], error)
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], opts ...VerifierOption) error
AssertProof asserts that the SNARK proof holds for the given witness and verifying key.
type VerifierOption ¶ added in v1.1.0
type VerifierOption func(cfg *verifierCfg) error
VerifierOption allows to modify the behaviour of Groth16 verifier.
func WithCompleteArithmetic ¶ added in v1.1.0
func WithCompleteArithmetic() VerifierOption
WithCompleteArithmetic returns a VerifierOption that forces complete arithmetic.
func WithSubgroupCheck ¶ added in v1.1.0
func WithSubgroupCheck() VerifierOption
WithSubgroupCheck returns a VerifierOption that forces subgroup checks.
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 } CommitmentKey pedersen.VerifyingKey[G2El] PublicAndCommitmentCommitted [][]int }
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 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.