From d194d3860f85ebd1a55d5a15c8a994dfa1441ba5 Mon Sep 17 00:00:00 2001 From: Kevin Jue Date: Fri, 4 Nov 2022 21:23:32 -0700 Subject: [PATCH] initial commit for fri verification chip --- plonky2_verifier/fri.go | 118 +++++++++++++++++++++++++++++++++++ plonky2_verifier/fri_test.go | 35 +++++++++++ plonky2_verifier/plonk.go | 19 +----- plonky2_verifier/structs.go | 2 +- plonky2_verifier/utils.go | 21 +++++++ 5 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 plonky2_verifier/fri_test.go create mode 100644 plonky2_verifier/utils.go diff --git a/plonky2_verifier/fri.go b/plonky2_verifier/fri.go index 7e66fdc..bdc57bd 100644 --- a/plonky2_verifier/fri.go +++ b/plonky2_verifier/fri.go @@ -1,7 +1,12 @@ package plonky2_verifier import ( + "fmt" + "gnark-ed25519/field" . "gnark-ed25519/field" + "math" + + "github.com/consensys/gnark/frontend" ) type FriOpeningBatch struct { @@ -23,3 +28,116 @@ func (c *OpeningSet) ToFriOpenings() FriOpenings { zetaNextBatch := FriOpeningBatch{values: c.PlonkZsNext} return FriOpenings{Batches: []FriOpeningBatch{zetaBatch, zetaNextBatch}} } + +type FriChip struct { + api frontend.API + field frontend.API + qe *QuadraticExtensionAPI + + friParams *FriParams + verifierOnlyCircuitData *VerifierOnlyCircuitData +} + +func NewFriChip(api frontend.API, field frontend.API, qe *QuadraticExtensionAPI, friParams *FriParams) *FriChip { + return &FriChip{ + api: api, + field: field, + qe: qe, + friParams: friParams, + } +} + +func (f *FriChip) assertLeadingZeros(powWitness F, friConfig FriConfig) { + // Asserts that powWitness'es big-endian bit representation has at least `leading_zeros` leading zeros. + // Note that this is assuming that the Goldilocks field is being used. Specfically that the + // field is 64 bits long + maxPowWitness := uint64(math.Pow(2, float64(64-friConfig.ProofOfWorkBits))) - 1 + f.field.Println(powWitness) + fmt.Println(maxPowWitness) + fmt.Println(friConfig.ProofOfWorkBits) + f.field.AssertIsLessOrEqual(powWitness, field.NewFieldElement(maxPowWitness)) +} + +func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExtension) []QuadraticExtension { + // One reduced opening for all openings evaluated at point Zeta. + // Another one for all openings evaluated at point Zeta * Omega (which is only PlonkZsNext polynomial) + + reducedOpenings := make([]QuadraticExtension, 0, 2) + for _, batch := range openings.Batches { + reducedOpenings = append(reducedOpenings, reduceWithPowers(f.qe, batch.values, alpha)) + } + + return reducedOpenings +} + +func (f *FriChip) verifyMerkleProofToCap(leafData []F, leafIndex F, merkleCap MerkleCap, proof *MerkleProof) { +} + +func (f *FriChip) verifyInitialProof(xIndex F, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap) { + if len(proof.EvalsProofs) != len(initialMerkleCaps) { + panic("length of eval proofs in fri proof should equal length of initial merkle caps") + } + + for i := 0; i < len(initialMerkleCaps); i++ { + evals := proof.EvalsProofs[i].Elements + merkleProof := proof.EvalsProofs[i].MerkleProof + cap := initialMerkleCaps[i] + f.verifyMerkleProofToCap(evals, xIndex, cap, &merkleProof) + } +} + +func (f *FriChip) verifyQueryRound( + challenges *FriChallenges, + precomputedReducedEval []QuadraticExtension, + initialMerkleCaps []MerkleCap, + proof *FriProof, + xIndex F, + n uint64, + roundProof *FriQueryRound, +) { + f.verifyInitialProof(xIndex, &roundProof.InitialTreesProof, initialMerkleCaps) +} + +func (f *FriChip) VerifyFriProof( + openings *FriOpenings, + friChallenges *FriChallenges, + initialMerkleCaps []MerkleCap, + friProof *FriProof, +) { + // TODO: Check fri config + /* if let Some(max_arity_bits) = params.max_arity_bits() { + self.check_recursion_config::(max_arity_bits); + } + + debug_assert_eq!( + params.final_poly_len(), + proof.final_poly.len(), + "Final polynomial has wrong degree." + ); */ + + // Check POW + f.assertLeadingZeros(friProof.PowWitness, f.friParams.Config) + + precomputedReducedEvals := f.fromOpeningsAndAlpha(openings, friChallenges.FriAlpha) + + // Size of the LDE domain. + n := uint64(math.Pow(2, float64(f.friParams.DegreeBits+f.friParams.Config.RateBits))) + + if len(friChallenges.FriQueryIndicies) != len(precomputedReducedEvals) { + panic("Number of queryRoundProofs should equal number of precomputedReducedEvals") + } + + for idx, xIndex := range friChallenges.FriQueryIndicies { + roundProof := friProof.QueryRoundProofs[idx] + + f.verifyQueryRound( + friChallenges, + precomputedReducedEvals, + initialMerkleCaps, + friProof, + xIndex, + n, + &roundProof, + ) + } +} diff --git a/plonky2_verifier/fri_test.go b/plonky2_verifier/fri_test.go new file mode 100644 index 0000000..e7b1a9a --- /dev/null +++ b/plonky2_verifier/fri_test.go @@ -0,0 +1,35 @@ +package plonky2_verifier + +import ( + . "gnark-ed25519/field" + "testing" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/test" +) + +type TestFriCircuit struct{} + +func (circuit *TestFriCircuit) Define(api frontend.API) error { + proofWithPis := DeserializeProofWithPublicInputs("./data/fibonacci/proof_with_public_inputs.json") + commonCircuitData := DeserializeCommonCircuitData("./data/fibonacci/common_circuit_data.json") + + field := NewFieldAPI(api) + + friChip := NewFriChip(api, field, commonCircuitData.Config.FriConfig) + friChip.VerifyFriProof(&proofWithPis.Proof.OpeningProof) + return nil +} + +func TestFriProof(t *testing.T) { + assert := test.NewAssert(t) + + testCase := func() { + circuit := TestFriCircuit{} + witness := TestFriCircuit{} + err := test.IsSolved(&circuit, &witness, TEST_CURVE.ScalarField()) + assert.NoError(err) + } + + testCase() +} diff --git a/plonky2_verifier/plonk.go b/plonky2_verifier/plonk.go index d44581c..4eb7420 100644 --- a/plonky2_verifier/plonk.go +++ b/plonky2_verifier/plonk.go @@ -180,22 +180,6 @@ func (p *PlonkChip) evalVanishingPoly(zetaPowN QuadraticExtension) []QuadraticEx return reducedValues } -func (p *PlonkChip) reduceWithPowers(terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension { - sum := p.qe.ZERO_QE - - for i := len(terms) - 1; i >= 0; i-- { - sum = p.qe.AddExtension( - p.qe.MulExtension( - sum, - scalar, - ), - terms[i], - ) - } - - return sum -} - func (p *PlonkChip) Verify() { // Calculate zeta^n zetaPowN := p.expPowerOf2Extension(p.proofChallenges.PlonkZeta) @@ -213,7 +197,8 @@ func (p *PlonkChip) Verify() { for i := 0; i < len(p.openings.QuotientPolys); i += int(p.commonData.QuotientDegreeFactor) { prod := p.qe.MulExtension( zHZeta, - p.reduceWithPowers( + reduceWithPowers( + p.qe, p.openings.QuotientPolys[i:i+int(p.commonData.QuotientDegreeFactor)], zetaPowN, ), diff --git a/plonky2_verifier/structs.go b/plonky2_verifier/structs.go index f5c5be1..14944e5 100644 --- a/plonky2_verifier/structs.go +++ b/plonky2_verifier/structs.go @@ -35,7 +35,7 @@ type PolynomialCoeffs struct { type FriProof struct { CommitPhaseMerkleCaps []MerkleCap - QueryRoundProofs FriQueryRound + QueryRoundProofs []FriQueryRound FinalPoly PolynomialCoeffs PowWitness F } diff --git a/plonky2_verifier/utils.go b/plonky2_verifier/utils.go new file mode 100644 index 0000000..3578def --- /dev/null +++ b/plonky2_verifier/utils.go @@ -0,0 +1,21 @@ +package plonky2_verifier + +import ( + . "gnark-ed25519/field" +) + +func reduceWithPowers(qe *QuadraticExtensionAPI, terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension { + sum := qe.ZERO_QE + + for i := len(terms) - 1; i >= 0; i-- { + sum = qe.AddExtension( + qe.MulExtension( + sum, + scalar, + ), + terms[i], + ) + } + + return sum +}