arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/plonk/plonk.go
plonk/plonk.goRaw
  1package plonk
  2
  3import (
  4	"github.com/consensys/gnark/frontend"
  5	gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
  6	"github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates"
  7	"github.com/succinctlabs/gnark-plonky2-verifier/poseidon"
  8	"github.com/succinctlabs/gnark-plonky2-verifier/types"
  9	"github.com/succinctlabs/gnark-plonky2-verifier/variables"
 10)
 11
 12type PlonkChip struct {
 13	api frontend.API `gnark:"-"`
 14
 15	commonData types.CommonCircuitData `gnark:"-"`
 16
 17	// These are global constant variables that we use in this Chip that we save here.
 18	// This avoids having to recreate them every time we use them.
 19	DEGREE        gl.Variable                   `gnark:"-"`
 20	DEGREE_BITS_F gl.Variable                   `gnark:"-"`
 21	DEGREE_QE     gl.QuadraticExtensionVariable `gnark:"-"`
 22	commonDataKIs []gl.Variable                 `gnark:"-"`
 23
 24	evaluateGatesChip *gates.EvaluateGatesChip
 25}
 26
 27func NewPlonkChip(api frontend.API, commonData types.CommonCircuitData) *PlonkChip {
 28	// Create the gates based on commonData GateIds
 29	createdGates := []gates.Gate{}
 30	for _, gateId := range commonData.GateIds {
 31		createdGates = append(createdGates, gates.GateInstanceFromId(gateId))
 32	}
 33
 34	evaluateGatesChip := gates.NewEvaluateGatesChip(
 35		api,
 36		createdGates,
 37		commonData.NumGateConstraints,
 38		commonData.SelectorsInfo,
 39	)
 40
 41	return &PlonkChip{
 42		api: api,
 43
 44		commonData: commonData,
 45
 46		DEGREE:        gl.NewVariable(1 << commonData.DegreeBits),
 47		DEGREE_BITS_F: gl.NewVariable(commonData.DegreeBits),
 48		DEGREE_QE:     gl.NewVariable(1 << commonData.DegreeBits).ToQuadraticExtension(),
 49		commonDataKIs: gl.Uint64ArrayToVariableArray(commonData.KIs),
 50
 51		evaluateGatesChip: evaluateGatesChip,
 52	}
 53}
 54
 55func (p *PlonkChip) expPowerOf2Extension(x gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable {
 56	glApi := gl.New(p.api)
 57	for i := uint64(0); i < p.commonData.DegreeBits; i++ {
 58		x = glApi.MulExtension(x, x)
 59	}
 60	return x
 61}
 62
 63func (p *PlonkChip) evalL0(x gl.QuadraticExtensionVariable, xPowN gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable {
 64	// L_0(x) = (x^n - 1) / (n * (x - 1))
 65	glApi := gl.New(p.api)
 66	evalZeroPoly := glApi.SubExtension(
 67		xPowN,
 68		gl.OneExtension(),
 69	)
 70	denominator := glApi.SubExtension(
 71		glApi.ScalarMulExtension(x, p.DEGREE),
 72		p.DEGREE_QE,
 73	)
 74
 75	quotient, hasQuotient := glApi.DivExtension(
 76		evalZeroPoly,
 77		denominator,
 78	)
 79
 80	p.api.AssertIsEqual(hasQuotient, frontend.Variable(1))
 81
 82	return quotient
 83}
 84
 85func (p *PlonkChip) checkPartialProducts(
 86	numerators []gl.QuadraticExtensionVariable,
 87	denominators []gl.QuadraticExtensionVariable,
 88	challengeNum uint64,
 89	openings variables.OpeningSet,
 90) []gl.QuadraticExtensionVariable {
 91	glApi := gl.New(p.api)
 92	numPartProds := p.commonData.NumPartialProducts
 93	quotDegreeFactor := p.commonData.QuotientDegreeFactor
 94
 95	productAccs := make([]gl.QuadraticExtensionVariable, 0, numPartProds+2)
 96	productAccs = append(productAccs, openings.PlonkZs[challengeNum])
 97	productAccs = append(productAccs, openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...)
 98	productAccs = append(productAccs, openings.PlonkZsNext[challengeNum])
 99
100	partialProductChecks := make([]gl.QuadraticExtensionVariable, 0, numPartProds)
101
102	for i := uint64(0); i <= numPartProds; i += 1 {
103		ppStartIdx := i * quotDegreeFactor
104		numeProduct := numerators[ppStartIdx]
105		denoProduct := denominators[ppStartIdx]
106		for j := uint64(1); j < quotDegreeFactor; j++ {
107			numeProduct = glApi.MulExtension(numeProduct, numerators[ppStartIdx+j])
108			denoProduct = glApi.MulExtension(denoProduct, denominators[ppStartIdx+j])
109		}
110
111		partialProductCheck := glApi.SubExtension(
112			glApi.MulExtension(productAccs[i], numeProduct),
113			glApi.MulExtension(productAccs[i+1], denoProduct),
114		)
115
116		partialProductChecks = append(partialProductChecks, partialProductCheck)
117	}
118	return partialProductChecks
119}
120
121func (p *PlonkChip) evalVanishingPoly(
122	vars gates.EvaluationVars,
123	proofChallenges variables.ProofChallenges,
124	openings variables.OpeningSet,
125	zetaPowN gl.QuadraticExtensionVariable,
126) []gl.QuadraticExtensionVariable {
127	glApi := gl.New(p.api)
128	constraintTerms := p.evaluateGatesChip.EvaluateGateConstraints(vars)
129
130	// Calculate the k[i] * x
131	sIDs := make([]gl.QuadraticExtensionVariable, p.commonData.Config.NumRoutedWires)
132
133	for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ {
134		sIDs[i] = glApi.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonDataKIs[i])
135	}
136
137	// Calculate L_0(zeta)
138	l0Zeta := p.evalL0(proofChallenges.PlonkZeta, zetaPowN)
139
140	vanishingZ1Terms := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumChallenges)
141	vanishingPartialProductsTerms := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts)
142	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
143		// L_0(zeta) (Z(zeta) - 1) = 0
144		z1_term := glApi.MulExtension(
145			l0Zeta,
146			glApi.SubExtension(openings.PlonkZs[i], gl.OneExtension()))
147		vanishingZ1Terms = append(vanishingZ1Terms, z1_term)
148
149		numeratorValues := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumRoutedWires)
150		denominatorValues := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumRoutedWires)
151		for j := uint64(0); j < p.commonData.Config.NumRoutedWires; j++ {
152			// The numerator is `beta * s_id + wire_value + gamma`, and the denominator is
153			// `beta * s_sigma + wire_value + gamma`.
154			wireValuePlusGamma := glApi.AddExtension(
155				openings.Wires[j],
156				gl.NewQuadraticExtensionVariable(proofChallenges.PlonkGammas[i], gl.Zero()),
157			)
158
159			numerator := glApi.AddExtension(
160				glApi.MulExtension(
161					gl.NewQuadraticExtensionVariable(proofChallenges.PlonkBetas[i], gl.Zero()),
162					sIDs[j],
163				),
164				wireValuePlusGamma,
165			)
166
167			denominator := glApi.AddExtension(
168				glApi.MulExtension(
169					gl.NewQuadraticExtensionVariable(proofChallenges.PlonkBetas[i], gl.Zero()),
170					openings.PlonkSigmas[j],
171				),
172				wireValuePlusGamma,
173			)
174
175			numeratorValues = append(numeratorValues, numerator)
176			denominatorValues = append(denominatorValues, denominator)
177		}
178
179		vanishingPartialProductsTerms = append(
180			vanishingPartialProductsTerms,
181			p.checkPartialProducts(numeratorValues, denominatorValues, i, openings)...,
182		)
183	}
184
185	vanishingTerms := append(vanishingZ1Terms, vanishingPartialProductsTerms...)
186	vanishingTerms = append(vanishingTerms, constraintTerms...)
187
188	reducedValues := make([]gl.QuadraticExtensionVariable, p.commonData.Config.NumChallenges)
189	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
190		reducedValues[i] = gl.ZeroExtension()
191	}
192
193	// reverse iterate the vanishingPartialProductsTerms array
194	for i := len(vanishingTerms) - 1; i >= 0; i-- {
195		for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ {
196			reducedValues[j] = glApi.AddExtension(
197				vanishingTerms[i],
198				glApi.ScalarMulExtension(
199					reducedValues[j],
200					proofChallenges.PlonkAlphas[j],
201				),
202			)
203		}
204	}
205
206	return reducedValues
207}
208
209func (p *PlonkChip) Verify(
210	proofChallenges variables.ProofChallenges,
211	openings variables.OpeningSet,
212	publicInputsHash poseidon.GoldilocksHashOut,
213) {
214	glApi := gl.New(p.api)
215
216	// Calculate zeta^n
217	zetaPowN := p.expPowerOf2Extension(proofChallenges.PlonkZeta)
218
219	localConstants := openings.Constants
220	localWires := openings.Wires
221	vars := gates.NewEvaluationVars(
222		localConstants,
223		localWires,
224		publicInputsHash,
225	)
226
227	vanishingPolysZeta := p.evalVanishingPoly(*vars, proofChallenges, openings, zetaPowN)
228
229	// Calculate Z(H)
230	zHZeta := glApi.SubExtension(zetaPowN, gl.OneExtension())
231
232	// `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations.
233	// Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)`
234	// where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`.
235	// So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each
236	// `quotient_degree_factor`-sized chunk of the original evaluations.
237	for i := 0; i < len(vanishingPolysZeta); i++ {
238		quotientPolysStartIdx := i * int(p.commonData.QuotientDegreeFactor)
239		quotientPolysEndIdx := quotientPolysStartIdx + int(p.commonData.QuotientDegreeFactor)
240		prod := glApi.MulExtension(
241			zHZeta,
242			glApi.ReduceWithPowers(
243				openings.QuotientPolys[quotientPolysStartIdx:quotientPolysEndIdx],
244				zetaPowN,
245			),
246		)
247
248		glApi.AssertIsEqualExtension(vanishingPolysZeta[i], prod)
249	}
250}