Browse Source

added plonk_benchmark

main
Kevin Jue 2 years ago
parent
commit
a0405cd207
3 changed files with 192 additions and 56 deletions
  1. +132
    -0
      plonk_benchmark.go
  2. +59
    -53
      plonky2_verifier/plonk.go
  3. +1
    -3
      plonky2_verifier/plonk_test.go

+ 132
- 0
plonk_benchmark.go

@ -0,0 +1,132 @@
package main
import (
"fmt"
. "gnark-ed25519/field"
. "gnark-ed25519/plonky2_verifier"
"os"
"time"
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
)
type BenchmarkPlonkCircuit struct {
proofChallenges ProofChallenges
openings OpeningSet
}
func (circuit *BenchmarkPlonkCircuit) Define(api frontend.API) error {
commonCircuitData := DeserializeCommonCircuitData("./plonky2_verifier/data/dummy_2^14_gates/common_circuit_data.json")
field := NewFieldAPI(api)
qeAPI := NewQuadraticExtensionAPI(field, commonCircuitData.DegreeBits)
plonkChip := NewPlonkChip(api, qeAPI, commonCircuitData)
plonkChip.Verify(circuit.proofChallenges, circuit.openings)
return nil
}
func compileCircuit() frontend.CompiledConstraintSystem {
fmt.Println("compiling circuit", time.Now())
circuit := BenchmarkPlonkCircuit{}
proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json")
// Challenge associated with the data from "/.data/dummy_2^14_gates/*"
proofChallenges := ProofChallenges{
PlonkBetas: []F{
NewFieldElementFromString("4678728155650926271"),
NewFieldElementFromString("13611962404289024887"),
},
PlonkGammas: []F{
NewFieldElementFromString("13237663823305715949"),
NewFieldElementFromString("15389314098328235145"),
},
PlonkAlphas: []F{
NewFieldElementFromString("14505919539124304197"),
NewFieldElementFromString("1695455639263736117"),
},
PlonkZeta: QuadraticExtension{
NewFieldElementFromString("14887793628029982930"),
NewFieldElementFromString("1136137158284059037"),
},
}
circuit.proofChallenges = proofChallenges
circuit.openings = proofWithPis.Proof.Openings
r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit)
if err != nil {
fmt.Println("error in building circuit", err)
os.Exit(1)
}
return r1cs
}
func createProof(r1cs frontend.CompiledConstraintSystem) groth16.Proof {
proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json")
// Challenge associated with the data from "/.data/dummy_2^14_gates/*"
proofChallenges := ProofChallenges{
PlonkBetas: []F{
NewFieldElementFromString("4678728155650926271"),
NewFieldElementFromString("13611962404289024887"),
},
PlonkGammas: []F{
NewFieldElementFromString("13237663823305715949"),
NewFieldElementFromString("15389314098328235145"),
},
PlonkAlphas: []F{
NewFieldElementFromString("14505919539124304197"),
NewFieldElementFromString("1695455639263736117"),
},
PlonkZeta: QuadraticExtension{
NewFieldElementFromString("14887793628029982930"),
NewFieldElementFromString("1136137158284059037"),
},
}
// Witness
assignment := &BenchmarkPlonkCircuit{
proofChallenges: proofChallenges,
openings: proofWithPis.Proof.Openings,
}
fmt.Println("Generating witness", time.Now())
witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField())
publicWitness, _ := witness.Public()
pk, vk, err := groth16.Setup(r1cs)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Creating proof", time.Now())
proof, err := groth16.Prove(r1cs, pk, witness)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Verifying proof", time.Now())
err = groth16.Verify(proof, vk, publicWitness)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return proof
}
func main() {
r1cs := compileCircuit()
fi, _ := os.Open("dummy_fri.r1cs")
r1cs.WriteTo(fi)
proof := createProof(r1cs)
fmt.Println(proof.CurveID(), time.Now())
}

+ 59
- 53
plonky2_verifier/plonk.go

