Documentation ¶
Overview ¶
Package plonk implements in-circuit PLONK verifier.
NB! The circuit allows verifying proofs of PLONK circuits of size up to 2**30 constraints.
Example (Emulated) ¶
Example of verifying recursively BW6-761 PLONK proof in BN254 PLONK circuit using field emulation
package main import ( "fmt" "math/big" "github.com/consensys/gnark-crypto/ecc" native_plonk "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/backend/witness" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/std/algebra" "github.com/consensys/gnark/std/algebra/emulated/sw_bw6761" "github.com/consensys/gnark/std/math/emulated" "github.com/consensys/gnark/std/recursion/plonk" "github.com/consensys/gnark/test/unsafekzg" ) // 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 generated 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(ccs) 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-377 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.BLS12_377.ScalarField(), ecc.BW6_761.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.BW6_761.ScalarField(), scs.NewBuilder, outerCircuit) if err != nil { panic("compile failed: " + err.Error()) } // NB! UNSAFE! Use MPC. srs, srsLagrange, err := unsafekzg.NewSRS(ccs) 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.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 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:
Index ¶
- func GetNativeProverOptions(outer, field *big.Int) backend.ProverOption
- func GetNativeVerifierOptions(outer, field *big.Int) backend.VerifierOption
- type BaseVerifyingKey
- func PlaceholderBaseVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) BaseVerifyingKey[FR, G1El, G2El]
- func ValueOfBaseVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](vk backend_plonk.VerifyingKey) (BaseVerifyingKey[FR, G1El, G2El], error)
- type CircuitVerifyingKey
- func PlaceholderCircuitVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT](ccs constraint.ConstraintSystem) CircuitVerifyingKey[FR, G1El]
- func ValueOfCircuitVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT](vk backend_plonk.VerifyingKey) (CircuitVerifyingKey[FR, G1El], error)
- type Proof
- func PlaceholderProof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) Proof[FR, G1El, G2El]
- func ValueOfProof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](proof backend_plonk.Proof) (Proof[FR, G1El, G2El], error)
- type Verifier
- func (v *Verifier[FR, G1El, G2El, GtEl]) AssertDifferentProofs(bvk BaseVerifyingKey[FR, G1El, G2El], cvks []CircuitVerifyingKey[FR, G1El], ...) error
- func (v *Verifier[FR, G1El, G2El, GtEl]) AssertProof(vk VerifyingKey[FR, G1El, G2El], proof Proof[FR, G1El, G2El], ...) error
- func (v *Verifier[FR, G1El, G2El, GtEl]) AssertSameProofs(vk VerifyingKey[FR, G1El, G2El], proofs []Proof[FR, G1El, G2El], ...) error
- func (v *Verifier[FR, G1El, G2El, GtEl]) PrepareVerification(vk VerifyingKey[FR, G1El, G2El], proof Proof[FR, G1El, G2El], ...) ([]kzg.Commitment[G1El], []kzg.OpeningProof[FR, G1El], []emulated.Element[FR], ...)
- func (v *Verifier[FR, G1El, G2El, GtEl]) SwitchVerificationKey(bvk BaseVerifyingKey[FR, G1El, G2El], idx frontend.Variable, ...) (VerifyingKey[FR, G1El, G2El], error)
- type VerifierOption
- type VerifyingKey
- func PlaceholderVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](ccs constraint.ConstraintSystem) VerifyingKey[FR, G1El, G2El]
- func ValueOfVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT](vk backend_plonk.VerifyingKey) (VerifyingKey[FR, G1El, G2El], error)
- type Witness
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 { 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 verification keys for the same-sized circuits.
type CircuitVerifyingKey ¶
type CircuitVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT] struct { // Size circuit Size frontend.Variable SizeInv emulated.Element[FR] Generator emulated.Element[FR] // 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[FR emulated.FieldParams, G1El algebra.G1ElementT](ccs constraint.ConstraintSystem) CircuitVerifyingKey[FR, G1El]
PlaceholderCircuitVerifyingKey returns the placeholder for the unique part of the verification key with same BaseVerifyingKey.
func ValueOfCircuitVerifyingKey ¶
func ValueOfCircuitVerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT](vk backend_plonk.VerifyingKey) (CircuitVerifyingKey[FR, 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[FR, G1El], switches []frontend.Variable, proofs []Proof[FR, G1El, G2El], witnesses []Witness[FR], opts ...VerifierOption) 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 arguments 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], opts ...VerifierOption) 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], opts ...VerifierOption) 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], opts ...VerifierOption) ([]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[FR, 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 VerifierOption ¶
type VerifierOption func(cfg *verifierCfg) error
VerifierOption allows to modify the behaviour of PLONK verifier.
func WithCompleteArithmetic ¶
func WithCompleteArithmetic() VerifierOption
WithCompleteArithmetic forces the usage of complete formulas for point addition and multi-scalar multiplication. The option is necessary when recursing simple inner circuits whose selector polynomials may have exceptional cases (zeros, equal to each other, inverses of each other).
Safe formulas are less efficient to use, so using this option has performance impact on the outer circuit size.
type VerifyingKey ¶
type VerifyingKey[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT] struct { BaseVerifyingKey[FR, G1El, G2El] CircuitVerifyingKey[FR, 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 ¶
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.