arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/plonky2_verifier/plonk.go
plonky2_verifier/plonk.goRaw
  1package plonky2_verifier
  2
  3import (
  4	. "gnark-plonky2-verifier/field"
  5
  6	"github.com/consensys/gnark/frontend"
  7)
  8
  9type PlonkOracle struct {
 10	index    uint64
 11	blinding bool
 12}
 13
 14var CONSTANTS_SIGMAS = PlonkOracle{
 15	index:    0,
 16	blinding: false,
 17}
 18
 19var WIRES = PlonkOracle{
 20	index:    1,
 21	blinding: true,
 22}
 23
 24var ZS_PARTIAL_PRODUCTS = PlonkOracle{
 25	index:    2,
 26	blinding: true,
 27}
 28
 29var QUOTIENT = PlonkOracle{
 30	index:    3,
 31	blinding: true,
 32}
 33
 34type PlonkChip struct {
 35	api   frontend.API           `gnark:"-"`
 36	qeAPI *QuadraticExtensionAPI `gnark:"-"`
 37
 38	commonData CommonCircuitData `gnark:"-"`
 39
 40	DEGREE        F                  `gnark:"-"`
 41	DEGREE_BITS_F F                  `gnark:"-"`
 42	DEGREE_QE     QuadraticExtension `gnark:"-"`
 43}
 44
 45func NewPlonkChip(api frontend.API, qeAPI *QuadraticExtensionAPI, commonData CommonCircuitData) *PlonkChip {
 46	// TODO:  Should degreeBits be verified that it fits within the field and that degree is within uint64?
 47
 48	return &PlonkChip{
 49		api:   api,
 50		qeAPI: qeAPI,
 51
 52		commonData: commonData,
 53
 54		DEGREE:        NewFieldElement(1 << commonData.DegreeBits),
 55		DEGREE_BITS_F: NewFieldElement(commonData.DegreeBits),
 56		DEGREE_QE:     QuadraticExtension{NewFieldElement(1 << commonData.DegreeBits), ZERO_F},
 57	}
 58}
 59
 60func (p *PlonkChip) expPowerOf2Extension(x QuadraticExtension) QuadraticExtension {
 61	for i := uint64(0); i < p.commonData.DegreeBits; i++ {
 62		x = p.qeAPI.SquareExtension(x)
 63	}
 64
 65	return x
 66}
 67
 68func (p *PlonkChip) evalL0(x QuadraticExtension, xPowN QuadraticExtension) QuadraticExtension {
 69	// L_0(x) = (x^n - 1) / (n * (x - 1))
 70	evalZeroPoly := p.qeAPI.SubExtension(
 71		xPowN,
 72		p.qeAPI.ONE_QE,
 73	)
 74	denominator := p.qeAPI.SubExtension(
 75		p.qeAPI.ScalarMulExtension(x, p.DEGREE),
 76		p.DEGREE_QE,
 77	)
 78	return p.qeAPI.DivExtension(
 79		evalZeroPoly,
 80		denominator,
 81	)
 82}
 83
 84func (p *PlonkChip) checkPartialProducts(
 85	numerators []QuadraticExtension,
 86	denominators []QuadraticExtension,
 87	challengeNum uint64,
 88	openings OpeningSet,
 89) []QuadraticExtension {
 90	numPartProds := p.commonData.NumPartialProducts
 91	quotDegreeFactor := p.commonData.QuotientDegreeFactor
 92
 93	productAccs := make([]QuadraticExtension, 0, numPartProds+2)
 94	productAccs = append(productAccs, openings.PlonkZs[challengeNum])
 95	productAccs = append(productAccs, openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...)
 96	productAccs = append(productAccs, openings.PlonkZsNext[challengeNum])
 97
 98	partialProductChecks := make([]QuadraticExtension, 0, numPartProds)
 99
100	for i := uint64(0); i <= numPartProds; i += 1 {
101		ppStartIdx := i * quotDegreeFactor
102		numeProduct := numerators[ppStartIdx]
103		denoProduct := denominators[ppStartIdx]
104		for j := uint64(1); j < quotDegreeFactor; j++ {
105			numeProduct = p.qeAPI.MulExtension(numeProduct, numerators[ppStartIdx+j])
106			denoProduct = p.qeAPI.MulExtension(denoProduct, denominators[ppStartIdx+j])
107		}
108
109		partialProductCheck := p.qeAPI.SubExtension(
110			p.qeAPI.MulExtension(productAccs[i], numeProduct),
111			p.qeAPI.MulExtension(productAccs[i+1], denoProduct),
112		)
113
114		partialProductChecks = append(partialProductChecks, partialProductCheck)
115	}
116	return partialProductChecks
117}
118
119func (p *PlonkChip) evaluateGateConstraints(vars EvaluationVars) []QuadraticExtension {
120	constraints := make([]QuadraticExtension, p.commonData.NumGateConstraints)
121	for i, _ := range constraints {
122		constraints[i] = p.qeAPI.ZERO_QE
123	}
124
125	for i, gate := range p.commonData.Gates {
126		selectorIndex := p.commonData.SelectorsInfo.selectorIndices[i]
127
128		gateConstraints := p.evalFiltered(
129			gate,
130			vars,
131			uint64(i),
132			selectorIndex,
133			p.commonData.SelectorsInfo.groups[selectorIndex],
134			p.commonData.SelectorsInfo.NumSelectors(),
135		)
136
137		for i, constraint := range gateConstraints {
138			if uint64(i) >= p.commonData.NumGateConstraints {
139				panic("num_constraints() gave too low of a number")
140			}
141			constraints[i] = p.qeAPI.AddExtension(constraints[i], constraint)
142		}
143	}
144
145	return constraints
146}
147
148func (p *PlonkChip) evalVanishingPoly(vars EvaluationVars, proofChallenges ProofChallenges, openings OpeningSet, zetaPowN QuadraticExtension) []QuadraticExtension {
149	constraintTerms := p.evaluateGateConstraints(vars)
150
151	// Calculate the k[i] * x
152	sIDs := make([]QuadraticExtension, p.commonData.Config.NumRoutedWires)
153
154	for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ {
155		sIDs[i] = p.qeAPI.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonData.KIs[i])
156	}
157
158	// Calculate L_0(zeta)
159	l0Zeta := p.evalL0(proofChallenges.PlonkZeta, zetaPowN)
160
161	vanishingZ1Terms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges)
162	vanishingPartialProductsTerms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts)
163	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
164		// L_0(zeta) (Z(zeta) - 1) = 0
165		z1_term := p.qeAPI.MulExtension(
166			l0Zeta,
167			p.qeAPI.SubExtension(openings.PlonkZs[i], p.qeAPI.ONE_QE))
168		vanishingZ1Terms = append(vanishingZ1Terms, z1_term)
169
170		numeratorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires)
171		denominatorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires)
172		for j := uint64(0); j < p.commonData.Config.NumRoutedWires; j++ {
173			// The numerator is `beta * s_id + wire_value + gamma`, and the denominator is
174			// `beta * s_sigma + wire_value + gamma`.
175			wireValuePlusGamma := p.qeAPI.AddExtension(
176				openings.Wires[j],
177				p.qeAPI.FieldToQE(proofChallenges.PlonkGammas[i]),
178			)
179
180			numerator := p.qeAPI.AddExtension(
181				p.qeAPI.MulExtension(
182					p.qeAPI.FieldToQE(proofChallenges.PlonkBetas[i]),
183					sIDs[j],
184				),
185				wireValuePlusGamma,
186			)
187
188			denominator := p.qeAPI.AddExtension(
189				p.qeAPI.MulExtension(
190					p.qeAPI.FieldToQE(proofChallenges.PlonkBetas[i]),
191					openings.PlonkSigmas[j],
192				),
193				wireValuePlusGamma,
194			)
195
196			numeratorValues = append(numeratorValues, numerator)
197			denominatorValues = append(denominatorValues, denominator)
198		}
199
200		vanishingPartialProductsTerms = append(
201			vanishingPartialProductsTerms,
202			p.checkPartialProducts(numeratorValues, denominatorValues, i, openings)...,
203		)
204	}
205
206	vanishingTerms := append(vanishingZ1Terms, vanishingPartialProductsTerms...)
207	vanishingTerms = append(vanishingTerms, constraintTerms...)
208
209	reducedValues := make([]QuadraticExtension, p.commonData.Config.NumChallenges)
210	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
211		reducedValues[i] = p.qeAPI.ZERO_QE
212	}
213
214	// reverse iterate the vanishingPartialProductsTerms array
215	for i := len(vanishingTerms) - 1; i >= 0; i-- {
216		for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ {
217			reducedValues[j] = p.qeAPI.AddExtension(
218				vanishingTerms[i],
219				p.qeAPI.ScalarMulExtension(
220					reducedValues[j],
221					proofChallenges.PlonkAlphas[j],
222				),
223			)
224		}
225	}
226
227	return reducedValues
228}
229
230func (p *PlonkChip) Verify(proofChallenges ProofChallenges, openings OpeningSet, publicInputsHash Hash) {
231	// Calculate zeta^n
232	zetaPowN := p.expPowerOf2Extension(proofChallenges.PlonkZeta)
233
234	localConstants := openings.Constants
235	localWires := openings.Wires
236	vars := EvaluationVars{
237		localConstants,
238		localWires,
239		publicInputsHash,
240	}
241
242	vanishingPolysZeta := p.evalVanishingPoly(vars, proofChallenges, openings, zetaPowN)
243
244	// Calculate Z(H)
245	zHZeta := p.qeAPI.SubExtension(zetaPowN, p.qeAPI.ONE_QE)
246
247	// `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations.
248	// Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)`
249	// where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`.
250	// So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each
251	// `quotient_degree_factor`-sized chunk of the original evaluations.
252	for i := 0; i < len(vanishingPolysZeta); i++ {
253		quotientPolysStartIdx := i * int(p.commonData.QuotientDegreeFactor)
254		quotientPolysEndIdx := quotientPolysStartIdx + int(p.commonData.QuotientDegreeFactor)
255		prod := p.qeAPI.MulExtension(
256			zHZeta,
257			p.qeAPI.ReduceWithPowers(
258				openings.QuotientPolys[quotientPolysStartIdx:quotientPolysEndIdx],
259				zetaPowN,
260			),
261		)
262
263		p.qeAPI.AssertIsEqual(vanishingPolysZeta[i], prod)
264	}
265}