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