@ -1,7 +1,6 @@
package plonky2_verifier package plonky2_verifier
import ( import (
"gnark-ed25519/field"
. "gnark-ed25519/field" . "gnark-ed25519/field"
"github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend"
@ -33,36 +32,34 @@ var QUOTIENT = PlonkOracle{
} }
type PlonkChip struct { type PlonkChip struct {
api frontend.API
qe *QuadraticExtensionAPI
api frontend.API
qeAPI *QuadraticExtensionAPI
commonData CommonCircuitData
proofChallenges ProofChallenges
openings OpeningSet
commonData CommonCircuitData
DEGREE F DEGREE F
DEGREE_BITS_F F DEGREE_BITS_F F
DEGREE_QE QuadraticExtension DEGREE_QE QuadraticExtension
} }
func NewPlonkChip(api frontend.API, qe *QuadraticExtensionAPI, commonData CommonCircuitData) *PlonkChip {
func NewPlonkChip(api frontend.API, qeAPI *QuadraticExtensionAPI, commonData CommonCircuitData) *PlonkChip {
// TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64? // TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64?
return &PlonkChip{ return &PlonkChip{
api: api,
qe: qe,
api: api,
qeAPI: qeAPI,
commonData: commonData, commonData: commonData,
DEGREE: NewFieldElement(1 << commonData.DegreeBits), DEGREE: NewFieldElement(1 << commonData.DegreeBits),
DEGREE_BITS_F: NewFieldElement(commonData.DegreeBits), DEGREE_BITS_F: NewFieldElement(commonData.DegreeBits),
DEGREE_QE: QuadraticExtension{NewFieldElement(1 << commonData.DegreeBits), field.ZERO_F},
DEGREE_QE: QuadraticExtension{NewFieldElement(1 << commonData.DegreeBits), ZERO_F},
} }
} }
func (p *PlonkChip) expPowerOf2Extension(x QuadraticExtension) QuadraticExtension { func (p *PlonkChip) expPowerOf2Extension(x QuadraticExtension) QuadraticExtension {
for i := uint64(0); i < p.commonData.DegreeBits; i++ { for i := uint64(0); i < p.commonData.DegreeBits; i++ {
x = p.qe.SquareExtension(x)
x = p.qeAPI.SquareExtension(x)
} }
return x return x
@ -70,15 +67,15 @@ func (p *PlonkChip) expPowerOf2Extension(x QuadraticExtension) QuadraticExtensio
func (p *PlonkChip) evalL0(x QuadraticExtension, xPowN QuadraticExtension) QuadraticExtension { func (p *PlonkChip) evalL0(x QuadraticExtension, xPowN QuadraticExtension) QuadraticExtension {
// L_0(x) = (x^n - 1) / (n * (x - 1)) // L_0(x) = (x^n - 1) / (n * (x - 1))
evalZeroPoly := p.qe.SubExtension(
evalZeroPoly := p.qeAPI.SubExtension(
xPowN, xPowN,
p.qe.ONE_QE,
p.qeAPI.ONE_QE,
) )
denominator := p.qe.SubExtension(
p.qe.ScalarMulExtension(x, p.DEGREE),
denominator := p.qeAPI.SubExtension(
p.qeAPI.ScalarMulExtension(x, p.DEGREE),
p.DEGREE_QE, p.DEGREE_QE,
) )
return p.qe.DivExtension(
return p.qeAPI.DivExtension(
evalZeroPoly, evalZeroPoly,
denominator, denominator,
) )
@ -88,14 +85,15 @@ func (p *PlonkChip) checkPartialProducts(
numerators []QuadraticExtension, numerators []QuadraticExtension,
denominators []QuadraticExtension, denominators []QuadraticExtension,
challengeNum uint64, challengeNum uint64,
openings OpeningSet,
) []QuadraticExtension { ) []QuadraticExtension {
numPartProds := p.commonData.NumPartialProducts numPartProds := p.commonData.NumPartialProducts
quotDegreeFactor := p.commonData.QuotientDegreeFactor quotDegreeFactor := p.commonData.QuotientDegreeFactor
productAccs := make([]QuadraticExtension, 0, numPartProds+2) productAccs := make([]QuadraticExtension, 0, numPartProds+2)
productAccs = append(productAccs, p.openings.PlonkZs[challengeNum])
productAccs = append(productAccs, p.openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...)
productAccs = append(productAccs, p.openings.PlonkZsNext[challengeNum])
productAccs = append(productAccs, openings.PlonkZs[challengeNum])
productAccs = append(productAccs, openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...)
productAccs = append(productAccs, openings.PlonkZsNext[challengeNum])
partialProductChecks := make([]QuadraticExtension, 0, numPartProds) partialProductChecks := make([]QuadraticExtension, 0, numPartProds)
@ -104,13 +102,13 @@ func (p *PlonkChip) checkPartialProducts(
numeProduct := numerators[ppStartIdx] numeProduct := numerators[ppStartIdx]
denoProduct := denominators[ppStartIdx] denoProduct := denominators[ppStartIdx]
for j := uint64(1); j < quotDegreeFactor; j++ { for j := uint64(1); j < quotDegreeFactor; j++ {
numeProduct = p.qe.MulExtension(numeProduct, numerators[ppStartIdx+j])
denoProduct = p.qe.MulExtension(denoProduct, denominators[ppStartIdx+j])
numeProduct = p.qeAPI.MulExtension(numeProduct, numerators[ppStartIdx+j])
denoProduct = p.qeAPI.MulExtension(denoProduct, denominators[ppStartIdx+j])
} }
partialProductCheck := p.qe.SubExtension(
p.qe.MulExtension(productAccs[i], numeProduct),
p.qe.MulExtension(productAccs[i+1], denoProduct),
partialProductCheck := p.qeAPI.SubExtension(
p.qeAPI.MulExtension(productAccs[i], numeProduct),
p.qeAPI.MulExtension(productAccs[i+1], denoProduct),
) )
partialProductChecks = append(partialProductChecks, partialProductCheck) partialProductChecks = append(partialProductChecks, partialProductCheck)
@ -118,24 +116,24 @@ func (p *PlonkChip) checkPartialProducts(
return partialProductChecks return partialProductChecks
} }
func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticExtension {
func (p *PlonkChip) evalVanishingPoly(proofChallenges ProofChallenges, openings OpeningSet, zetaPowN QuadraticExtension) []QuadraticExtension {
// Calculate the k[i] * x // Calculate the k[i] * x
sIDs := make([]QuadraticExtension, p.commonData.Config.NumRoutedWires) sIDs := make([]QuadraticExtension, p.commonData.Config.NumRoutedWires)
for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ { for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ {
sIDs[i] = p.qe.ScalarMulExtension(p.proofChallenges.PlonkZeta, p.commonData.KIs[i])
sIDs[i] = p.qeAPI.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonData.KIs[i])
} }
// Calculate L_0(zeta) // Calculate L_0(zeta)
l0Zeta := p.evalL0(p.proofChallenges.PlonkZeta, zetaPowN)
l0Zeta := p.evalL0(proofChallenges.PlonkZeta, zetaPowN)
vanishingZ1Terms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges) vanishingZ1Terms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges)
vanishingPartialProductsTerms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts) vanishingPartialProductsTerms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts)
for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ { for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
// L_0(zeta) (Z(zeta) - 1) = 0 // L_0(zeta) (Z(zeta) - 1) = 0
z1_term := p.qe.MulExtension(
z1_term := p.qeAPI.MulExtension(
l0Zeta, l0Zeta,
p.qe.SubExtension(p.openings.PlonkZs[i], p.qe.ONE_QE))
p.qeAPI.SubExtension(openings.PlonkZs[i], p.qeAPI.ONE_QE))
vanishingZ1Terms = append(vanishingZ1Terms, z1_term) vanishingZ1Terms = append(vanishingZ1Terms, z1_term)
numeratorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires) numeratorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires)
@ -144,23 +142,23 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx
// The numerator is `beta * s_id + wire_value + gamma`, and the denominator is // The numerator is `beta * s_id + wire_value + gamma`, and the denominator is
// `beta * s_sigma + wire_value + gamma`. // `beta * s_sigma + wire_value + gamma`.
wireValuePlusGamma := p.qe.AddExtension(
p.openings.Wires[j],
p.qe.FieldToQE(p.proofChallenges.PlonkGammas[i]),
wireValuePlusGamma := p.qeAPI.AddExtension(
openings.Wires[j],
p.qeAPI.FieldToQE(proofChallenges.PlonkGammas[i]),
) )
numerator := p.qe.AddExtension(
p.qe.MulExtension(
p.qe.FieldToQE(p.proofChallenges.PlonkBetas[i]),
numerator := p.qeAPI.AddExtension(
p.qeAPI.MulExtension(
p.qeAPI.FieldToQE(proofChallenges.PlonkBetas[i]),
sIDs[j], sIDs[j],
), ),
wireValuePlusGamma, wireValuePlusGamma,
) )
denominator := p.qe.AddExtension(
p.qe.MulExtension(
p.qe.FieldToQE(p.proofChallenges.PlonkBetas[i]),
p.openings.PlonkSigmas[j],
denominator := p.qeAPI.AddExtension(
p.qeAPI.MulExtension(
p.qeAPI.FieldToQE(proofChallenges.PlonkBetas[i]),
openings.PlonkSigmas[j],
), ),
wireValuePlusGamma, wireValuePlusGamma,
) )
@ -171,7 +169,7 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx
vanishingPartialProductsTerms = append( vanishingPartialProductsTerms = append(
vanishingPartialProductsTerms, vanishingPartialProductsTerms,
p.checkPartialProducts(numeratorValues, denominatorValues, i)...,
p.checkPartialProducts(numeratorValues, denominatorValues, i, openings)...,
) )
} }
@ -179,7 +177,7 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx
reducedValues := make([]QuadraticExtension, p.commonData.Config.NumChallenges) reducedValues := make([]QuadraticExtension, p.commonData.Config.NumChallenges)
for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ { for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
reducedValues[i] = p.qe.ZERO_QE
reducedValues[i] = p.qeAPI.ZERO_QE
} }
// TODO: Enable this check once the custom gate evaluations are added to the // TODO: Enable this check once the custom gate evaluations are added to the
@ -193,11 +191,11 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx
// reverse iterate the vanishingPartialProductsTerms array // reverse iterate the vanishingPartialProductsTerms array
for i := len(vanishingTerms) - 1; i >= 0; i-- { for i := len(vanishingTerms) - 1; i >= 0; i-- {
for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ { for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ {
reducedValues[j] = p.qe.AddExtension(
reducedValues[j] = p.qeAPI.AddExtension(
vanishingTerms[i], vanishingTerms[i],
p.qe.ScalarMulExtension(
p.qeAPI.ScalarMulExtension(
reducedValues[j], reducedValues[j],
p.proofChallenges.PlonkAlphas[j],
proofChallenges.PlonkAlphas[j],
), ),
) )
} }
@ -206,30 +204,38 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx
return reducedValues return reducedValues
} }
func (p *PlonkChip) Verify() {
func (p *PlonkChip) Verify(proofChallenges ProofChallenges, openings OpeningSet) {
// Calculate zeta^n // Calculate zeta^n
zetaPowN := p.expPowerOf2Extension(p.proofChallenges.PlonkZeta)
zetaPowN := p.expPowerOf2Extension(proofChallenges.PlonkZeta)
vanishingPolysZeta := p.evalVanishingPoly(zetaPowN)
vanishingPolysZeta := p.evalVanishingPoly(proofChallenges, openings, zetaPowN)
// Calculate Z(H) // Calculate Z(H)
zHZeta := p.qe.SubExtension(zetaPowN, p.qe.ONE_QE)
zHZeta := p.qeAPI.SubExtension(zetaPowN, p.qeAPI.ONE_QE)
// `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations.
// Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)`
// where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`.
// So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each
// `quotient_degree_factor`-sized chunk of the original evaluations. // `quotient_degree_factor`-sized chunk of the original evaluations.
for i := 0; i < len(p.openings.QuotientPolys); i += int(p.commonData.QuotientDegreeFactor) {
prod := p.qe.MulExtension(
for i := 0; i < len(vanishingPolysZeta); i++ {
quotientPolysStartIdx := i * len(vanishingPolysZeta)
quotientPolysEndIdx := quotientPolysStartIdx + len(vanishingPolysZeta)
prod := p.qeAPI.MulExtension(
zHZeta, zHZeta,
p.qe.ReduceWithPowers(
p.openings.QuotientPolys[i:i+int(p.commonData.QuotientDegreeFactor)],
p.qeAPI.ReduceWithPowers(
openings.QuotientPolys[quotientPolysStartIdx:quotientPolysEndIdx],
zetaPowN, zetaPowN,
), ),
) )
// TODO: Uncomment this after adding in the custom gates evaluations // TODO: Uncomment this after adding in the custom gates evaluations
//p.api.AssertIsEqual(vanishingPolysZeta[i], prod)
//p.qeAPI.AssertIsEqual(vanishingPolysZeta[i], prod)
// For now, just put in a dummy equality check so that VS stops complaining about unused variables
p.qeAPI.AssertIsEqual(
p.qeAPI.MulExtension(vanishingPolysZeta[i], p.qeAPI.ZERO_QE),
p.qeAPI.MulExtension(prod, p.qeAPI.ZERO_QE),
)
} }
} }

+ 1
- 3
plonky2_verifier/plonk_test.go

@ -38,10 +38,8 @@ func (circuit *TestPlonkCircuit) Define(api frontend.API) error {
} }
plonkChip := NewPlonkChip(api, qe, commonCircuitData) plonkChip := NewPlonkChip(api, qe, commonCircuitData)
plonkChip.proofChallenges = proofChallenges
plonkChip.openings = proofWithPis.Proof.Openings
plonkChip.Verify()
plonkChip.Verify(proofChallenges, proofWithPis.Proof.Openings)
return nil return nil
} }

Loading…
Cancel
Save