groth16

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2024 License: Apache-2.0 Imports: 34 Imported by: 0

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

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

func ValueOfWitness[FR emulated.FieldParams](w witness.Witness) (Witness[FR], error)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL