plonk

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Dec 19, 2023 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package plonk implements in-circuit PLONK verifier.

Example (Emulated)

Example of verifying recursively BW6-761 PLONK proof in BN254 PLONK circuit using field emulation

package main

import (
	"fmt"
	"math/big"

	native_plonk "github.com/airchains-network/gnark/backend/plonk"
	"github.com/airchains-network/gnark/backend/witness"
	"github.com/airchains-network/gnark/constraint"
	"github.com/airchains-network/gnark/frontend"
	"github.com/airchains-network/gnark/frontend/cs/scs"
	"github.com/airchains-network/gnark/std/algebra"
	"github.com/airchains-network/gnark/std/algebra/emulated/sw_bw6761"
	"github.com/airchains-network/gnark/std/math/emulated"
	"github.com/airchains-network/gnark/std/recursion/plonk"
	"github.com/airchains-network/gnark/test/unsafekzg"
	"github.com/consensys/gnark-crypto/ecc"
)

// 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 PLONK keys are generated on the fly, but
// in practice should be genrated once and using MPC.
func computeInnerProof(field, outer *big.Int) (constraint.ConstraintSystem, native_plonk.VerifyingKey, witness.Witness, native_plonk.Proof) {
	innerCcs, err := frontend.Compile(field, scs.NewBuilder, &InnerCircuitNative{})
	if err != nil {
		panic(err)
	}
	// NB! UNSAFE! Use MPC.
	srs, srsLagrange, err := unsafekzg.NewSRS(innerCcs)
	if err != nil {
		panic(err)
	}

	innerPK, innerVK, err := native_plonk.Setup(innerCcs, srs, srsLagrange)
	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 := native_plonk.Prove(innerCcs, innerPK, innerWitness, plonk.GetNativeProverOptions(outer, field))
	if err != nil {
		panic(err)
	}
	innerPubWitness, err := innerWitness.Public()
	if err != nil {
		panic(err)
	}
	err = native_plonk.Verify(innerProof, innerVK, innerPubWitness, plonk.GetNativeVerifierOptions(outer, field))
	if err != nil {
		panic(err)
	}
	return innerCcs, innerVK, innerPubWitness, innerProof
}

// OuterCircuit is the generic outer circuit which can verify PLONK 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        plonk.Proof[FR, G1El, G2El]
	VerifyingKey plonk.VerifyingKey[FR, G1El, G2El] `gnark:"-"` // constant verification key
	InnerWitness plonk.Witness[FR]                  `gnark:",public"`
}

func (c *OuterCircuit[FR, G1El, G2El, GtEl]) Define(api frontend.API) error {
	verifier, err := plonk.NewVerifier[FR, G1El, G2El, GtEl](api)
	if err != nil {
		return fmt.Errorf("new verifier: %w", err)
	}
	err = verifier.AssertProof(c.VerifyingKey, c.Proof, c.InnerWitness)
	return err
}

// Example of verifying recursively BW6-761 PLONK proof in BN254 PLONK circuit using field emulation
func main() {
	// compute the proof which we want to verify recursively
	innerCcs, innerVK, innerWitness, innerProof := computeInnerProof(ecc.BW6_761.ScalarField(), ecc.BN254.ScalarField())

	// initialize the witness elements
	circuitVk, err := plonk.ValueOfVerifyingKey[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine](innerVK)
	if err != nil {
		panic(err)
	}
	circuitWitness, err := plonk.ValueOfWitness[sw_bw6761.ScalarField](innerWitness)
	if err != nil {
		panic(err)
	}
	circuitProof, err := plonk.ValueOfProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine](innerProof)
	if err != nil {
		panic(err)
	}

	outerCircuit := &OuterCircuit[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]{
		InnerWitness: plonk.PlaceholderWitness[sw_bw6761.ScalarField](innerCcs),
		Proof:        plonk.PlaceholderProof[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine](innerCcs),
		VerifyingKey: circuitVk,
	}
	outerAssignment := &OuterCircuit[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]{
		InnerWitness: circuitWitness,
		Proof:        circuitProof,
	}
	// compile the outer circuit
	ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, outerCircuit)
	if err != nil {
		panic("compile failed: " + err.Error())
	}

	// NB! UNSAFE! Use MPC.
	srs, srsLagrange, err := unsafekzg.NewSRS(innerCcs)
	if err != nil {
		panic(err)
	}

	// create PLONK setup. NB! UNSAFE
	pk, vk, err := native_plonk.Setup(ccs, srs, srsLagrange) // 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 PLONK proof of verifying PLONK proof in-circuit
	outerProof, err := native_plonk.Prove(ccs, pk, secretWitness)
	if err != nil {
		panic("proving failed: " + err.Error())
	}

	// verify the PLONK proof
	err = native_plonk.Verify(outerProof, vk, publicWitness)
	if err != nil {
		panic("circuit verification failed: " + err.Error())
	}
}
Output:

Example (Native)

Example of verifying recursively BLS12-371 PLONK proof in BW6-761 PLONK circuit using field emulation

// compute the proof which we want to verify recursively
innerCcs, innerVK, innerWitness, innerProof := computeInnerProof(ecc.BW6_761.ScalarField(), ecc.BN254.ScalarField())

// initialize the witness elements
circuitVk, err := plonk.ValueOfVerifyingKey[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine](innerVK)
if err != nil {
	panic(err)
}
circuitWitness, err := plonk.ValueOfWitness[sw_bls12377.ScalarField](innerWitness)
if err != nil {
	panic(err)
}
circuitProof, err := plonk.ValueOfProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine](innerProof)
if err != nil {
	panic(err)
}

outerCircuit := &OuterCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{
	InnerWitness: plonk.PlaceholderWitness[sw_bls12377.ScalarField](innerCcs),
	Proof:        plonk.PlaceholderProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine](innerCcs),
	VerifyingKey: circuitVk,
}
outerAssignment := &OuterCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{
	InnerWitness: circuitWitness,
	Proof:        circuitProof,
}
// compile the outer circuit
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, outerCircuit)
if err != nil {
	panic("compile failed: " + err.Error())
}

// NB! UNSAFE! Use MPC.
srs, srsLagrange, err := unsafekzg.NewSRS(innerCcs)
if err != nil {
	panic(err)
}

// create PLONK setup. NB! UNSAFE
pk, vk, err := native_plonk.Setup(ccs, srs, srsLagrange) // 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 PLONK proof of verifying PLONK proof in-circuit
outerProof, err := native_plonk.Prove(ccs, pk, secretWitness)
if err != nil {
	panic("proving failed: " + err.Error())
}

// verify the Groth16 proof
err = native_plonk.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

func GetNativeProverOptions(outer, field *big.Int) backend.ProverOption

GetNativeProverOptions returns PLONK prover options for the native prover to initialize the configuration suitable for in-circuit verification.

func GetNativeVerifierOptions

func GetNativeVerifierOptions(outer, field *big.Int) backend.VerifierOption

GetNativeVerifierOptions returns PLONK verifier options to initialize the configuration to be compatible with in-circuit verification.

Types

type BaseVerifyingKey

type BaseVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct {
	// Size circuit
	Size              uint64
	SizeInv           emulated.Element[FR]
	Generator         emulated.Element[FR]
	NbPublicVariables uint64

	// Commitment scheme that is used for an instantiation of PLONK
	Kzg kzg.VerifyingKey[G1El, G2El]

	// cosetShift generator of the coset on the small domain
	CosetShift emulated.Element[FR]
}

BaseVerifyingKey is the common part of the verification key for the circuits with same size, same number of public inputs and same number of commitments. Use PlaceholderBaseVerifyingKey for creating a placeholder for compiling and ValueOfBaseVerifyingKey for witness assignment.

func PlaceholderBaseVerifyingKey

func PlaceholderBaseVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) BaseVerifyingKey[FR, G1El, G2El]

PlaceholderBaseVerifyingKey returns placeholder of the base verification key common to circuits with same size, same number of public inputs and same number of commitments.

func ValueOfBaseVerifyingKey

func ValueOfBaseVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](vk backend_plonk.VerifyingKey) (BaseVerifyingKey[FR, G1El, G2El], error)

ValueOfBaseVerifyingKey assigns the base verification key from the witness. Use one of the verifiaction keys for the same-sized circuits.

type CircuitVerifyingKey

type CircuitVerifyingKey[G1El algebra.G1ElementT] struct {
	// S commitments to S1, S2, S3
	S [3]kzg.Commitment[G1El]

	// Commitments to ql, qr, qm, qo, qcp prepended with as many zeroes (ones for l) as there are public inputs.
	// In particular Qk is not complete.
	Ql, Qr, Qm, Qo, Qk kzg.Commitment[G1El]

	Qcp []kzg.Commitment[G1El]

	CommitmentConstraintIndexes []frontend.Variable
}

CircuitVerifyingKey is the unique part of the verification key for the circuits with same BaseVerifyingKey. Use PlaceholderCircuitVerifyingKey for creating a placeholder for compiling the circuit or ValueOfCircuitVerifyingKey for witness assignment.

func PlaceholderCircuitVerifyingKey

func PlaceholderCircuitVerifyingKey[G1El algebra.G1ElementT](ccs constraint.ConstraintSystem) CircuitVerifyingKey[G1El]

