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