diff --git a/fri_benchmark.go b/fri_benchmark.go new file mode 100644 index 0000000..e66dc99 --- /dev/null +++ b/fri_benchmark.go @@ -0,0 +1,407 @@ +package main + +import ( + "fmt" + . "gnark-ed25519/field" + . "gnark-ed25519/plonky2_verifier" + "gnark-ed25519/poseidon" + "os" + "time" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" +) + +type BenchmarkLargeDummyFriCircuit struct { + zeta QuadraticExtension + openings FriOpenings + friChallenges FriChallenges + initialMerkleCaps []MerkleCap + friProof FriProof +} + +func (circuit *BenchmarkLargeDummyFriCircuit) Define(api frontend.API) error { + commonCircuitData := DeserializeCommonCircuitData("./plonky2_verifier/data/dummy_2^14_gates/common_circuit_data.json") + + field := NewFieldAPI(api) + qeAPI := NewQuadraticExtensionAPI(field, commonCircuitData.DegreeBits) + poseidonChip := poseidon.NewPoseidonChip(api, field) + friChip := NewFriChip(api, field, qeAPI, poseidonChip, &commonCircuitData.FriParams) + + friChip.VerifyFriProof( + commonCircuitData.GetFriInstance(qeAPI, circuit.zeta, commonCircuitData.DegreeBits), + circuit.openings, + &circuit.friChallenges, + circuit.initialMerkleCaps, + &circuit.friProof, + ) + + return nil +} + +func compileCircuit() frontend.CompiledConstraintSystem { + fmt.Println("compiling circuit", time.Now()) + circuit := BenchmarkLargeDummyFriCircuit{} + + /* + commonCircuitData := DeserializeCommonCircuitData("./plonky2_verifier/data/dummy_2^14_gates/common_circuit_data.json") + + circuit.zeta[0] = emulated.NewElement[EmulatedField](nil) + circuit.zeta[1] = emulated.NewElement[EmulatedField](nil) + + fmt.Println("circuit zeta allocated") + + // Batch 0 has the following openings + // Constants (config.num_constants + 1) + // Sigmas (config.num_routed_wires) + // Wires (config.num_wires) + // Plonk_Z (config.num_challenges) + // Partial Products (config.num_challenges * config.num_partial_products) + // Quotient Polynomails (config.num_challenges * config.quotient_degree_factor) + + // Batch 1 has the following openings + // Plonk_Z_next (config.num_challenges) + + circuit.openings.Batches = make([]FriOpeningBatch, 2) + + batch1Size := commonCircuitData.NumConstants + 1 + + commonCircuitData.Config.NumRoutedWires + + commonCircuitData.Config.NumWires + + commonCircuitData.Config.NumChallenges + + (commonCircuitData.Config.NumChallenges * commonCircuitData.NumPartialProducts) + + (commonCircuitData.Config.NumChallenges * commonCircuitData.QuotientDegreeFactor) + + circuit.openings.Batches[0].Values = make([]QuadraticExtension, 0) + for i := uint64(0); i < batch1Size; i++ { + circuit.openings.Batches[0].Values = append(circuit.openings.Batches[0].Values, NewEmptyQuadraticExtension()) + } + + batch2Size := commonCircuitData.Config.NumChallenges + circuit.openings.Batches[1].Values = make([]QuadraticExtension, 0) + for i := uint64(0); i < batch2Size; i++ { + circuit.openings.Batches[1].Values = append(circuit.openings.Batches[1].Values, NewEmptyQuadraticExtension()) + } + + fmt.Println("circuit openings allocated") + + circuit.friChallenges.FriAlpha = NewEmptyQuadraticExtension() + circuit.friChallenges.FriPowResponse = emulated.NewElement[EmulatedField](nil) + circuit.friChallenges.FriBetas = make([]QuadraticExtension, 0) + for i := 0; i < len(commonCircuitData.FriParams.ReductionArityBits); i++ { + circuit.friChallenges.FriBetas = append(circuit.friChallenges.FriBetas, NewEmptyQuadraticExtension()) + } + circuit.friChallenges.FriQueryIndicies = make([]F, 0) + for i := uint64(0); i < commonCircuitData.FriParams.Config.NumQueryRounds; i++ { + circuit.friChallenges.FriQueryIndicies = append(circuit.friChallenges.FriQueryIndicies, NewEmptyFieldElement()) + } + + fmt.Println("circuit challenges allocated") + + // initial merkle caps is the merkle cap for + // the constant/sigmas, wires, partial products, + // and quotient composite polynomial + // The merkle cap size is 2**cap_height hashes + numMerkleCaps := 4 + merkleCapSize := 1 << commonCircuitData.Config.FriConfig.CapHeight + circuit.initialMerkleCaps = make([]MerkleCap, 0) + for i := 0; i < numMerkleCaps; i++ { + merkleCap := make([]Hash, 0) + for j := 0; j < merkleCapSize; j++ { + merkleCap = append(merkleCap, NewEmptyHash()) + } + circuit.initialMerkleCaps = append(circuit.initialMerkleCaps, merkleCap) + } + + fmt.Println("circuit initialMerkleCaps allocated") + + // CommitPhaseMerkleCap is number of reduction_arity_bits + // finalPoly has 2^(degreeBits - sum(arity_bits)) coefficients + numCommitPhaseMerkleCaps := len(commonCircuitData.FriParams.ReductionArityBits) + for i := 0; i < numCommitPhaseMerkleCaps; i++ { + circuit.friProof.CommitPhaseMerkleCaps = make([]MerkleCap, 0) + merkleCap := make([]Hash, 0) + for j := 0; j < merkleCapSize; j++ { + merkleCap = append(merkleCap, NewEmptyHash()) + } + circuit.friProof.CommitPhaseMerkleCaps = append(circuit.friProof.CommitPhaseMerkleCaps, merkleCap) + } + + fmt.Println("circuit friproof CommitPhaseMerkleCaps allocated") + + friOracleInfo := commonCircuitData.FriOracles() + circuit.friProof.QueryRoundProofs = make([]FriQueryRound, 0) + for i := 0; i < int(commonCircuitData.FriParams.Config.NumQueryRounds); i++ { + + evalsProof := make([]EvalProof, 0) + // Allocation for the initial trees proof + for j := 0; j < len(friOracleInfo); j++ { + leafSize := friOracleInfo[0].NumPolys + merkleProofLen := commonCircuitData.DegreeBits + commonCircuitData.Config.FriConfig.RateBits - commonCircuitData.Config.FriConfig.CapHeight + + evalElements := make([]F, 0) + for k := uint64(0); k < leafSize; k++ { + evalElements = append(evalElements, NewEmptyFieldElement()) + } + + merkleProofSiblings := make([]Hash, 0) + for k := uint64(0); k < merkleProofLen; k++ { + merkleProofSiblings = append(merkleProofSiblings, NewEmptyHash()) + } + + evalsProof = append( + evalsProof, + EvalProof{ + Elements: evalElements, + MerkleProof: MerkleProof{merkleProofSiblings}, + }, + ) + } + + // Allocation for the steps + steps := make([]FriQueryStep, 0) + codewordLenBits := commonCircuitData.DegreeBits + commonCircuitData.Config.FriConfig.RateBits + for j := 0; j < len(commonCircuitData.FriParams.ReductionArityBits); j++ { + arityBits := commonCircuitData.FriParams.ReductionArityBits[j] + leafSize := 1 << int(arityBits) + codewordLenBits -= arityBits + merkleProofLen := codewordLenBits - commonCircuitData.Config.FriConfig.CapHeight + + evalQEs := make([]QuadraticExtension, 0) + for k := 0; k < leafSize; k++ { + evalQEs = append(evalQEs, NewEmptyQuadraticExtension()) + } + + merkleProofSiblings := make([]Hash, 0) + for k := uint64(0); k < merkleProofLen; k++ { + merkleProofSiblings = append(merkleProofSiblings, NewEmptyHash()) + } + + steps = append( + steps, + FriQueryStep{ + Evals: evalQEs, + MerkleProof: MerkleProof{merkleProofSiblings}, + }, + ) + } + + circuit.friProof.QueryRoundProofs = append( + circuit.friProof.QueryRoundProofs, + FriQueryRound{ + InitialTreesProof: FriInitialTreeProof{evalsProof}, + Steps: steps, + }, + ) + + fmt.Println("circuit friproof QueryRoundProofs allocated for round", i) + } + + // Final poly allocation + finalPolyLenBit := commonCircuitData.DegreeBits + for _, arityBit := range commonCircuitData.FriParams.ReductionArityBits { + finalPolyLenBit -= arityBit + } + + circuit.friProof.FinalPoly.Coeffs = make([]QuadraticExtension, 0) + for i := 0; i < (1 << finalPolyLenBit); i++ { + circuit.friProof.FinalPoly.Coeffs = append(circuit.friProof.FinalPoly.Coeffs, NewEmptyQuadraticExtension()) + } + + fmt.Println("circuit friproof FinalPoly allocated") + + // PowWitness allocation + circuit.friProof.PowWitness = NewEmptyFieldElement() + fmt.Println("Partial witness allocation done") + */ + + proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json") + verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("./plonky2_verifier/data/dummy_2^14_gates/verifier_only_circuit_data.json") + + zeta := QuadraticExtension{ + NewFieldElementFromString("17377750363769967882"), + NewFieldElementFromString("11921191651424768462"), + } + friChallenges := FriChallenges{ + FriAlpha: QuadraticExtension{ + NewFieldElementFromString("16721004555774385479"), + NewFieldElementFromString("10688151135543754663"), + }, + FriBetas: []QuadraticExtension{ + { + NewFieldElementFromString("3312441922957827805"), + NewFieldElementFromString("15128092514958289671"), + }, + { + NewFieldElementFromString("13630530769060141802"), + NewFieldElementFromString("14559883974933163008"), + }, + { + NewFieldElementFromString("16146508250083930687"), + NewFieldElementFromString("5176346568444408396"), + }, + }, + FriPowResponse: NewFieldElement(4389), + FriQueryIndicies: []F{ + NewFieldElementFromString("16334967868590615051"), + NewFieldElementFromString("2911473540496037915"), + NewFieldElementFromString("14887216056886344225"), + NewFieldElementFromString("7808811227805914295"), + NewFieldElementFromString("2018594961417375749"), + NewFieldElementFromString("3733368398777208435"), + NewFieldElementFromString("2623035669037055104"), + NewFieldElementFromString("299243030573481514"), + NewFieldElementFromString("7189789717962704433"), + NewFieldElementFromString("14566344026886816268"), + NewFieldElementFromString("12555390069003437453"), + NewFieldElementFromString("17225508403199418233"), + NewFieldElementFromString("5088797913879903292"), + NewFieldElementFromString("9715691392773433023"), + NewFieldElementFromString("7565836764713256165"), + NewFieldElementFromString("1500143546029322929"), + NewFieldElementFromString("1245802417104422080"), + NewFieldElementFromString("6831959786661245110"), + NewFieldElementFromString("17271054758535453780"), + NewFieldElementFromString("6225460404576395409"), + NewFieldElementFromString("15932661092896277351"), + NewFieldElementFromString("12452534049198240575"), + NewFieldElementFromString("4225199666055520177"), + NewFieldElementFromString("13235091290587791090"), + NewFieldElementFromString("2562357622728700774"), + NewFieldElementFromString("17676678042980201498"), + NewFieldElementFromString("5837067135702409874"), + NewFieldElementFromString("11238419549114325157"), + }, + } + + initialMerkleCaps := []MerkleCap{ + verifierOnlyCircuitData.ConstantSigmasCap, + proofWithPis.Proof.WiresCap, + proofWithPis.Proof.PlonkZsPartialProductsCap, + proofWithPis.Proof.QuotientPolysCap, + } + + circuit.zeta = zeta + circuit.openings = proofWithPis.Proof.Openings.ToFriOpenings() + circuit.friChallenges = friChallenges + circuit.initialMerkleCaps = initialMerkleCaps + circuit.friProof = proofWithPis.Proof.OpeningProof + + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + fmt.Println("error in building circuit", err) + os.Exit(1) + } + + return r1cs +} + +func createProof(r1cs frontend.CompiledConstraintSystem) groth16.Proof { + proofWithPis := DeserializeProofWithPublicInputs("./plonky2_verifier/data/dummy_2^14_gates/proof_with_public_inputs.json") + verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("./plonky2_verifier/data/dummy_2^14_gates/verifier_only_circuit_data.json") + + zeta := QuadraticExtension{ + NewFieldElementFromString("17377750363769967882"), + NewFieldElementFromString("11921191651424768462"), + } + friChallenges := FriChallenges{ + FriAlpha: QuadraticExtension{ + NewFieldElementFromString("16721004555774385479"), + NewFieldElementFromString("10688151135543754663"), + }, + FriBetas: []QuadraticExtension{ + { + NewFieldElementFromString("3312441922957827805"), + NewFieldElementFromString("15128092514958289671"), + }, + { + NewFieldElementFromString("13630530769060141802"), + NewFieldElementFromString("14559883974933163008"), + }, + { + NewFieldElementFromString("16146508250083930687"), + NewFieldElementFromString("5176346568444408396"), + }, + }, + FriPowResponse: NewFieldElement(4389), + FriQueryIndicies: []F{ + NewFieldElementFromString("16334967868590615051"), + NewFieldElementFromString("2911473540496037915"), + NewFieldElementFromString("14887216056886344225"), + NewFieldElementFromString("7808811227805914295"), + NewFieldElementFromString("2018594961417375749"), + NewFieldElementFromString("3733368398777208435"), + NewFieldElementFromString("2623035669037055104"), + NewFieldElementFromString("299243030573481514"), + NewFieldElementFromString("7189789717962704433"), + NewFieldElementFromString("14566344026886816268"), + NewFieldElementFromString("12555390069003437453"), + NewFieldElementFromString("17225508403199418233"), + NewFieldElementFromString("5088797913879903292"), + NewFieldElementFromString("9715691392773433023"), + NewFieldElementFromString("7565836764713256165"), + NewFieldElementFromString("1500143546029322929"), + NewFieldElementFromString("1245802417104422080"), + NewFieldElementFromString("6831959786661245110"), + NewFieldElementFromString("17271054758535453780"), + NewFieldElementFromString("6225460404576395409"), + NewFieldElementFromString("15932661092896277351"), + NewFieldElementFromString("12452534049198240575"), + NewFieldElementFromString("4225199666055520177"), + NewFieldElementFromString("13235091290587791090"), + NewFieldElementFromString("2562357622728700774"), + NewFieldElementFromString("17676678042980201498"), + NewFieldElementFromString("5837067135702409874"), + NewFieldElementFromString("11238419549114325157"), + }, + } + + initialMerkleCaps := []MerkleCap{ + verifierOnlyCircuitData.ConstantSigmasCap, + proofWithPis.Proof.WiresCap, + proofWithPis.Proof.PlonkZsPartialProductsCap, + proofWithPis.Proof.QuotientPolysCap, + } + + // Witness + assignment := &BenchmarkLargeDummyFriCircuit{ + zeta: zeta, + openings: proofWithPis.Proof.Openings.ToFriOpenings(), + friChallenges: friChallenges, + initialMerkleCaps: initialMerkleCaps, + friProof: proofWithPis.Proof.OpeningProof, + } + + fmt.Println("Generating witness", time.Now()) + witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + publicWitness, _ := witness.Public() + pk, vk, err := groth16.Setup(r1cs) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + fmt.Println("Creating proof", time.Now()) + proof, err := groth16.Prove(r1cs, pk, witness) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println("Verifying proof", time.Now()) + err = groth16.Verify(proof, vk, publicWitness) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + return proof +} + +func main() { + r1cs := compileCircuit() + fi, _ := os.Open("dummy_fri.r1cs") + r1cs.WriteTo(fi) + proof := createProof(r1cs) + fmt.Println(proof.CurveID(), time.Now()) +} diff --git a/plonky2_verifier/challenger.go b/plonky2_verifier/challenger.go index 266b495..955c4be 100644 --- a/plonky2_verifier/challenger.go +++ b/plonky2_verifier/challenger.go @@ -67,7 +67,7 @@ func (c *ChallengerChip) ObserveExtensionElements(elements []QuadraticExtension) func (c *ChallengerChip) ObserveOpenings(openings FriOpenings) { for i := 0; i < len(openings.Batches); i++ { - c.ObserveExtensionElements(openings.Batches[i].values) + c.ObserveExtensionElements(openings.Batches[i].Values) } } diff --git a/plonky2_verifier/fri.go b/plonky2_verifier/fri.go index c0ea26d..365cde4 100644 --- a/plonky2_verifier/fri.go +++ b/plonky2_verifier/fri.go @@ -48,7 +48,7 @@ func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExt reducedOpenings := make([]QuadraticExtension, 0, 2) for _, batch := range openings.Batches { - reducedOpenings = append(reducedOpenings, f.qeAPI.ReduceWithPowers(batch.values, alpha)) + reducedOpenings = append(reducedOpenings, f.qeAPI.ReduceWithPowers(batch.Values, alpha)) } return reducedOpenings @@ -93,6 +93,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits copy(leftSiblingState[8:12], fourZeros[0:4]) leftHash := f.poseidonChip.Poseidon(leftSiblingState) + var leftHashCompress Hash leftHashCompress[0] = leftHash[0] leftHashCompress[1] = leftHash[1] @@ -105,6 +106,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits copy(rightSiblingState[8:12], fourZeros[0:4]) rightHash := f.poseidonChip.Poseidon(rightSiblingState) + var rightHashCompress Hash rightHashCompress[0] = rightHash[0] rightHashCompress[1] = rightHash[1] @@ -116,7 +118,12 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits // We assume that the cap_height is 4. Create two levels of the Lookup2 circuit if len(capIndexBits) != 4 || len(merkleCap) != 16 { - panic("capIndexBits length should be 4 and the merkleCap length should be 16") + errorMsg, _ := fmt.Printf( + "capIndexBits length should be 4 and the merkleCap length should be 16. Actual values (capIndexBits: %d, merkleCap: %d)\n", + len(capIndexBits), + len(merkleCap), + ) + panic(errorMsg) } const NUM_LEAF_LOOKUPS = 4 diff --git a/plonky2_verifier/fri_utils.go b/plonky2_verifier/fri_utils.go index 9e5d58f..e008992 100644 --- a/plonky2_verifier/fri_utils.go +++ b/plonky2_verifier/fri_utils.go @@ -6,7 +6,7 @@ import ( ) type FriOpeningBatch struct { - values []QuadraticExtension + Values []QuadraticExtension } type FriOpenings struct { @@ -14,14 +14,14 @@ type FriOpenings struct { } func (c *OpeningSet) ToFriOpenings() FriOpenings { - values := c.Constants - values = append(values, c.PlonkSigmas...) - values = append(values, c.Wires...) - values = append(values, c.PlonkZs...) - values = append(values, c.PartialProducts...) - values = append(values, c.QuotientPolys...) - zetaBatch := FriOpeningBatch{values: values} - zetaNextBatch := FriOpeningBatch{values: c.PlonkZsNext} + values := c.Constants // num_constants + 1 + values = append(values, c.PlonkSigmas...) // num_routed_wires + values = append(values, c.Wires...) // num_wires + values = append(values, c.PlonkZs...) // num_challenges + values = append(values, c.PartialProducts...) // num_challenges * num_partial_products + values = append(values, c.QuotientPolys...) // num_challenges * quotient_degree_factor + zetaBatch := FriOpeningBatch{Values: values} + zetaNextBatch := FriOpeningBatch{Values: c.PlonkZsNext} return FriOpenings{Batches: []FriOpeningBatch{zetaBatch, zetaNextBatch}} } @@ -156,7 +156,7 @@ func (c *CommonCircuitData) GetFriInstance(qeAPI *QuadraticExtensionAPI, zeta Qu } g := field.GoldilocksPrimitiveRootOfUnity(degreeBits) - zetaNext := qeAPI.MulExtension(QuadraticExtension{field.NewFieldElement(g.Uint64()), field.ZERO_F}, zeta) + zetaNext := qeAPI.MulExtension(qeAPI.FieldToQE(field.NewFieldElement(g.Uint64())), zeta) zetaNextBath := FriBatchInfo{ Point: zetaNext, diff --git a/poseidon/poseidon.go b/poseidon/poseidon.go index e86d534..330b894 100644 --- a/poseidon/poseidon.go +++ b/poseidon/poseidon.go @@ -37,6 +37,10 @@ func (c *PoseidonChip) Poseidon(input PoseidonState) PoseidonState { func (c *PoseidonChip) HashNToMNoPad(input []F, nbOutputs int) []F { var state PoseidonState + for i := 0; i < WIDTH; i++ { + state[i] = ZERO_F + } + for i := 0; i < len(input); i += SPONGE_RATE { for j := 0; j < SPONGE_RATE; j++ { if i+j < len(input) {