package plonky2_verifier import ( . "gnark-ed25519/field" ) type PlonkChip struct { qe *QuadraticExtensionAPI commonData CommonCircuitData proofChallenges ProofChallenges openings OpeningSet DEGREE F DEGREE_BITS_F F DEGREE_QE QuadraticExtension } func NewPlonkChip(qe *QuadraticExtensionAPI, commonData CommonCircuitData) *PlonkChip { // TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64? return &PlonkChip{ qe: qe, commonData: commonData, DEGREE: NewFieldElement(1 << commonData.DegreeBits), DEGREE_BITS_F: NewFieldElement(commonData.DegreeBits), DEGREE_QE: QuadraticExtension{NewFieldElement(1 << commonData.DegreeBits), NewFieldElement(0)}, } } func (p *PlonkChip) expPowerOf2Extension(x QuadraticExtension) QuadraticExtension { for i := uint64(0); i < p.commonData.DegreeBits; i++ { x = p.qe.SquareExtension(x) } return x } func (p *PlonkChip) evalL0(x QuadraticExtension, xPowN QuadraticExtension) QuadraticExtension { // L_0(x) = (x^n - 1) / (n * (x - 1)) evalZeroPoly := p.qe.SubExtension( xPowN, p.qe.ONE, ) denominator := p.qe.SubExtension( p.qe.ScalarMulExtension(x, p.DEGREE), p.DEGREE_QE, ) return p.qe.DivExtension( evalZeroPoly, denominator, ) } func (p *PlonkChip) checkPartialProducts( numerators []QuadraticExtension, denominators []QuadraticExtension, challengeNum uint64, ) []QuadraticExtension { numPartProds := p.commonData.NumPartialProducts quotDegreeFactor := p.commonData.QuotientDegreeFactor productAccs := make([]QuadraticExtension, 0, numPartProds+2) productAccs = append(productAccs, p.openings.PlonkZs[challengeNum]) productAccs = append(productAccs, p.openings.PartialProducts[challengeNum*numPartProds:(challengeNum+1)*numPartProds]...) productAccs = append(productAccs, p.openings.PlonkZsNext[challengeNum]) partialProductChecks := make([]QuadraticExtension, 0, numPartProds) for i := uint64(0); i <= numPartProds; i += 1 { ppStartIdx := i * quotDegreeFactor numeProduct := numerators[ppStartIdx] denoProduct := denominators[ppStartIdx] for j := uint64(1); j < quotDegreeFactor; j++ { numeProduct = p.qe.MulExtension(numeProduct, numerators[ppStartIdx+j]) denoProduct = p.qe.MulExtension(denoProduct, denominators[ppStartIdx+j]) } partialProductCheck := p.qe.SubExtension( p.qe.MulExtension(productAccs[i], numeProduct), p.qe.MulExtension(productAccs[i+1], denoProduct), ) partialProductChecks = append(partialProductChecks, partialProductCheck) } return partialProductChecks } func (p *PlonkChip) evalVanishingPoly() []QuadraticExtension { // Calculate the k[i] * x sIDs := make([]QuadraticExtension, p.commonData.Config.NumRoutedWires) for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ { sIDs[i] = p.qe.ScalarMulExtension(p.proofChallenges.PlonkZeta, p.commonData.KIs[i]) } // Calculate zeta^n zetaPowN := p.expPowerOf2Extension(p.proofChallenges.PlonkZeta) // Calculate L_0(zeta) l0Zeta := p.evalL0(p.proofChallenges.PlonkZeta, zetaPowN) vanishingZ1Terms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges) vanishingPartialProductsTerms := make([]QuadraticExtension, 0, p.commonData.Config.NumChallenges*p.commonData.NumPartialProducts) for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ { // L_0(zeta) (Z(zeta) - 1) = 0 z1_term := p.qe.MulExtension( l0Zeta, p.qe.SubExtension(p.openings.PlonkZs[i], p.qe.ONE)) vanishingZ1Terms = append(vanishingZ1Terms, z1_term) numeratorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires) denominatorValues := make([]QuadraticExtension, 0, p.commonData.Config.NumRoutedWires) for j := uint64(0); j < p.commonData.Config.NumRoutedWires; j++ { // The numerator is `beta * s_id + wire_value + gamma`, and the denominator is // `beta * s_sigma + wire_value + gamma`. wireValuePlusGamma := p.qe.AddExtension( p.openings.Wires[j], p.qe.FieldToQE(p.proofChallenges.PlonkGammas[i]), ) numerator := p.qe.AddExtension( p.qe.MulExtension( p.qe.FieldToQE(p.proofChallenges.PlonkBetas[i]), sIDs[j], ), wireValuePlusGamma, ) denominator := p.qe.AddExtension( p.qe.MulExtension( p.qe.FieldToQE(p.proofChallenges.PlonkBetas[i]), p.openings.PlonkSigmas[j], ), wireValuePlusGamma, ) numeratorValues = append(numeratorValues, numerator) denominatorValues = append(denominatorValues, denominator) } vanishingPartialProductsTerms = append( vanishingPartialProductsTerms, p.checkPartialProducts(numeratorValues, denominatorValues, i)..., ) } vanishingTerms := append(vanishingZ1Terms, vanishingPartialProductsTerms...) reducedValues := make([]QuadraticExtension, p.commonData.Config.NumChallenges) for i := uint64(0); i < p.commonData.Config.NumChallenges; i++ { reducedValues[i] = p.qe.ZERO_QE } // TODO: Enable this check once the custom gate evaluations are added to the // vanishingTerms array /* if len(vanishingTerms) != int(p.commonData.QuotientDegreeFactor) { panic("evalVanishingPoly: len(vanishingTerms) != int(p.commonData.QuotientDegreeFactor)") } */ // reverse iterate the vanishingPartialProductsTerms array for i := len(vanishingTerms) - 1; i >= 0; i-- { for j := uint64(0); j < p.commonData.Config.NumChallenges; j++ { reducedValues[j] = p.qe.AddExtension( vanishingTerms[i], p.qe.ScalarMulExtension( reducedValues[j], p.proofChallenges.PlonkAlphas[j], ), ) } } return reducedValues } func (p *PlonkChip) Verify() { vanishingPolysZeta := p.evalVanishingPoly() for _, vp := range vanishingPolysZeta { p.qe.Println(vp) } /* let alphas = &alphas.iter().map(|&a| a.into()).collect::>(); plonk_common::reduce_with_powers_multi(&vanishing_terms, alphas) // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = challenges .plonk_zeta .exp_power_of_2(common_data.degree_bits()); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each // `quotient_degree_factor`-sized chunk of the original evaluations. for (i, chunk) in quotient_polys_zeta .chunks(common_data.quotient_degree_factor) .enumerate() { ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); } */ }