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	return glApi.DivExtension(
 75		evalZeroPoly,
 76		denominator,
 77	)
 78}
 79
 80func (p *PlonkChip) checkPartialProducts(
 81	numerators []gl.QuadraticExtensionVariable,
 82	denominators []gl.QuadraticExtensionVariable,
 83	challengeNum uint64,
 84	openings variables.OpeningSet,
 85) []gl.QuadraticExtensionVariable {
 86	glApi := gl.New(p.api)
 87	numPartProds := p.commonData.NumPartialProducts
 88	quotDegreeFactor := p.commonData.QuotientDegreeFactor
 89
 90	productAccs := make([]gl.QuadraticExtensionVariable, 0, numPartProds+2)
 91	productAccs = append(productAccs, openings.PlonkZs[challengeNum])
 92	productAccs = append(productAccs, openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...)
 93	productAccs = append(productAccs, openings.PlonkZsNext[challengeNum])
 94
 95	partialProductChecks := make([]gl.QuadraticExtensionVariable, 0, numPartProds)
 96
 97	for i := uint64(0); i <= numPartProds; i += 1 {
 98		ppStartIdx := i * quotDegreeFactor
 99		numeProduct := numerators[ppStartIdx]
100		denoProduct := denominators[ppStartIdx]
101		for j := uint64(1); j < quotDegreeFactor; j++ {
102			numeProduct = glApi.MulExtension(numeProduct, numerators[ppStartIdx+j])
103			denoProduct = glApi.MulExtension(denoProduct, denominators[ppStartIdx+j])
104		}
105
106		partialProductCheck := glApi.SubExtension(
107			glApi.MulExtension(productAccs[i], numeProduct),
108			glApi.MulExtension(productAccs[i+1], denoProduct),
109		)
110
111		partialProductChecks = append(partialProductChecks, partialProductCheck)
112	}
113	return partialProductChecks
114}
115
116func (p *PlonkChip) evalVanishingPoly(
117	vars gates.EvaluationVars,
118	proofChallenges variables.ProofChallenges,
119	openings variables.OpeningSet,
120	zetaPowN gl.QuadraticExtensionVariable,
121) []gl.QuadraticExtensionVariable {
122	glApi := gl.New(p.api)
123	constraintTerms := p.evaluateGatesChip.EvaluateGateConstraints(vars)
124
125	// Calculate the k[i] * x
126	sIDs := make([]gl.QuadraticExtensionVariable, p.commonData.Config.NumRoutedWires)
127
128	for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ {
129		sIDs[i] = glApi.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonDataKIs[i])
130	}
131
132	// Calculate L_0(zeta)
133	l0Zeta := p.evalL0(proofChallenges.PlonkZeta, zetaPowN)
134
135	vanishingZ1Terms := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumChallenges)
136	vanishingPartialProductsTerms := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts)
137	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
138		// L_0(zeta) (Z(zeta) - 1) = 0
139		z1_term := glApi.MulExtension(
140			l0Zeta,
141			glApi.SubExtension(openings.PlonkZs[i], gl.OneExtension()))
142		vanishingZ1Terms = append(vanishingZ1Terms, z1_term)
143
144		numeratorValues := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumRoutedWires)
145		denominatorValues := make([]gl.QuadraticExtensionVariable, 0, p.commonData.Config.NumRoutedWires)
146		for j := uint64(0); j < p.commonData.Config.NumRoutedWires; j++ {
147			// The numerator is `beta * s_id + wire_value + gamma`, and the denominator is
148			// `beta * s_sigma + wire_value + gamma`.
149			wireValuePlusGamma := glApi.AddExtension(
150				openings.Wires[j],
151				gl.NewQuadraticExtensionVariable(proofChallenges.PlonkGammas[i], gl.Zero()),
152			)
153
154			numerator := glApi.AddExtension(
155				glApi.MulExtension(
156					gl.NewQuadraticExtensionVariable(proofChallenges.PlonkBetas[i], gl.Zero()),
157					sIDs[j],
158				),
159				wireValuePlusGamma,
160			)
161
162			denominator := glApi.AddExtension(
163				glApi.MulExtension(
164					gl.NewQuadraticExtensionVariable(proofChallenges.PlonkBetas[i], gl.Zero()),
165					openings.PlonkSigmas[j],
166				),
167				wireValuePlusGamma,
168			)
169
170			numeratorValues = append(numeratorValues, numerator)
171			denominatorValues = append(denominatorValues, denominator)
172		}
173
174		vanishingPartialProductsTerms = append(
175			vanishingPartialProductsTerms,
176			p.checkPartialProducts(numeratorValues, denominatorValues, i, openings)...,
177		)
178	}
179
180	vanishingTerms := append(vanishingZ1Terms, vanishingPartialProductsTerms...)
181	vanishingTerms = append(vanishingTerms, constraintTerms...)
182
183	reducedValues := make([]gl.QuadraticExtensionVariable, p.commonData.Config.NumChallenges)
184	for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ {
185		reducedValues[i] = gl.ZeroExtension()
186	}
187
188	// reverse iterate the vanishingPartialProductsTerms array
189	for i := len(vanishingTerms) - 1; i >= 0; i-- {
190		for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ {
191			reducedValues[j] = glApi.AddExtension(
192				vanishingTerms[i],
193				glApi.ScalarMulExtension(
194					reducedValues[j],
195					proofChallenges.PlonkAlphas[j],
196				),
197			)
198		}
199	}
200
201	return reducedValues
202}
203
204func (p *PlonkChip) Verify(
205	proofChallenges variables.ProofChallenges,
206	openings variables.OpeningSet,
207	publicInputsHash poseidon.GoldilocksHashOut,
208) {
209	glApi := gl.New(p.api)
210
211	// Calculate zeta^n
212	zetaPowN := p.expPowerOf2Extension(proofChallenges.PlonkZeta)
213
214	localConstants := openings.Constants
215	localWires := openings.Wires
216	vars := gates.NewEvaluationVars(
217		localConstants,
218		localWires,
219		publicInputsHash,
220	)
221
222	vanishingPolysZeta := p.evalVanishingPoly(*vars, proofChallenges, openings, zetaPowN)
223
224	// Calculate Z(H)
225	zHZeta := glApi.SubExtension(zetaPowN, gl.OneExtension())
226
227	// `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations.
228	// Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)`
229	// where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`.
230	// So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each
231	// `quotient_degree_factor`-sized chunk of the original evaluations.
232	for i := 0; i < len(vanishingPolysZeta); i++ {
233		quotientPolysStartIdx := i * int(p.commonData.QuotientDegreeFactor)
234		quotientPolysEndIdx := quotientPolysStartIdx + int(p.commonData.QuotientDegreeFactor)
235		prod := glApi.MulExtension(
236			zHZeta,
237			glApi.ReduceWithPowers(
238				openings.QuotientPolys[quotientPolysStartIdx:quotientPolysEndIdx],
239				zetaPowN,
240			),
241		)
242
243		glApi.AssertIsEqualExtension(vanishingPolysZeta[i], prod)
244	}
245}