PlaceholderCircuitVerifyingKey returns the placeholder for the unique part of the verification key with same BaseVerifyingKey.

func ValueOfCircuitVerifyingKey

func ValueOfCircuitVerifyingKey[G1El algebra.G1ElementT](vk backend_plonk.VerifyingKey) (CircuitVerifyingKey[G1El], error)

ValueOfCircuitVerifyingKey returns the witness for the unique part of the verification key. Returns an error if there is a mismatch between type arguments and given witness.

type Proof

type Proof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct {

	// Commitments to the solution vectors
	LRO [3]kzg.Commitment[G1El]

	// Commitment to Z, the permutation polynomial
	Z kzg.Commitment[G1El]

	// Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial
	H [3]kzg.Commitment[G1El]

	Bsb22Commitments []kzg.Commitment[G1El]

	// Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime
	BatchedProof kzg.BatchOpeningProof[FR, G1El]

	// Opening proof of Z at zeta*mu
	ZShiftedOpening kzg.OpeningProof[FR, G1El]
}

Proof is a typed PLONK proof of SNARK. Use ValueOfProof to initialize the witness from the native proof. Use PlaceholderProof to initialize the placeholder witness for compiling the circuit.

func PlaceholderProof

func PlaceholderProof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) Proof[FR, G1El, G2El]

PlaceholderProof returns a placeholder proof witness to be use for compiling the outer circuit for witness alignment. For actual witness assignment use ValueOfProof.

func ValueOfProof

func ValueOfProof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](proof backend_plonk.Proof) (Proof[FR, 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 PLONK 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.

func (*Verifier[FR, G1El, G2El, GtEl]) AssertDifferentProofs

func (v *Verifier[FR, G1El, G2El, GtEl]) AssertDifferentProofs(bvk BaseVerifyingKey[FR, G1El, G2El], cvks []CircuitVerifyingKey[G1El],
	switches []frontend.Variable, proofs []Proof[FR, G1El, G2El], witnesses []Witness[FR]) error

AssertDifferentProofs asserts the validity of different proofs for different circuits. We define the base verification key bvk and per-circuit part in cvks. The selector which verification key to use ise given in slice switches. The proofs and witnesses are given in the argumens and must correspond to each other.

func (*Verifier[FR, G1El, G2El, GtEl]) AssertProof

func (v *Verifier[FR, G1El, G2El, GtEl]) AssertProof(vk VerifyingKey[FR, G1El, G2El], proof Proof[FR, G1El, G2El], witness Witness[FR]) error

AssertProof asserts that the SNARK proof holds for the given witness and verifying key.

func (*Verifier[FR, G1El, G2El, GtEl]) AssertSameProofs

func (v *Verifier[FR, G1El, G2El, GtEl]) AssertSameProofs(vk VerifyingKey[FR, G1El, G2El], proofs []Proof[FR, G1El, G2El], witnesses []Witness[FR]) error

AssertSameProofs asserts that multiple proofs for the same circuit are valid.

func (*Verifier[FR, G1El, G2El, GtEl]) PrepareVerification

func (v *Verifier[FR, G1El, G2El, GtEl]) PrepareVerification(vk VerifyingKey[FR, G1El, G2El], proof Proof[FR, G1El, G2El], witness Witness[FR]) ([]kzg.Commitment[G1El], []kzg.OpeningProof[FR, G1El], []emulated.Element[FR], error)

PrepareVerification returns a list of (openingProof, commitment, point), which are to be verified using kzg's BatchVerifyMultiPoints.

func (*Verifier[FR, G1El, G2El, GtEl]) SwitchVerificationKey

func (v *Verifier[FR, G1El, G2El, GtEl]) SwitchVerificationKey(bvk BaseVerifyingKey[FR, G1El, G2El], idx frontend.Variable, cvks []CircuitVerifyingKey[G1El]) (VerifyingKey[FR, G1El, G2El], error)

SwitchVerificationKey returns a verification key by the index idx using the base verification key bvk and circuit specific verification key cvks[idx].

type VerifyingKey

type VerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct {
	BaseVerifyingKey[FR, G1El, G2El]
	CircuitVerifyingKey[G1El]
}

VerifyingKey is a typed PLONK verification key. Use ValueOfVerifyingKey or PlaceholderVerifyingKey for initializing.

func PlaceholderVerifyingKey

func PlaceholderVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) VerifyingKey[FR, G1El, G2El]

PlaceholderVerifyingKey returns placeholder of the verification key for compiling the outer circuit.

func ValueOfVerifyingKey

func ValueOfVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](vk backend_plonk.VerifyingKey) (VerifyingKey[FR, G1El, G2El], error)

ValueOfVerifyingKey initializes witness from the given PLONK 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 []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