From 940c81b212ecd101b32c0e9970c29a9c891d232e Mon Sep 17 00:00:00 2001 From: puma314 Date: Wed, 11 Oct 2023 18:02:46 -0700 Subject: [PATCH] Significant refactor and all tests passing, as well as optimized range check for Goldilocks (#37) --- .gitignore | 1 + README.md | 4 +- benchmark.go | 226 ++++++--- benchmark_plonk.go | 187 -------- challenger/challenger.go | 11 +- fri/fri.go | 73 ++- fri/fri_test.go | 46 +- fri/fri_utils.go | 54 --- fri/vars.go | 21 + go.mod | 26 +- go.sum | 26 ++ goldilocks/.gitignore | 1 + goldilocks/base.go | 93 ++-- goldilocks/base_test.go | 57 ++- goldilocks/quadratic_extension_test.go | 4 +- plonk/gates/evaluate_gates.go | 6 +- plonk/gates/gates_test.go | 6 +- plonk/gates/poseidon_mds_gate.go | 2 +- plonk/gates/{selectors.go => types.go} | 0 plonk/plonk.go | 35 +- plonk/plonk_test.go | 36 +- poseidon/bn254.go | 2 +- .../{bn254constants.go => bn254_constants.go} | 0 poseidon/goldilocks.go | 2 +- poseidon/goldilocks_test.go | 2 +- poseidon/public_inputs_hash_test.go | 4 +- {verifier/data => testdata}/.DS_Store | Bin .../decode_block/common_circuit_data.json | 0 .../proof_with_public_inputs.json | 0 .../verifier_only_circuit_data.json | 0 .../step/common_circuit_data.json | 0 .../step/proof_with_public_inputs.json | 0 .../step/verifier_only_circuit_data.json | 0 types/circuit.go | 51 -- types/common_data.go | 122 +++++ types/common_data_test.go | 9 + types/deserialize.go | 126 +++++ types/deserialize_test.go | 13 + types/types.go | 48 ++ types/utils.go | 21 + variables/circuit.go | 24 + variables/deserialize.go | 156 +++++++ variables/deserialize_test.go | 17 + {types => variables}/fri.go | 21 +- {types => variables}/plonk.go | 2 +- verifier/deserialize.go | 435 ------------------ verifier/deserialize_test.go | 24 - verifier/util.go | 24 + verifier/verifier.go | 104 +---- verifier/verifier_test.go | 113 +---- 50 files changed, 1089 insertions(+), 1146 deletions(-) delete mode 100644 benchmark_plonk.go create mode 100644 fri/vars.go create mode 100644 goldilocks/.gitignore rename plonk/gates/{selectors.go => types.go} (100%) rename poseidon/{bn254constants.go => bn254_constants.go} (100%) rename {verifier/data => testdata}/.DS_Store (100%) rename {verifier/data => testdata}/decode_block/common_circuit_data.json (100%) rename {verifier/data => testdata}/decode_block/proof_with_public_inputs.json (100%) rename {verifier/data => testdata}/decode_block/verifier_only_circuit_data.json (100%) rename {verifier/data => testdata}/step/common_circuit_data.json (100%) rename {verifier/data => testdata}/step/proof_with_public_inputs.json (100%) rename {verifier/data => testdata}/step/verifier_only_circuit_data.json (100%) delete mode 100644 types/circuit.go create mode 100644 types/common_data.go create mode 100644 types/common_data_test.go create mode 100644 types/deserialize.go create mode 100644 types/deserialize_test.go create mode 100644 types/types.go create mode 100644 types/utils.go create mode 100644 variables/circuit.go create mode 100644 variables/deserialize.go create mode 100644 variables/deserialize_test.go rename {types => variables}/fri.go (88%) rename {types => variables}/plonk.go (98%) delete mode 100644 verifier/deserialize.go delete mode 100644 verifier/deserialize_test.go create mode 100644 verifier/util.go diff --git a/.gitignore b/.gitignore index fd45dc0..8045102 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ # vendor/ gnark-ed25519 +gnark.pprof \ No newline at end of file diff --git a/README.md b/README.md index 1bd048b..874f1d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# gnark-plonky2-verifier +# Gnark Plonky2 Verifier -This is an in-progress implementation of a [Plonky2](https://github.com/mir-protocol/plonky2) verifier in Gnark (supports Groth16 and PLONK). It currently is able to verify some dummy circuits, but not much more as many of the custom gates used in Plonky2 are currently not implemented. +This is an implementation of a [Plonky2](https://github.com/mir-protocol/plonky2) verifier in Gnark (supports Groth16 and PLONK). Besides the verifier, there are some Gnark implementation of circuits in this repo that may be useful for other projects: diff --git a/benchmark.go b/benchmark.go index 9b60a3b..67efe6e 100644 --- a/benchmark.go +++ b/benchmark.go @@ -8,51 +8,50 @@ import ( "os" "time" - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" "github.com/succinctlabs/gnark-plonky2-verifier/verifier" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/profile" + "github.com/consensys/gnark/test" ) -type BenchmarkPlonky2VerifierCircuit struct { - Proof types.Proof - PublicInputs []gl.Variable `gnark:",public"` +func runBenchmark(plonky2Circuit string, proofSystem string, profileCircuit bool, dummy bool, saveArtifacts bool) { + commonCircuitData := types.ReadCommonCircuitData("testdata/" + plonky2Circuit + "/common_circuit_data.json") - verifierChip *verifier.VerifierChip `gnark:"-"` - plonky2CircuitName string `gnark:"-"` -} - -func (circuit *BenchmarkPlonky2VerifierCircuit) Define(api frontend.API) error { - circuitDirname := "./verifier/data/" + circuit.plonky2CircuitName + "/" - commonCircuitData := verifier.DeserializeCommonCircuitData(circuitDirname + "common_circuit_data.json") - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuitDirname + "verifier_only_circuit_data.json") - - circuit.verifierChip = verifier.NewVerifierChip(api, commonCircuitData) + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("testdata/" + plonky2Circuit + "/proof_with_public_inputs.json")) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("testdata/" + plonky2Circuit + "/verifier_only_circuit_data.json")) - circuit.verifierChip.Verify(circuit.Proof, circuit.PublicInputs, verifierOnlyCircuitData, commonCircuitData) - - return nil -} - -func compileCircuit(plonky2Circuit string, profileCircuit bool, serialize bool, outputSolidity bool) (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey) { - circuit := BenchmarkPlonky2VerifierCircuit{ - plonky2CircuitName: plonky2Circuit, + circuit := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + CommonCircuitData: commonCircuitData, } - proofWithPis := verifier.DeserializeProofWithPublicInputs("./verifier/data/" + plonky2Circuit + "/proof_with_public_inputs.json") - circuit.Proof = proofWithPis.Proof - circuit.PublicInputs = proofWithPis.PublicInputs var p *profile.Profile if profileCircuit { p = profile.Start() } - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + + var builder frontend.NewBuilder + if proofSystem == "plonk" { + builder = scs.NewBuilder + } else if proofSystem == "groth16" { + builder = r1cs.NewBuilder + } else { + fmt.Println("Please provide a valid proof system to benchmark, we only support plonk and groth16") + os.Exit(1) + } + + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), builder, &circuit) if err != nil { fmt.Println("error in building circuit", err) os.Exit(1) @@ -68,53 +67,157 @@ func compileCircuit(plonky2Circuit string, profileCircuit bool, serialize bool, println("r1cs.GetNbInternalVariables(): ", r1cs.GetNbInternalVariables()) } + if proofSystem == "plonk" { + plonkProof(r1cs, plonky2Circuit, dummy, saveArtifacts) + } else if proofSystem == "groth16" { + groth16Proof(r1cs, plonky2Circuit, dummy, saveArtifacts) + } else { + panic("Please provide a valid proof system to benchmark, we only support plonk and groth16") + } +} + +func plonkProof(r1cs constraint.ConstraintSystem, circuitName string, dummy bool, saveArtifacts bool) { + var pk plonk.ProvingKey + var vk plonk.VerifyingKey + var err error + + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("testdata/" + circuitName + "/proof_with_public_inputs.json")) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("testdata/" + circuitName + "/verifier_only_circuit_data.json")) + assignment := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + } + // Don't serialize the circuit for now, since it takes up too much memory - /* - if serialize { - fR1CS, _ := os.Create("circuit") - r1cs.WriteTo(fR1CS) - fR1CS.Close() - } - */ + // if saveArtifacts { + // fR1CS, _ := os.Create("circuit") + // r1cs.WriteTo(fR1CS) + // fR1CS.Close() + // } fmt.Println("Running circuit setup", time.Now()) - pk, vk, err := groth16.Setup(r1cs) + if dummy { + panic("dummy setup not supported for plonk") + } else { + fmt.Println("Using real setup") + srs, err := test.NewKZGSRS(r1cs) + if err != nil { + panic(err) + } + pk, vk, err = plonk.Setup(r1cs, srs) + } if err != nil { fmt.Println(err) os.Exit(1) } - if serialize { + if saveArtifacts { fPK, _ := os.Create("proving.key") pk.WriteTo(fPK) fPK.Close() - fVK, _ := os.Create("verifying.key") - vk.WriteTo(fVK) - fVK.Close() - } + if vk != nil { + fVK, _ := os.Create("verifying.key") + vk.WriteTo(fVK) + fVK.Close() + } - if outputSolidity { fSolidity, _ := os.Create("proof.sol") err = vk.ExportSolidity(fSolidity) } - return r1cs, pk, vk + fmt.Println("Generating witness", time.Now()) + witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) + publicWitness, _ := witness.Public() + if saveArtifacts { + fWitness, _ := os.Create("witness") + witness.WriteTo(fWitness) + fWitness.Close() + } + + fmt.Println("Creating proof", time.Now()) + proof, err := plonk.Prove(r1cs, pk, witness) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if saveArtifacts { + fProof, _ := os.Create("proof.proof") + proof.WriteTo(fProof) + fProof.Close() + } + + if vk == nil { + fmt.Println("vk is nil, means you're using dummy setup and we skip verification of proof") + return + } + + fmt.Println("Verifying proof", time.Now()) + err = plonk.Verify(proof, vk, publicWitness) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + const fpSize = 4 * 8 + var buf bytes.Buffer + proof.WriteRawTo(&buf) + proofBytes := buf.Bytes() + fmt.Printf("proofBytes: %v\n", proofBytes) } -func createProof(plonky2Circuit string, r1cs constraint.ConstraintSystem, pk groth16.ProvingKey, vk groth16.VerifyingKey, serialize bool) groth16.Proof { - proofWithPis := verifier.DeserializeProofWithPublicInputs("./verifier/data/" + plonky2Circuit + "/proof_with_public_inputs.json") +func groth16Proof(r1cs constraint.ConstraintSystem, circuitName string, dummy bool, saveArtifacts bool) { + var pk groth16.ProvingKey + var vk groth16.VerifyingKey + var err error + + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("testdata/" + circuitName + "/proof_with_public_inputs.json")) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("testdata/" + circuitName + "/verifier_only_circuit_data.json")) + assignment := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + } + // Don't serialize the circuit for now, since it takes up too much memory + // if saveArtifacts { + // fR1CS, _ := os.Create("circuit") + // r1cs.WriteTo(fR1CS) + // fR1CS.Close() + // } - // Witness - assignment := &BenchmarkPlonky2VerifierCircuit{ - Proof: proofWithPis.Proof, - PublicInputs: proofWithPis.PublicInputs, + fmt.Println("Running circuit setup", time.Now()) + if dummy { + fmt.Println("Using dummy setup") + pk, err = groth16.DummySetup(r1cs) + } else { + fmt.Println("Using real setup") + pk, vk, err = groth16.Setup(r1cs) + } + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if saveArtifacts { + fPK, _ := os.Create("proving.key") + pk.WriteTo(fPK) + fPK.Close() + + if vk != nil { + fVK, _ := os.Create("verifying.key") + vk.WriteTo(fVK) + fVK.Close() + } + + fSolidity, _ := os.Create("proof.sol") + err = vk.ExportSolidity(fSolidity) } fmt.Println("Generating witness", time.Now()) - witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) publicWitness, _ := witness.Public() - if serialize { + if saveArtifacts { fWitness, _ := os.Create("witness") witness.WriteTo(fWitness) fWitness.Close() @@ -126,12 +229,17 @@ func createProof(plonky2Circuit string, r1cs constraint.ConstraintSystem, pk gro fmt.Println(err) os.Exit(1) } - if serialize { + if saveArtifacts { fProof, _ := os.Create("proof.proof") proof.WriteTo(fProof) fProof.Close() } + if vk == nil { + fmt.Println("vk is nil, means you're using dummy setup and we skip verification of proof") + return + } + fmt.Println("Verifying proof", time.Now()) err = groth16.Verify(proof, vk, publicWitness) if err != nil { @@ -171,14 +279,14 @@ func createProof(plonky2Circuit string, r1cs constraint.ConstraintSystem, pk gro println("c[0] is ", c[0].String()) println("c[1] is ", c[1].String()) - return proof } func main() { plonky2Circuit := flag.String("plonky2-circuit", "", "plonky2 circuit to benchmark") - profileCircuit := flag.Bool("profile", false, "profile the circuit") - serialize := flag.Bool("serialize", false, "serialize the circuit") - outputSolidity := flag.Bool("solidity", false, "output solidity code for the circuit") + proofSystem := flag.String("proof-system", "groth16", "proof system to benchmark") + profileCircuit := flag.Bool("profile", true, "profile the circuit") + dummySetup := flag.Bool("dummy", true, "use dummy setup") + saveArtifacts := flag.Bool("save", false, "save circuit artifacts") flag.Parse() @@ -187,6 +295,12 @@ func main() { os.Exit(1) } - r1cs, pk, vk := compileCircuit(*plonky2Circuit, *profileCircuit, *serialize, *outputSolidity) - createProof(*plonky2Circuit, r1cs, pk, vk, *serialize) + if *proofSystem == "plonk" { + *dummySetup = false + } + + fmt.Printf("Running benchmark for %s circuit with proof system %s\n", *plonky2Circuit, *proofSystem) + fmt.Printf("Profiling: %t, DummySetup: %t, SaveArtifacts: %t\n", *profileCircuit, *dummySetup, *saveArtifacts) + + runBenchmark(*plonky2Circuit, *proofSystem, *profileCircuit, *dummySetup, *saveArtifacts) } diff --git a/benchmark_plonk.go b/benchmark_plonk.go deleted file mode 100644 index 6f7df12..0000000 --- a/benchmark_plonk.go +++ /dev/null @@ -1,187 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "math/big" - "os" - "time" - - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" - "github.com/succinctlabs/gnark-plonky2-verifier/types" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" - "github.com/consensys/gnark/profile" - "github.com/consensys/gnark/test" -) - -type BenchmarkPlonky2VerifierCircuitPlonk struct { - Proof types.Proof - PublicInputs []gl.Variable `gnark:",public"` - - verifierChip *verifier.VerifierChip `gnark:"-"` - plonky2CircuitName string `gnark:"-"` -} - -func (circuit *BenchmarkPlonky2VerifierCircuitPlonk) Define(api frontend.API) error { - circuitDirname := "./verifier/data/" + circuit.plonky2CircuitName + "/" - commonCircuitData := verifier.DeserializeCommonCircuitData(circuitDirname + "common_circuit_data.json") - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuitDirname + "verifier_only_circuit_data.json") - - circuit.verifierChip = verifier.NewVerifierChip(api, commonCircuitData) - - circuit.verifierChip.Verify(circuit.Proof, circuit.PublicInputs, verifierOnlyCircuitData, commonCircuitData) - - return nil -} - -func compileCircuitPlonk(plonky2Circuit string, profileCircuit bool, serialize bool, outputSolidity bool) (constraint.ConstraintSystem, plonk.ProvingKey, plonk.VerifyingKey) { - circuit := BenchmarkPlonky2VerifierCircuitPlonk{ - plonky2CircuitName: plonky2Circuit, - } - proofWithPis := verifier.DeserializeProofWithPublicInputs("./verifier/data/" + plonky2Circuit + "/proof_with_public_inputs.json") - circuit.Proof = proofWithPis.Proof - circuit.PublicInputs = proofWithPis.PublicInputs - - var p *profile.Profile - if profileCircuit { - p = profile.Start() - } - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - fmt.Println("error in building circuit", err) - os.Exit(1) - } - - if profileCircuit { - p.Stop() - p.Top() - println("r1cs.GetNbCoefficients(): ", r1cs.GetNbCoefficients()) - println("r1cs.GetNbConstraints(): ", r1cs.GetNbConstraints()) - println("r1cs.GetNbSecretVariables(): ", r1cs.GetNbSecretVariables()) - println("r1cs.GetNbPublicVariables(): ", r1cs.GetNbPublicVariables()) - println("r1cs.GetNbInternalVariables(): ", r1cs.GetNbInternalVariables()) - } - - // Don't serialize the circuit for now, since it takes up too much memory - /* - if serialize { - fR1CS, _ := os.Create("circuit") - r1cs.WriteTo(fR1CS) - fR1CS.Close() - } - */ - - srs, err := test.NewKZGSRS(r1cs) - if err != nil { - panic(err) - } - - fmt.Println("Running circuit setup", time.Now()) - pk, vk, err := plonk.Setup(r1cs, srs) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if serialize { - fPK, _ := os.Create("proving.key") - pk.WriteTo(fPK) - fPK.Close() - - fVK, _ := os.Create("verifying.key") - vk.WriteTo(fVK) - fVK.Close() - } - - if outputSolidity { - fSolidity, _ := os.Create("proof.sol") - err = vk.ExportSolidity(fSolidity) - } - - return r1cs, pk, vk -} - -func createProofPlonk(plonky2Circuit string, r1cs constraint.ConstraintSystem, pk plonk.ProvingKey, vk plonk.VerifyingKey, serialize bool) plonk.Proof { - proofWithPis := verifier.DeserializeProofWithPublicInputs("./verifier/data/" + plonky2Circuit + "/proof_with_public_inputs.json") - - // Witness - assignment := &BenchmarkPlonky2VerifierCircuitPlonk{ - Proof: proofWithPis.Proof, - PublicInputs: proofWithPis.PublicInputs, - } - - fmt.Println("Generating witness", time.Now()) - witness, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) - publicWitness, _ := witness.Public() - - fmt.Println("Creating proof", time.Now()) - proof, err := plonk.Prove(r1cs, pk, witness) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Println("Verifying proof", time.Now()) - err = plonk.Verify(proof, vk, publicWitness) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - const fpSize = 4 * 8 - var buf bytes.Buffer - proof.WriteRawTo(&buf) - proofBytes := buf.Bytes() - - var ( - a [2]*big.Int - b [2][2]*big.Int - c [2]*big.Int - ) - - // proof.Ar, proof.Bs, proof.Krs - a[0] = new(big.Int).SetBytes(proofBytes[fpSize*0 : fpSize*1]) - a[1] = new(big.Int).SetBytes(proofBytes[fpSize*1 : fpSize*2]) - b[0][0] = new(big.Int).SetBytes(proofBytes[fpSize*2 : fpSize*3]) - b[0][1] = new(big.Int).SetBytes(proofBytes[fpSize*3 : fpSize*4]) - b[1][0] = new(big.Int).SetBytes(proofBytes[fpSize*4 : fpSize*5]) - b[1][1] = new(big.Int).SetBytes(proofBytes[fpSize*5 : fpSize*6]) - c[0] = new(big.Int).SetBytes(proofBytes[fpSize*6 : fpSize*7]) - c[1] = new(big.Int).SetBytes(proofBytes[fpSize*7 : fpSize*8]) - - println("a[0] is ", a[0].String()) - println("a[1] is ", a[1].String()) - - println("b[0][0] is ", b[0][0].String()) - println("b[0][1] is ", b[0][1].String()) - println("b[1][0] is ", b[1][0].String()) - println("b[1][1] is ", b[1][1].String()) - - println("c[0] is ", c[0].String()) - println("c[1] is ", c[1].String()) - - return proof -} - -func main() { - plonky2Circuit := flag.String("plonky2-circuit", "", "plonky2 circuit to benchmark") - profileCircuit := flag.Bool("profile", false, "profile the circuit") - serialize := flag.Bool("serialize", false, "serialize the circuit") - outputSolidity := flag.Bool("solidity", false, "output solidity code for the circuit") - - flag.Parse() - - if plonky2Circuit == nil || *plonky2Circuit == "" { - fmt.Println("Please provide a plonky2 circuit to benchmark") - os.Exit(1) - } - - r1cs, pk, vk := compileCircuitPlonk(*plonky2Circuit, *profileCircuit, *serialize, *outputSolidity) - createProofPlonk(*plonky2Circuit, r1cs, pk, vk, *serialize) -} diff --git a/challenger/challenger.go b/challenger/challenger.go index b1af60b..b61f2a5 100644 --- a/challenger/challenger.go +++ b/challenger/challenger.go @@ -8,6 +8,7 @@ import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type Chip struct { @@ -113,12 +114,12 @@ func (c *Chip) GetHash() poseidon.GoldilocksHashOut { } func (c *Chip) GetFriChallenges( - commitPhaseMerkleCaps []types.FriMerkleCap, - finalPoly types.PolynomialCoeffs, + commitPhaseMerkleCaps []variables.FriMerkleCap, + finalPoly variables.PolynomialCoeffs, powWitness gl.Variable, degreeBits uint64, config types.FriConfig, -) types.FriChallenges { +) variables.FriChallenges { numFriQueries := config.NumQueryRounds friAlpha := c.GetExtensionChallenge() @@ -134,7 +135,7 @@ func (c *Chip) GetFriChallenges( friPowResponse := c.GetChallenge() friQueryIndices := c.GetNChallenges(numFriQueries) - return types.FriChallenges{ + return variables.FriChallenges{ FriAlpha: friAlpha, FriBetas: friBetas, FriPowResponse: friPowResponse, @@ -152,7 +153,7 @@ func (c *Chip) duplexing() { panic("something went wrong") } - glApi := gl.NewChip(c.api) + glApi := gl.New(c.api) for i := 0; i < len(c.inputBuffer); i++ { c.spongeState[i] = glApi.Reduce(c.inputBuffer[i]) diff --git a/fri/fri.go b/fri/fri.go index 373fe01..7fe5b43 100644 --- a/fri/fri.go +++ b/fri/fri.go @@ -11,28 +11,67 @@ import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type Chip struct { - api frontend.API `gnark:"-"` - gl gl.Chip `gnark:"-"` - poseidonBN254Chip *poseidon.BN254Chip - friParams *types.FriParams `gnark:"-"` + api frontend.API `gnark:"-"` + gl gl.Chip `gnark:"-"` + poseidonBN254Chip *poseidon.BN254Chip `gnark:"-"` + commonData *types.CommonCircuitData `gnark:"-"` + friParams *types.FriParams `gnark:"-"` } func NewChip( api frontend.API, + commonData *types.CommonCircuitData, friParams *types.FriParams, ) *Chip { poseidonBN254Chip := poseidon.NewBN254Chip(api) return &Chip{ api: api, poseidonBN254Chip: poseidonBN254Chip, + commonData: commonData, friParams: friParams, - gl: *gl.NewChip(api), + gl: *gl.New(api), } } +func (f *Chip) GetInstance(zeta gl.QuadraticExtensionVariable) InstanceInfo { + zetaBatch := BatchInfo{ + Point: zeta, + Polynomials: friAllPolys(f.commonData), + } + + g := gl.PrimitiveRootOfUnity(f.commonData.DegreeBits) + zetaNext := f.gl.MulExtension( + gl.NewVariable(g.Uint64()).ToQuadraticExtension(), + zeta, + ) + + zetaNextBath := BatchInfo{ + Point: zetaNext, + Polynomials: friZSPolys(f.commonData), + } + + return InstanceInfo{ + Oracles: friOracles(f.commonData), + Batches: []BatchInfo{zetaBatch, zetaNextBath}, + } +} + +func (f *Chip) ToOpenings(c variables.OpeningSet) Openings { + 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 := OpeningBatch{Values: values} + zetaNextBatch := OpeningBatch{Values: c.PlonkZsNext} + return Openings{Batches: []OpeningBatch{zetaBatch, zetaNextBatch}} +} + func (f *Chip) assertLeadingZeros(powWitness gl.Variable, friConfig types.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 @@ -61,8 +100,8 @@ func (f *Chip) verifyMerkleProofToCapWithCapIndex( leafData []gl.Variable, leafIndexBits []frontend.Variable, capIndexBits []frontend.Variable, - merkleCap types.FriMerkleCap, - proof *types.FriMerkleProof, + merkleCap variables.FriMerkleCap, + proof *variables.FriMerkleProof, ) { currentDigest := f.poseidonBN254Chip.HashOrNoop(leafData) for i, sibling := range proof.Siblings { @@ -100,7 +139,7 @@ func (f *Chip) verifyMerkleProofToCapWithCapIndex( f.api.AssertIsEqual(currentDigest, merkleCapEntry) } -func (f *Chip) verifyInitialProof(xIndexBits []frontend.Variable, proof *types.FriInitialTreeProof, initialMerkleCaps []types.FriMerkleCap, capIndexBits []frontend.Variable) { +func (f *Chip) verifyInitialProof(xIndexBits []frontend.Variable, proof *variables.FriInitialTreeProof, initialMerkleCaps []variables.FriMerkleCap, capIndexBits []frontend.Variable) { if len(proof.EvalsProofs) != len(initialMerkleCaps) { panic("length of eval proofs in fri proof should equal length of initial merkle caps") } @@ -187,7 +226,7 @@ func (f *Chip) calculateSubgroupX( func (f *Chip) friCombineInitial( instance InstanceInfo, - proof types.FriInitialTreeProof, + proof variables.FriInitialTreeProof, friAlpha gl.QuadraticExtensionVariable, subgroupX_QE gl.QuadraticExtensionVariable, precomputedReducedEval []gl.QuadraticExtensionVariable, @@ -228,7 +267,7 @@ func (f *Chip) friCombineInitial( return sum } -func (f *Chip) finalPolyEval(finalPoly types.PolynomialCoeffs, point gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable { +func (f *Chip) finalPolyEval(finalPoly variables.PolynomialCoeffs, point gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable { ret := gl.ZeroExtension() for i := len(finalPoly.Coeffs) - 1; i >= 0; i-- { ret = f.gl.MulAddExtension(ret, point, finalPoly.Coeffs[i]) @@ -355,14 +394,14 @@ func (f *Chip) computeEvaluation( func (f *Chip) verifyQueryRound( instance InstanceInfo, - challenges *types.FriChallenges, + challenges *variables.FriChallenges, precomputedReducedEval []gl.QuadraticExtensionVariable, - initialMerkleCaps []types.FriMerkleCap, - proof *types.FriProof, + initialMerkleCaps []variables.FriMerkleCap, + proof *variables.FriProof, xIndex gl.Variable, n uint64, nLog uint64, - roundProof *types.FriQueryRound, + roundProof *variables.FriQueryRound, ) { f.assertNoncanonicalIndicesOK() xIndex = f.gl.Reduce(xIndex) @@ -468,9 +507,9 @@ func (f *Chip) verifyQueryRound( func (f *Chip) VerifyFriProof( instance InstanceInfo, openings Openings, - friChallenges *types.FriChallenges, - initialMerkleCaps []types.FriMerkleCap, - friProof *types.FriProof, + friChallenges *variables.FriChallenges, + initialMerkleCaps []variables.FriMerkleCap, + friProof *variables.FriProof, ) { // TODO: Check fri config /* if let Some(max_arity_bits) = params.max_arity_bits() { diff --git a/fri/fri_test.go b/fri/fri_test.go index d5c0033..6634bdf 100644 --- a/fri/fri_test.go +++ b/fri/fri_test.go @@ -11,23 +11,23 @@ import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" "github.com/succinctlabs/gnark-plonky2-verifier/types" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type TestFriCircuit struct { - proofWithPIsFilename string `gnark:"-"` - commonCircuitDataFilename string `gnark:"-"` - verifierOnlyCircuitDataFilename string `gnark:"-"` + ProofWithPis variables.ProofWithPublicInputs + VerifierOnlyCircuitData variables.VerifierOnlyCircuitData + CommonCircuitData types.CommonCircuitData } func (circuit *TestFriCircuit) Define(api frontend.API) error { - proofWithPis := verifier.DeserializeProofWithPublicInputs(circuit.proofWithPIsFilename) - commonCircuitData := verifier.DeserializeCommonCircuitData(circuit.commonCircuitDataFilename) - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuit.verifierOnlyCircuitDataFilename) + commonCircuitData := circuit.CommonCircuitData + verifierOnlyCircuitData := circuit.VerifierOnlyCircuitData + proofWithPis := circuit.ProofWithPis - glApi := gl.NewChip(api) + glApi := gl.New(api) poseidonChip := poseidon.NewGoldilocksChip(api) - friChip := fri.NewChip(api, &commonCircuitData.FriParams) + friChip := fri.NewChip(api, &commonCircuitData, &commonCircuitData.FriParams) challengerChip := challenger.NewChip(api) challengerChip.ObserveBN254Hash(verifierOnlyCircuitData.CircuitDigest) @@ -46,7 +46,7 @@ func (circuit *TestFriCircuit) Define(api frontend.API) error { plonkZeta := challengerChip.GetExtensionChallenge() glApi.AssertIsEqual(plonkZeta[0], gl.NewVariable("3892795992421241388")) - challengerChip.ObserveOpenings(fri.ToOpenings(proofWithPis.Proof.Openings)) + challengerChip.ObserveOpenings(friChip.ToOpenings(proofWithPis.Proof.Openings)) friChallenges := challengerChip.GetFriChallenges( proofWithPis.Proof.OpeningProof.CommitPhaseMerkleCaps, @@ -67,7 +67,7 @@ func (circuit *TestFriCircuit) Define(api frontend.API) error { x = 11890500485816111017 api.AssertIsEqual(friChallenges.FriQueryIndices[0].Limb, x) - initialMerkleCaps := []types.FriMerkleCap{ + initialMerkleCaps := []variables.FriMerkleCap{ verifierOnlyCircuitData.ConstantSigmasCap, proofWithPis.Proof.WiresCap, proofWithPis.Proof.PlonkZsPartialProductsCap, @@ -94,8 +94,8 @@ func (circuit *TestFriCircuit) Define(api frontend.API) error { } friChip.VerifyFriProof( - fri.GetInstance(&commonCircuitData, glApi, plonkZeta, commonCircuitData.DegreeBits), - fri.ToOpenings(proofWithPis.Proof.Openings), + friChip.GetInstance(plonkZeta), + friChip.ToOpenings(proofWithPis.Proof.Openings), &friChallenges, initialMerkleCaps, &proofWithPis.Proof.OpeningProof, @@ -107,16 +107,24 @@ func (circuit *TestFriCircuit) Define(api frontend.API) error { func TestDecodeBlockFriVerification(t *testing.T) { assert := test.NewAssert(t) + proofWithPIsFilename := "../testdata/decode_block/proof_with_public_inputs.json" + commonCircuitDataFilename := "../testdata/decode_block/common_circuit_data.json" + verifierOnlyCircuitDataFilename := "../testdata/decode_block/verifier_only_circuit_data.json" + + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs(proofWithPIsFilename)) + commonCircuitData := types.ReadCommonCircuitData(commonCircuitDataFilename) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData(verifierOnlyCircuitDataFilename)) + testCase := func() { circuit := TestFriCircuit{ - proofWithPIsFilename: "../../data/decode_block/proof_with_public_inputs.json", - commonCircuitDataFilename: "../../data/decode_block//common_circuit_data.json", - verifierOnlyCircuitDataFilename: "../../data/decode_block//verifier_only_circuit_data.json", + proofWithPis, + verifierOnlyCircuitData, + commonCircuitData, } witness := TestFriCircuit{ - proofWithPIsFilename: "../../data/dummy_2^14_gates/proof_with_public_inputs.json", - commonCircuitDataFilename: "../../data/dummy_2^14_gates/common_circuit_data.json", - verifierOnlyCircuitDataFilename: ".../../data/dummy_2^14_gates/verifier_only_circuit_data.json", + proofWithPis, + verifierOnlyCircuitData, + commonCircuitData, } err := test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) assert.NoError(err) diff --git a/fri/fri_utils.go b/fri/fri_utils.go index 962a36b..dccee7e 100644 --- a/fri/fri_utils.go +++ b/fri/fri_utils.go @@ -1,30 +1,9 @@ package fri import ( - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/types" ) -type OpeningBatch struct { - Values []gl.QuadraticExtensionVariable -} - -type Openings struct { - Batches []OpeningBatch -} - -func ToOpenings(c types.OpeningSet) Openings { - 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 := OpeningBatch{Values: values} - zetaNextBatch := OpeningBatch{Values: c.PlonkZsNext} - return Openings{Batches: []OpeningBatch{zetaBatch, zetaNextBatch}} -} - type PolynomialInfo struct { OracleIndex uint64 PolynomialInfo uint64 @@ -35,16 +14,6 @@ type OracleInfo struct { Blinding bool } -type BatchInfo struct { - Point gl.QuadraticExtensionVariable - Polynomials []PolynomialInfo -} - -type InstanceInfo struct { - Oracles []OracleInfo - Batches []BatchInfo -} - type PlonkOracle struct { index uint64 blinding bool @@ -177,26 +146,3 @@ func friAllPolys(c *types.CommonCircuitData) []PolynomialInfo { return returnArr } - -func GetInstance(c *types.CommonCircuitData, glApi *gl.Chip, zeta gl.QuadraticExtensionVariable, degreeBits uint64) InstanceInfo { - zetaBatch := BatchInfo{ - Point: zeta, - Polynomials: friAllPolys(c), - } - - g := gl.PrimitiveRootOfUnity(degreeBits) - zetaNext := glApi.MulExtension( - gl.NewVariable(g.Uint64()).ToQuadraticExtension(), - zeta, - ) - - zetaNextBath := BatchInfo{ - Point: zetaNext, - Polynomials: friZSPolys(c), - } - - return InstanceInfo{ - Oracles: friOracles(c), - Batches: []BatchInfo{zetaBatch, zetaNextBath}, - } -} diff --git a/fri/vars.go b/fri/vars.go new file mode 100644 index 0000000..e3ef63b --- /dev/null +++ b/fri/vars.go @@ -0,0 +1,21 @@ +package fri + +import gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" + +type BatchInfo struct { + Point gl.QuadraticExtensionVariable + Polynomials []PolynomialInfo +} + +type InstanceInfo struct { + Oracles []OracleInfo + Batches []BatchInfo +} + +type OpeningBatch struct { + Values []gl.QuadraticExtensionVariable +} + +type Openings struct { + Batches []OpeningBatch +} diff --git a/go.mod b/go.mod index 82e406c..54d78eb 100644 --- a/go.mod +++ b/go.mod @@ -3,29 +3,29 @@ module github.com/succinctlabs/gnark-plonky2-verifier go 1.19 require ( - github.com/consensys/gnark v0.7.2-0.20230620211433-d5a7678bb74a - github.com/consensys/gnark-crypto v0.11.1-0.20230609175512-0ee617fa6d43 + github.com/consensys/gnark v0.9.0 + github.com/consensys/gnark-crypto v0.11.2 ) require ( - github.com/bits-and-blooms/bitset v1.5.0 // indirect + github.com/bits-and-blooms/bitset v1.8.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect + github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect - github.com/rs/zerolog v1.29.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/zerolog v1.30.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/sys v0.11.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect -) \ No newline at end of file +) diff --git a/go.sum b/go.sum index 9e3d146..f53e8b5 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= @@ -8,23 +10,32 @@ github.com/consensys/gnark v0.7.2-0.20230620211433-d5a7678bb74a h1:2fkBLd+kjuh0f github.com/consensys/gnark v0.7.2-0.20230620211433-d5a7678bb74a/go.mod h1:YUwaE3A1EfViSRMaJp5kyFow4rde64AKVtqRZNPkze8= github.com/consensys/gnark v0.8.0 h1:0bQ2MyDG4oNjMQpNyL8HjrrUSSL3yYJg0Elzo6LzmcU= github.com/consensys/gnark v0.8.0/go.mod h1:aKmA7dIiLbTm0OV37xTq0z+Bpe4xER8EhRLi6necrm8= +github.com/consensys/gnark v0.9.0 h1:OoOr0Q771mQINVdP3s1AF2Rs1y8gtXhWVkadz/9KmZc= +github.com/consensys/gnark v0.9.0/go.mod h1:Sy9jJjIaGJFfNeupyNOR9Ei2IbAB6cfCO78DfG27YvM= github.com/consensys/gnark-crypto v0.9.1 h1:mru55qKdWl3E035hAoh1jj9d7hVnYY5pfb6tmovSmII= github.com/consensys/gnark-crypto v0.9.1/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= github.com/consensys/gnark-crypto v0.11.1-0.20230609175512-0ee617fa6d43 h1:6VCNdjn2RmxgG2ZklMmSGov9BtCNfVF4VjqAngysiPU= github.com/consensys/gnark-crypto v0.11.1-0.20230609175512-0ee617fa6d43/go.mod h1:6C2ytC8zmP8uH2GKVfPOjf0Vw3KwMAaUxlCPK5WQqmw= +github.com/consensys/gnark-crypto v0.11.2 h1:GJjjtWJ+db1xGao7vTsOgAOGgjfPe7eRGPL+xxMX0qE= +github.com/consensys/gnark-crypto v0.11.2/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ= github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo= +github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -36,6 +47,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -44,9 +57,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -56,19 +73,28 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/goldilocks/.gitignore b/goldilocks/.gitignore new file mode 100644 index 0000000..dca82ea --- /dev/null +++ b/goldilocks/.gitignore @@ -0,0 +1 @@ +gnark.pprof \ No newline at end of file diff --git a/goldilocks/base.go b/goldilocks/base.go index ad7b0c6..393d19e 100644 --- a/goldilocks/base.go +++ b/goldilocks/base.go @@ -11,15 +11,19 @@ package goldilocks // be very beneficial to use the no reduction methods and keep track of the maximum number of bits // your computation uses. +// This implementation is based on the following plonky2 implementation of Goldilocks +// Available here: https://github.com/0xPolygonZero/plonky2/blob/main/field/src/goldilocks_field.rs#L70 + import ( "fmt" + "math" "math/big" "github.com/consensys/gnark-crypto/field/goldilocks" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/bits" "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/std/rangecheck" ) // The multiplicative group generator of the field. @@ -45,6 +49,7 @@ func init() { solver.RegisterHint(MulAddHint) solver.RegisterHint(ReduceHint) solver.RegisterHint(InverseHint) + solver.RegisterHint(SplitLimbsHint) } // A type alias used to represent Goldilocks field elements. @@ -75,12 +80,14 @@ func NegOne() Variable { // The chip used for Goldilocks field operations. type Chip struct { - api frontend.API + api frontend.API + rangeChecker frontend.Rangechecker } -// Creates a new Goldilocks chip. -func NewChip(api frontend.API) *Chip { - return &Chip{api: api} +// Creates a new Goldilocks Chip. +func New(api frontend.API) *Chip { + rangeChecker := rangecheck.New(api) + return &Chip{api: api, rangeChecker: rangeChecker} } // Adds two field elements such that x + y = z within the Golidlocks field. @@ -180,8 +187,7 @@ func (p *Chip) Reduce(x Variable) Variable { } quotient := result[0] - rangeCheckNbBits := RANGE_CHECK_NB_BITS - p.api.ToBinary(quotient, rangeCheckNbBits) + p.rangeChecker.Check(quotient, RANGE_CHECK_NB_BITS) remainder := NewVariable(result[1]) p.RangeCheck(remainder) @@ -203,7 +209,7 @@ func (p *Chip) ReduceWithMaxBits(x Variable, maxNbBits uint64) Variable { } quotient := result[0] - p.api.ToBinary(quotient, int(maxNbBits)) + p.rangeChecker.Check(quotient, int(maxNbBits)) remainder := NewVariable(result[1]) p.RangeCheck(remainder) @@ -279,6 +285,29 @@ func (p *Chip) Exp(x Variable, k *big.Int) Variable { return z } +// The hint used to split a GoldilocksVariable into 2 32 bit limbs. +func SplitLimbsHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error { + if len(inputs) != 1 { + panic("SplitLimbsHint expects 1 input operand") + } + + // The Goldilocks field element + input := inputs[0] + + if input.Cmp(MODULUS) == 0 || input.Cmp(MODULUS) == 1 { + return fmt.Errorf("input is not in the field") + } + + two_32 := big.NewInt(int64(math.Pow(2, 32))) + + // The most significant bits + results[0] = new(big.Int).Quo(input, two_32) + // The least significant bits + results[1] = new(big.Int).Rem(input, two_32) + + return nil +} + // Range checks a field element x to be less than the Golidlocks modulus 2 ^ 64 - 2 ^ 32 + 1. func (p *Chip) RangeCheck(x Variable) { // The Goldilocks' modulus is 2^64 - 2^32 + 1, which is: @@ -288,40 +317,32 @@ func (p *Chip) RangeCheck(x Variable) { // in big endian binary. This function will first verify that x is at most 64 bits wide. Then it // checks that if the bits[0:31] (in big-endian) are all 1, then bits[32:64] are all zero. - // First decompose x into 64 bits. The bits will be in little-endian order. - bits := bits.ToBinary(p.api, x.Limb, bits.WithNbDigits(64)) - - // Those bits should compose back to x. - reconstructedX := frontend.Variable(0) - c := uint64(1) - for i := 0; i < 64; i++ { - reconstructedX = p.api.Add(reconstructedX, p.api.Mul(bits[i], c)) - c = c << 1 - p.api.AssertIsBoolean(bits[i]) - } - p.api.AssertIsEqual(x.Limb, reconstructedX) - - mostSigBits32Sum := frontend.Variable(0) - for i := 32; i < 64; i++ { - mostSigBits32Sum = p.api.Add(mostSigBits32Sum, bits[i]) + result, err := p.api.Compiler().NewHint(SplitLimbsHint, 2, x.Limb) + if err != nil { + panic(err) } - leastSigBits32Sum := frontend.Variable(0) - for i := 0; i < 32; i++ { - leastSigBits32Sum = p.api.Add(leastSigBits32Sum, bits[i]) - } + // We check that this is a valid decomposition of the Goldilock's element and range-check each limb. + mostSigLimb := result[0] + leastSigLimb := result[1] + p.api.AssertIsEqual( + p.api.Add( + p.api.Mul(mostSigLimb, uint64(math.Pow(2, 32))), + leastSigLimb, + ), + x.Limb, + ) + p.rangeChecker.Check(mostSigLimb, 32) + p.rangeChecker.Check(leastSigLimb, 32) - // If mostSigBits32Sum < 32, then we know that: - // - // x < (2^63 + ... + 2^32 + 0 * 2^31 + ... + 0 * 2^0) - // - // which equals to 2^64 - 2^32. So in that case, we don't need to do any more checks. If - // mostSigBits32Sum == 32, then we need to check that x == 2^64 - 2^32 (max GL value). - shouldCheck := p.api.IsZero(p.api.Sub(mostSigBits32Sum, 32)) + // If the most significant bits are all 1, then we need to check that the least significant bits are all zero + // in order for element to be less than the Goldilock's modulus. + // Otherwise, we don't need to do any checks, since we already know that the element is less than the Goldilocks modulus. + shouldCheck := p.api.IsZero(p.api.Sub(mostSigLimb, uint64(math.Pow(2, 32))-1)) p.api.AssertIsEqual( p.api.Select( shouldCheck, - leastSigBits32Sum, + leastSigLimb, frontend.Variable(0), ), frontend.Variable(0), diff --git a/goldilocks/base_test.go b/goldilocks/base_test.go index 3a5f490..d2f6f51 100644 --- a/goldilocks/base_test.go +++ b/goldilocks/base_test.go @@ -1,12 +1,16 @@ package goldilocks import ( + "fmt" "math/big" + "os" "testing" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/scs" + "github.com/consensys/gnark/profile" "github.com/consensys/gnark/test" ) @@ -15,8 +19,8 @@ type TestGoldilocksRangeCheckCircuit struct { } func (c *TestGoldilocksRangeCheckCircuit) Define(api frontend.API) error { - chip := NewChip(api) - chip.RangeCheck(NewVariable(c.X)) + glApi := New(api) + glApi.RangeCheck(NewVariable(c.X)) return nil } func TestGoldilocksRangeCheck(t *testing.T) { @@ -25,13 +29,13 @@ func TestGoldilocksRangeCheck(t *testing.T) { var circuit, witness TestGoldilocksRangeCheckCircuit witness.X = 1 - assert.ProverSucceeded(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerialization()) + assert.ProverSucceeded(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerializationChecks()) witness.X = 0 - assert.ProverSucceeded(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerialization()) + assert.ProverSucceeded(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerializationChecks()) witness.X = MODULUS - assert.ProverFailed(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerialization()) + assert.ProverFailed(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16), test.NoSerializationChecks()) one := big.NewInt(1) maxValidVal := new(big.Int).Sub(MODULUS, one) @@ -39,14 +43,53 @@ func TestGoldilocksRangeCheck(t *testing.T) { assert.ProverSucceeded(&circuit, &witness, test.WithCurves(ecc.BN254), test.WithBackends(backend.GROTH16)) } +type TestGoldilocksRangeCheckBenchmarkCircuit struct { + X []frontend.Variable +} + +func (c *TestGoldilocksRangeCheckBenchmarkCircuit) Define(api frontend.API) error { + glApi := New(api) + for _, x := range c.X { + glApi.RangeCheck(NewVariable(x)) + glApi.Reduce(NewVariable(x)) + + } + return nil +} + +func BenchmarkGoldilocksRangeCheck(b *testing.B) { + var sizes = []int{5, 10, 15} + for i := 0; i < len(sizes); i++ { + var circuit, witness TestGoldilocksRangeCheckBenchmarkCircuit + circuit.X = make([]frontend.Variable, 2< 1) vars.RemovePrefix(numSelectors) @@ -75,7 +75,7 @@ func (g *EvaluateGatesChip) evalFiltered( } func (g *EvaluateGatesChip) EvaluateGateConstraints(vars EvaluationVars) []gl.QuadraticExtensionVariable { - glApi := gl.NewChip(g.api) + glApi := gl.New(g.api) constraints := make([]gl.QuadraticExtensionVariable, g.numGateConstraints) for i := range constraints { constraints[i] = gl.ZeroExtension() diff --git a/plonk/gates/gates_test.go b/plonk/gates/gates_test.go index 8bf99ba..8e793c9 100644 --- a/plonk/gates/gates_test.go +++ b/plonk/gates/gates_test.go @@ -11,7 +11,7 @@ import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" + "github.com/succinctlabs/gnark-plonky2-verifier/types" ) // From recursive_step circuit @@ -690,10 +690,10 @@ type TestGateCircuit struct { } func (circuit *TestGateCircuit) Define(api frontend.API) error { - commonCircuitData := verifier.DeserializeCommonCircuitData("../../data/decode_block/common_circuit_data.json") + commonCircuitData := types.ReadCommonCircuitData("../../testdata/decode_block/common_circuit_data.json") numSelectors := commonCircuitData.SelectorsInfo.NumSelectors() - glApi := gl.NewChip(api) + glApi := gl.New(api) vars := gates.NewEvaluationVars(localConstants[numSelectors:], localWires, publicInputsHash) diff --git a/plonk/gates/poseidon_mds_gate.go b/plonk/gates/poseidon_mds_gate.go index 9efb2ef..f03319d 100644 --- a/plonk/gates/poseidon_mds_gate.go +++ b/plonk/gates/poseidon_mds_gate.go @@ -45,7 +45,7 @@ func (g *PoseidonMdsGate) mdsRowShfAlgebra( v [poseidon.SPONGE_WIDTH]gl.QuadraticExtensionAlgebraVariable, api frontend.API, ) gl.QuadraticExtensionAlgebraVariable { - glApi := gl.NewChip(api) + glApi := gl.New(api) if r >= poseidon.SPONGE_WIDTH { panic("MDS row index out of range") } diff --git a/plonk/gates/selectors.go b/plonk/gates/types.go similarity index 100% rename from plonk/gates/selectors.go rename to plonk/gates/types.go diff --git a/plonk/plonk.go b/plonk/plonk.go index 47494dd..945611f 100644 --- a/plonk/plonk.go +++ b/plonk/plonk.go @@ -6,6 +6,7 @@ import ( "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type PlonkChip struct { @@ -13,9 +14,12 @@ type PlonkChip struct { commonData types.CommonCircuitData `gnark:"-"` + // These are global constant variables that we use in this Chip that we save here. + // This avoids having to recreate them every time we use them. DEGREE gl.Variable `gnark:"-"` DEGREE_BITS_F gl.Variable `gnark:"-"` DEGREE_QE gl.QuadraticExtensionVariable `gnark:"-"` + commonDataKIs []gl.Variable `gnark:"-"` evaluateGatesChip *gates.EvaluateGatesChip } @@ -23,9 +27,15 @@ type PlonkChip struct { func NewPlonkChip(api frontend.API, commonData types.CommonCircuitData) *PlonkChip { // TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64? + // Create the gates based on commonData GateIds + createdGates := []gates.Gate{} + for _, gateId := range commonData.GateIds { + createdGates = append(createdGates, gates.GateInstanceFromId(gateId)) + } + evaluateGatesChip := gates.NewEvaluateGatesChip( api, - commonData.Gates, + createdGates, commonData.NumGateConstraints, commonData.SelectorsInfo, ) @@ -38,13 +48,14 @@ func NewPlonkChip(api frontend.API, commonData types.CommonCircuitData) *PlonkCh DEGREE: gl.NewVariable(1 << commonData.DegreeBits), DEGREE_BITS_F: gl.NewVariable(commonData.DegreeBits), DEGREE_QE: gl.NewVariable(1 << commonData.DegreeBits).ToQuadraticExtension(), + commonDataKIs: gl.Uint64ArrayToVariableArray(commonData.KIs), evaluateGatesChip: evaluateGatesChip, } } func (p *PlonkChip) expPowerOf2Extension(x gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable { - glApi := gl.NewChip(p.api) + glApi := gl.New(p.api) for i := uint64(0); i < p.commonData.DegreeBits; i++ { x = glApi.MulExtension(x, x) } @@ -53,7 +64,7 @@ func (p *PlonkChip) expPowerOf2Extension(x gl.QuadraticExtensionVariable) gl.Qua func (p *PlonkChip) evalL0(x gl.QuadraticExtensionVariable, xPowN gl.QuadraticExtensionVariable) gl.QuadraticExtensionVariable { // L_0(x) = (x^n - 1) / (n * (x - 1)) - glApi := gl.NewChip(p.api) + glApi := gl.New(p.api) evalZeroPoly := glApi.SubExtension( xPowN, gl.OneExtension(), @@ -72,9 +83,9 @@ func (p *PlonkChip) checkPartialProducts( numerators []gl.QuadraticExtensionVariable, denominators []gl.QuadraticExtensionVariable, challengeNum uint64, - openings types.OpeningSet, + openings variables.OpeningSet, ) []gl.QuadraticExtensionVariable { - glApi := gl.NewChip(p.api) + glApi := gl.New(p.api) numPartProds := p.commonData.NumPartialProducts quotDegreeFactor := p.commonData.QuotientDegreeFactor @@ -106,18 +117,18 @@ func (p *PlonkChip) checkPartialProducts( func (p *PlonkChip) evalVanishingPoly( vars gates.EvaluationVars, - proofChallenges types.ProofChallenges, - openings types.OpeningSet, + proofChallenges variables.ProofChallenges, + openings variables.OpeningSet, zetaPowN gl.QuadraticExtensionVariable, ) []gl.QuadraticExtensionVariable { - glApi := gl.NewChip(p.api) + glApi := gl.New(p.api) constraintTerms := p.evaluateGatesChip.EvaluateGateConstraints(vars) // Calculate the k[i] * x sIDs := make([]gl.QuadraticExtensionVariable, p.commonData.Config.NumRoutedWires) for i := uint64(0); i < p.commonData.Config.NumRoutedWires; i++ { - sIDs[i] = glApi.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonData.KIs[i]) + sIDs[i] = glApi.ScalarMulExtension(proofChallenges.PlonkZeta, p.commonDataKIs[i]) } // Calculate L_0(zeta) @@ -193,11 +204,11 @@ func (p *PlonkChip) evalVanishingPoly( } func (p *PlonkChip) Verify( - proofChallenges types.ProofChallenges, - openings types.OpeningSet, + proofChallenges variables.ProofChallenges, + openings variables.OpeningSet, publicInputsHash poseidon.GoldilocksHashOut, ) { - glApi := gl.NewChip(p.api) + glApi := gl.New(p.api) // Calculate zeta^n zetaPowN := p.expPowerOf2Extension(proofChallenges.PlonkZeta) diff --git a/plonk/plonk_test.go b/plonk/plonk_test.go index 5cb00dc..5b27c1b 100644 --- a/plonk/plonk_test.go +++ b/plonk/plonk_test.go @@ -7,23 +7,25 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/test" "github.com/succinctlabs/gnark-plonky2-verifier/plonk" + "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" "github.com/succinctlabs/gnark-plonky2-verifier/verifier" ) type TestPlonkCircuit struct { - proofWithPIsFilename string `gnark:"-"` - commonCircuitDataFilename string `gnark:"-"` - verifierOnlyCircuitDataFilename string `gnark:"-"` + ProofWithPis variables.ProofWithPublicInputs `gnark:",public"` + VerifierOnlyCircuitData variables.VerifierOnlyCircuitData `gnark:",public"` + CommonCircuitData types.CommonCircuitData } func (circuit *TestPlonkCircuit) Define(api frontend.API) error { - proofWithPis := verifier.DeserializeProofWithPublicInputs(circuit.proofWithPIsFilename) - commonCircuitData := verifier.DeserializeCommonCircuitData(circuit.commonCircuitDataFilename) - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuit.verifierOnlyCircuitDataFilename) + commonCircuitData := circuit.CommonCircuitData + verifierOnlyCircuitData := circuit.VerifierOnlyCircuitData + proofWithPis := circuit.ProofWithPis verifierChip := verifier.NewVerifierChip(api, commonCircuitData) publicInputsHash := verifierChip.GetPublicInputsHash(proofWithPis.PublicInputs) - proofChallenges := verifierChip.GetChallenges(proofWithPis.Proof, publicInputsHash, commonCircuitData, verifierOnlyCircuitData) + proofChallenges := verifierChip.GetChallenges(proofWithPis.Proof, publicInputsHash, verifierOnlyCircuitData) plonkChip := plonk.NewPlonkChip( api, @@ -37,13 +39,25 @@ func (circuit *TestPlonkCircuit) Define(api frontend.API) error { func TestPlonkDecodeBlock(t *testing.T) { assert := test.NewAssert(t) + proofWithPIsFilename := "../testdata/decode_block/proof_with_public_inputs.json" + commonCircuitDataFilename := "../testdata/decode_block/common_circuit_data.json" + verifierOnlyCircuitDataFilename := "../testdata/decode_block/verifier_only_circuit_data.json" + + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs(proofWithPIsFilename)) + commonCircuitData := types.ReadCommonCircuitData(commonCircuitDataFilename) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData(verifierOnlyCircuitDataFilename)) + testCase := func() { circuit := TestPlonkCircuit{ - proofWithPIsFilename: "../../data/decode_block/proof_with_public_inputs.json", - commonCircuitDataFilename: "../../data/decode_block/common_circuit_data.json", - verifierOnlyCircuitDataFilename: "../../data/decode_block/verifier_only_circuit_data.json", + proofWithPis, + verifierOnlyCircuitData, + commonCircuitData, + } + witness := TestPlonkCircuit{ + proofWithPis, + verifierOnlyCircuitData, + commonCircuitData, } - witness := TestPlonkCircuit{} err := test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } diff --git a/poseidon/bn254.go b/poseidon/bn254.go index d28b7b6..48e4a21 100644 --- a/poseidon/bn254.go +++ b/poseidon/bn254.go @@ -28,7 +28,7 @@ type BN254State = [BN254_SPONGE_WIDTH]frontend.Variable type BN254HashOut = frontend.Variable func NewBN254Chip(api frontend.API) *BN254Chip { - return &BN254Chip{api: api, gl: *gl.NewChip(api)} + return &BN254Chip{api: api, gl: *gl.New(api)} } func (c *BN254Chip) Poseidon(state BN254State) BN254State { diff --git a/poseidon/bn254constants.go b/poseidon/bn254_constants.go similarity index 100% rename from poseidon/bn254constants.go rename to poseidon/bn254_constants.go diff --git a/poseidon/goldilocks.go b/poseidon/goldilocks.go index ee27bcd..7fd6c34 100644 --- a/poseidon/goldilocks.go +++ b/poseidon/goldilocks.go @@ -21,7 +21,7 @@ type GoldilocksChip struct { } func NewGoldilocksChip(api frontend.API) *GoldilocksChip { - return &GoldilocksChip{api: api, gl: *gl.NewChip(api)} + return &GoldilocksChip{api: api, gl: *gl.New(api)} } // The permutation function. diff --git a/poseidon/goldilocks_test.go b/poseidon/goldilocks_test.go index 32be542..bad7aa4 100644 --- a/poseidon/goldilocks_test.go +++ b/poseidon/goldilocks_test.go @@ -25,7 +25,7 @@ func (circuit *TestPoseidonCircuit) Define(api frontend.API) error { poseidonChip := NewGoldilocksChip(api) output := poseidonChip.Poseidon(input) - glApi := gl.NewChip(api) + glApi := gl.New(api) for i := 0; i < 12; i++ { glApi.AssertIsEqual(output[i], gl.NewVariable(circuit.Out[i])) diff --git a/poseidon/public_inputs_hash_test.go b/poseidon/public_inputs_hash_test.go index 5ff7251..2939c6d 100644 --- a/poseidon/public_inputs_hash_test.go +++ b/poseidon/public_inputs_hash_test.go @@ -18,7 +18,7 @@ type TestPublicInputsHashCircuit struct { } func (circuit *TestPublicInputsHashCircuit) Define(api frontend.API) error { - glAPI := gl.NewChip(api) + glAPI := gl.New(api) // BN254 -> Binary(64) -> F var input [3]gl.Variable @@ -78,6 +78,6 @@ func TestPublicInputsHashWitness2(t *testing.T) { test.WithBackends(backend.GROTH16), test.WithCurves(ecc.BN254), test.NoFuzzing(), - test.NoSerialization(), + test.NoSerializationChecks(), ) } diff --git a/verifier/data/.DS_Store b/testdata/.DS_Store similarity index 100% rename from verifier/data/.DS_Store rename to testdata/.DS_Store diff --git a/verifier/data/decode_block/common_circuit_data.json b/testdata/decode_block/common_circuit_data.json similarity index 100% rename from verifier/data/decode_block/common_circuit_data.json rename to testdata/decode_block/common_circuit_data.json diff --git a/verifier/data/decode_block/proof_with_public_inputs.json b/testdata/decode_block/proof_with_public_inputs.json similarity index 100% rename from verifier/data/decode_block/proof_with_public_inputs.json rename to testdata/decode_block/proof_with_public_inputs.json diff --git a/verifier/data/decode_block/verifier_only_circuit_data.json b/testdata/decode_block/verifier_only_circuit_data.json similarity index 100% rename from verifier/data/decode_block/verifier_only_circuit_data.json rename to testdata/decode_block/verifier_only_circuit_data.json diff --git a/verifier/data/step/common_circuit_data.json b/testdata/step/common_circuit_data.json similarity index 100% rename from verifier/data/step/common_circuit_data.json rename to testdata/step/common_circuit_data.json diff --git a/verifier/data/step/proof_with_public_inputs.json b/testdata/step/proof_with_public_inputs.json similarity index 100% rename from verifier/data/step/proof_with_public_inputs.json rename to testdata/step/proof_with_public_inputs.json diff --git a/verifier/data/step/verifier_only_circuit_data.json b/testdata/step/verifier_only_circuit_data.json similarity index 100% rename from verifier/data/step/verifier_only_circuit_data.json rename to testdata/step/verifier_only_circuit_data.json diff --git a/types/circuit.go b/types/circuit.go deleted file mode 100644 index 9e46d30..0000000 --- a/types/circuit.go +++ /dev/null @@ -1,51 +0,0 @@ -package types - -import ( - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" - "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" - "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" -) - -type Proof struct { - WiresCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight - PlonkZsPartialProductsCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight - QuotientPolysCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight - Openings OpeningSet - OpeningProof FriProof -} - -type ProofWithPublicInputs struct { - Proof Proof - PublicInputs []gl.Variable // Length = CommonCircuitData.NumPublicInputs -} - -type VerifierOnlyCircuitData struct { - ConstantSigmasCap FriMerkleCap - CircuitDigest poseidon.BN254HashOut -} - -type CircuitConfig struct { - NumWires uint64 - NumRoutedWires uint64 - NumConstants uint64 - UseBaseArithmeticGate bool - SecurityBits uint64 - NumChallenges uint64 - ZeroKnowledge bool - MaxQuotientDegreeFactor uint64 - FriConfig FriConfig -} - -type CommonCircuitData struct { - Config CircuitConfig - FriParams FriParams - Gates []gates.Gate - SelectorsInfo gates.SelectorsInfo - DegreeBits uint64 - QuotientDegreeFactor uint64 - NumGateConstraints uint64 - NumConstants uint64 - NumPublicInputs uint64 - KIs []gl.Variable - NumPartialProducts uint64 -} diff --git a/types/common_data.go b/types/common_data.go new file mode 100644 index 0000000..9117e89 --- /dev/null +++ b/types/common_data.go @@ -0,0 +1,122 @@ +package types + +import ( + "encoding/json" + "io" + "os" + + "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" +) + +type CommonCircuitDataRaw struct { + Config struct { + NumWires uint64 `json:"num_wires"` + NumRoutedWires uint64 `json:"num_routed_wires"` + NumConstants uint64 `json:"num_constants"` + UseBaseArithmeticGate bool `json:"use_base_arithmetic_gate"` + SecurityBits uint64 `json:"security_bits"` + NumChallenges uint64 `json:"num_challenges"` + ZeroKnowledge bool `json:"zero_knowledge"` + MaxQuotientDegreeFactor uint64 `json:"max_quotient_degree_factor"` + FriConfig struct { + RateBits uint64 `json:"rate_bits"` + CapHeight uint64 `json:"cap_height"` + ProofOfWorkBits uint64 `json:"proof_of_work_bits"` + ReductionStrategy struct { + ConstantArityBits []uint64 `json:"ConstantArityBits"` + } `json:"reduction_strategy"` + NumQueryRounds uint64 `json:"num_query_rounds"` + } `json:"fri_config"` + } `json:"config"` + FriParams struct { + Config struct { + RateBits uint64 `json:"rate_bits"` + CapHeight uint64 `json:"cap_height"` + ProofOfWorkBits uint64 `json:"proof_of_work_bits"` + ReductionStrategy struct { + ConstantArityBits []uint64 `json:"ConstantArityBits"` + } `json:"reduction_strategy"` + NumQueryRounds uint64 `json:"num_query_rounds"` + } `json:"config"` + Hiding bool `json:"hiding"` + DegreeBits uint64 `json:"degree_bits"` + ReductionArityBits []uint64 `json:"reduction_arity_bits"` + } `json:"fri_params"` + Gates []string `json:"gates"` + SelectorsInfo struct { + SelectorIndices []uint64 `json:"selector_indices"` + Groups []struct { + Start uint64 `json:"start"` + End uint64 `json:"end"` + } `json:"groups"` + } `json:"selectors_info"` + QuotientDegreeFactor uint64 `json:"quotient_degree_factor"` + NumGateConstraints uint64 `json:"num_gate_constraints"` + NumConstants uint64 `json:"num_constants"` + NumPublicInputs uint64 `json:"num_public_inputs"` + KIs []uint64 `json:"k_is"` + NumPartialProducts uint64 `json:"num_partial_products"` +} + +func ReadCommonCircuitData(path string) CommonCircuitData { + jsonFile, err := os.Open(path) + if err != nil { + panic(err) + } + + defer jsonFile.Close() + rawBytes, _ := io.ReadAll(jsonFile) + + var raw CommonCircuitDataRaw + err = json.Unmarshal(rawBytes, &raw) + if err != nil { + panic(err) + } + + var commonCircuitData CommonCircuitData + commonCircuitData.Config.NumWires = raw.Config.NumWires + commonCircuitData.Config.NumRoutedWires = raw.Config.NumRoutedWires + commonCircuitData.Config.NumConstants = raw.Config.NumConstants + commonCircuitData.Config.UseBaseArithmeticGate = raw.Config.UseBaseArithmeticGate + commonCircuitData.Config.SecurityBits = raw.Config.SecurityBits + commonCircuitData.Config.NumChallenges = raw.Config.NumChallenges + commonCircuitData.Config.ZeroKnowledge = raw.Config.ZeroKnowledge + commonCircuitData.Config.MaxQuotientDegreeFactor = raw.Config.MaxQuotientDegreeFactor + + commonCircuitData.Config.FriConfig.RateBits = raw.Config.FriConfig.RateBits + commonCircuitData.Config.FriConfig.CapHeight = raw.Config.FriConfig.CapHeight + commonCircuitData.Config.FriConfig.ProofOfWorkBits = raw.Config.FriConfig.ProofOfWorkBits + commonCircuitData.Config.FriConfig.NumQueryRounds = raw.Config.FriConfig.NumQueryRounds + + commonCircuitData.FriParams.DegreeBits = raw.FriParams.DegreeBits + commonCircuitData.DegreeBits = raw.FriParams.DegreeBits + commonCircuitData.FriParams.Config.RateBits = raw.FriParams.Config.RateBits + commonCircuitData.FriParams.Config.CapHeight = raw.FriParams.Config.CapHeight + commonCircuitData.FriParams.Config.ProofOfWorkBits = raw.FriParams.Config.ProofOfWorkBits + commonCircuitData.FriParams.Config.NumQueryRounds = raw.FriParams.Config.NumQueryRounds + commonCircuitData.FriParams.ReductionArityBits = raw.FriParams.ReductionArityBits + + commonCircuitData.GateIds = raw.Gates + + selectorGroupStart := []uint64{} + selectorGroupEnd := []uint64{} + for _, group := range raw.SelectorsInfo.Groups { + selectorGroupStart = append(selectorGroupStart, group.Start) + selectorGroupEnd = append(selectorGroupEnd, group.End) + } + + commonCircuitData.SelectorsInfo = *gates.NewSelectorsInfo( + raw.SelectorsInfo.SelectorIndices, + selectorGroupStart, + selectorGroupEnd, + ) + + commonCircuitData.QuotientDegreeFactor = raw.QuotientDegreeFactor + commonCircuitData.NumGateConstraints = raw.NumGateConstraints + commonCircuitData.NumConstants = raw.NumConstants + commonCircuitData.NumPublicInputs = raw.NumPublicInputs + commonCircuitData.KIs = raw.KIs + commonCircuitData.NumPartialProducts = raw.NumPartialProducts + + return commonCircuitData +} diff --git a/types/common_data_test.go b/types/common_data_test.go new file mode 100644 index 0000000..0215efc --- /dev/null +++ b/types/common_data_test.go @@ -0,0 +1,9 @@ +package types + +import ( + "testing" +) + +func TestReadCommonCircuitData(t *testing.T) { + ReadCommonCircuitData("../testdata/decode_block/common_circuit_data.json") +} diff --git a/types/deserialize.go b/types/deserialize.go new file mode 100644 index 0000000..4a8780b --- /dev/null +++ b/types/deserialize.go @@ -0,0 +1,126 @@ +package types + +import ( + "encoding/json" + "io" + "os" +) + +type ProofWithPublicInputsRaw struct { + Proof struct { + WiresCap []string `json:"wires_cap"` + PlonkZsPartialProductsCap []string `json:"plonk_zs_partial_products_cap"` + QuotientPolysCap []string `json:"quotient_polys_cap"` + Openings struct { + Constants [][]uint64 `json:"constants"` + PlonkSigmas [][]uint64 `json:"plonk_sigmas"` + Wires [][]uint64 `json:"wires"` + PlonkZs [][]uint64 `json:"plonk_zs"` + PlonkZsNext [][]uint64 `json:"plonk_zs_next"` + PartialProducts [][]uint64 `json:"partial_products"` + QuotientPolys [][]uint64 `json:"quotient_polys"` + } `json:"openings"` + OpeningProof struct { + CommitPhaseMerkleCaps [][]string `json:"commit_phase_merkle_caps"` + QueryRoundProofs []struct { + InitialTreesProof struct { + EvalsProofs []EvalProofRaw `json:"evals_proofs"` + } `json:"initial_trees_proof"` + Steps []struct { + Evals [][]uint64 `json:"evals"` + MerkleProof struct { + Siblings []string `json:"siblings"` + } `json:"merkle_proof"` + } `json:"steps"` + } `json:"query_round_proofs"` + FinalPoly struct { + Coeffs [][]uint64 `json:"coeffs"` + } `json:"final_poly"` + PowWitness uint64 `json:"pow_witness"` + } `json:"opening_proof"` + } `json:"proof"` + PublicInputs []uint64 `json:"public_inputs"` +} + +type EvalProofRaw struct { + LeafElements []uint64 + MerkleProof MerkleProofRaw +} + +func (e *EvalProofRaw) UnmarshalJSON(data []byte) error { + return json.Unmarshal(data, &[]interface{}{&e.LeafElements, &e.MerkleProof}) +} + +type MerkleProofRaw struct { + Hash []string +} + +func (m *MerkleProofRaw) UnmarshalJSON(data []byte) error { + type SiblingObject struct { + Siblings []string // "siblings" + } + + var siblings SiblingObject + if err := json.Unmarshal(data, &siblings); err != nil { + panic(err) + } + + m.Hash = make([]string, len(siblings.Siblings)) + copy(m.Hash[:], siblings.Siblings) + + return nil +} + +type ProofChallengesRaw struct { + PlonkBetas []uint64 `json:"plonk_betas"` + PlonkGammas []uint64 `json:"plonk_gammas"` + PlonkAlphas []uint64 `json:"plonk_alphas"` + PlonkZeta []uint64 `json:"plonk_zeta"` + FriChallenges struct { + FriAlpha []uint64 `json:"fri_alpha"` + FriBetas [][]uint64 `json:"fri_betas"` + FriPowResponse uint64 `json:"fri_pow_response"` + FriQueryIndices []uint64 `json:"fri_query_indices"` + } `json:"fri_challenges"` +} + +type VerifierOnlyCircuitDataRaw struct { + ConstantsSigmasCap []string `json:"constants_sigmas_cap"` + CircuitDigest string `json:"circuit_digest"` +} + +func ReadProofWithPublicInputs(path string) ProofWithPublicInputsRaw { + jsonFile, err := os.Open(path) + if err != nil { + panic(err) + } + + defer jsonFile.Close() + rawBytes, _ := io.ReadAll(jsonFile) + + var raw ProofWithPublicInputsRaw + err = json.Unmarshal(rawBytes, &raw) + if err != nil { + panic(err) + } + + return raw +} + +func ReadVerifierOnlyCircuitData(path string) VerifierOnlyCircuitDataRaw { + jsonFile, err := os.Open(path) + if err != nil { + panic(err) + } + + defer jsonFile.Close() + rawBytes, _ := io.ReadAll(jsonFile) + + var raw VerifierOnlyCircuitDataRaw + err = json.Unmarshal(rawBytes, &raw) + if err != nil { + panic(err) + } + + return raw +} diff --git a/types/deserialize_test.go b/types/deserialize_test.go new file mode 100644 index 0000000..236987e --- /dev/null +++ b/types/deserialize_test.go @@ -0,0 +1,13 @@ +package types + +import ( + "testing" +) + +func TestReadProofWithPublicInputs(t *testing.T) { + ReadProofWithPublicInputs("../testdata/decode_block/proof_with_public_inputs.json") +} + +func TestReadVerifierOnlyCircuitData(t *testing.T) { + ReadVerifierOnlyCircuitData("../testdata/decode_block/verifier_only_circuit_data.json") +} diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..c206e1b --- /dev/null +++ b/types/types.go @@ -0,0 +1,48 @@ +package types + +import "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" + +type FriConfig struct { + RateBits uint64 + CapHeight uint64 + ProofOfWorkBits uint64 + NumQueryRounds uint64 + // TODO: add FriReductionStrategy +} + +func (fc *FriConfig) Rate() float64 { + return 1.0 / float64((uint64(1) << fc.RateBits)) +} + +type FriParams struct { + Config FriConfig + Hiding bool + DegreeBits uint64 + ReductionArityBits []uint64 +} + +type CircuitConfig struct { + NumWires uint64 + NumRoutedWires uint64 + NumConstants uint64 + UseBaseArithmeticGate bool + SecurityBits uint64 + NumChallenges uint64 + ZeroKnowledge bool + MaxQuotientDegreeFactor uint64 + FriConfig FriConfig +} + +type CommonCircuitData struct { + Config CircuitConfig + FriParams + GateIds []string + SelectorsInfo gates.SelectorsInfo + DegreeBits uint64 + QuotientDegreeFactor uint64 + NumGateConstraints uint64 + NumConstants uint64 + NumPublicInputs uint64 + KIs []uint64 + NumPartialProducts uint64 +} diff --git a/types/utils.go b/types/utils.go new file mode 100644 index 0000000..e7bcae5 --- /dev/null +++ b/types/utils.go @@ -0,0 +1,21 @@ +package types + +func ReductionArityBits( + arityBits uint64, + finalPolyBits uint64, + degreeBits uint64, + rateBits uint64, + capHeight uint64, +) []uint64 { + returnArr := make([]uint64, 0) + + for degreeBits > finalPolyBits && degreeBits+rateBits-arityBits >= capHeight { + returnArr = append(returnArr, arityBits) + if degreeBits < arityBits { + panic("degreeBits < arityBits") + } + degreeBits -= arityBits + } + + return returnArr +} diff --git a/variables/circuit.go b/variables/circuit.go new file mode 100644 index 0000000..829d362 --- /dev/null +++ b/variables/circuit.go @@ -0,0 +1,24 @@ +package variables + +import ( + gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" + "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" +) + +type Proof struct { + WiresCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight + PlonkZsPartialProductsCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight + QuotientPolysCap FriMerkleCap // length = 2^CircuitConfig.FriConfig.CapHeight + Openings OpeningSet + OpeningProof FriProof +} + +type ProofWithPublicInputs struct { + Proof Proof + PublicInputs []gl.Variable // Length = CommonCircuitData.NumPublicInputs +} + +type VerifierOnlyCircuitData struct { + ConstantSigmasCap FriMerkleCap + CircuitDigest poseidon.BN254HashOut +} diff --git a/variables/deserialize.go b/variables/deserialize.go new file mode 100644 index 0000000..33acaf3 --- /dev/null +++ b/variables/deserialize.go @@ -0,0 +1,156 @@ +package variables + +import ( + "math/big" + + "github.com/consensys/gnark/frontend" + gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" + "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" + "github.com/succinctlabs/gnark-plonky2-verifier/types" +) + +func DeserializeMerkleCap(merkleCapRaw []string) FriMerkleCap { + n := len(merkleCapRaw) + merkleCap := make([]poseidon.BN254HashOut, n) + for i := 0; i < n; i++ { + capBigInt, _ := new(big.Int).SetString(merkleCapRaw[i], 10) + merkleCap[i] = frontend.Variable(capBigInt) + } + return merkleCap +} + +func DeserializeMerkleProof(merkleProofRaw struct{ Siblings []interface{} }) FriMerkleProof { + n := len(merkleProofRaw.Siblings) + var mp FriMerkleProof + mp.Siblings = make([]poseidon.BN254HashOut, n) + for i := 0; i < n; i++ { + element := merkleProofRaw.Siblings[i].(struct{ Elements []uint64 }) + mp.Siblings[i] = gl.Uint64ArrayToVariableArray(element.Elements) + } + return mp +} + +func DeserializeOpeningSet(openingSetRaw struct { + Constants [][]uint64 + PlonkSigmas [][]uint64 + Wires [][]uint64 + PlonkZs [][]uint64 + PlonkZsNext [][]uint64 + PartialProducts [][]uint64 + QuotientPolys [][]uint64 +}) OpeningSet { + return OpeningSet{ + Constants: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Constants), + PlonkSigmas: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkSigmas), + Wires: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Wires), + PlonkZs: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZs), + PlonkZsNext: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZsNext), + PartialProducts: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PartialProducts), + QuotientPolys: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.QuotientPolys), + } +} + +func StringArrayToHashBN254Array(rawHashes []string) []poseidon.BN254HashOut { + hashes := []poseidon.BN254HashOut{} + + for i := 0; i < len(rawHashes); i++ { + hashBigInt, _ := new(big.Int).SetString(rawHashes[i], 10) + hashVar := frontend.Variable(hashBigInt) + hashes = append(hashes, poseidon.BN254HashOut(hashVar)) + } + + return hashes +} + +func DeserializeFriProof(openingProofRaw struct { + CommitPhaseMerkleCaps [][]string + QueryRoundProofs []struct { + InitialTreesProof struct { + EvalsProofs []types.EvalProofRaw + } + Steps []struct { + Evals [][]uint64 + MerkleProof struct { + Siblings []string + } + } + } + FinalPoly struct { + Coeffs [][]uint64 + } + PowWitness uint64 +}) FriProof { + var openingProof FriProof + openingProof.PowWitness = gl.NewVariable(openingProofRaw.PowWitness) + openingProof.FinalPoly.Coeffs = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.FinalPoly.Coeffs) + + openingProof.CommitPhaseMerkleCaps = make([]FriMerkleCap, len(openingProofRaw.CommitPhaseMerkleCaps)) + for i := 0; i < len(openingProofRaw.CommitPhaseMerkleCaps); i++ { + openingProof.CommitPhaseMerkleCaps[i] = StringArrayToHashBN254Array(openingProofRaw.CommitPhaseMerkleCaps[i]) + } + + numQueryRoundProofs := len(openingProofRaw.QueryRoundProofs) + openingProof.QueryRoundProofs = make([]FriQueryRound, numQueryRoundProofs) + + for i := 0; i < numQueryRoundProofs; i++ { + numEvalProofs := len(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs) + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs = make([]FriEvalProof, numEvalProofs) + for j := 0; j < numEvalProofs; j++ { + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].Elements = gl.Uint64ArrayToVariableArray(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].LeafElements) + openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Hash) + } + + numSteps := len(openingProofRaw.QueryRoundProofs[i].Steps) + openingProof.QueryRoundProofs[i].Steps = make([]FriQueryStep, numSteps) + for j := 0; j < numSteps; j++ { + openingProof.QueryRoundProofs[i].Steps[j].Evals = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.QueryRoundProofs[i].Steps[j].Evals) + openingProof.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings) + } + } + + return openingProof +} + +func DeserializeProofWithPublicInputs(raw types.ProofWithPublicInputsRaw) ProofWithPublicInputs { + var proofWithPis ProofWithPublicInputs + proofWithPis.Proof.WiresCap = DeserializeMerkleCap(raw.Proof.WiresCap) + proofWithPis.Proof.PlonkZsPartialProductsCap = DeserializeMerkleCap(raw.Proof.PlonkZsPartialProductsCap) + proofWithPis.Proof.QuotientPolysCap = DeserializeMerkleCap(raw.Proof.QuotientPolysCap) + proofWithPis.Proof.Openings = DeserializeOpeningSet(struct { + Constants [][]uint64 + PlonkSigmas [][]uint64 + Wires [][]uint64 + PlonkZs [][]uint64 + PlonkZsNext [][]uint64 + PartialProducts [][]uint64 + QuotientPolys [][]uint64 + }(raw.Proof.Openings)) + proofWithPis.Proof.OpeningProof = DeserializeFriProof(struct { + CommitPhaseMerkleCaps [][]string + QueryRoundProofs []struct { + InitialTreesProof struct { + EvalsProofs []types.EvalProofRaw + } + Steps []struct { + Evals [][]uint64 + MerkleProof struct { + Siblings []string + } + } + } + FinalPoly struct{ Coeffs [][]uint64 } + PowWitness uint64 + }(raw.Proof.OpeningProof)) + proofWithPis.PublicInputs = gl.Uint64ArrayToVariableArray(raw.PublicInputs) + + return proofWithPis +} + +func DeserializeVerifierOnlyCircuitData(raw types.VerifierOnlyCircuitDataRaw) VerifierOnlyCircuitData { + var verifierOnlyCircuitData VerifierOnlyCircuitData + verifierOnlyCircuitData.ConstantSigmasCap = DeserializeMerkleCap(raw.ConstantsSigmasCap) + circuitDigestBigInt, _ := new(big.Int).SetString(raw.CircuitDigest, 10) + circuitDigestVar := frontend.Variable(circuitDigestBigInt) + verifierOnlyCircuitData.CircuitDigest = poseidon.BN254HashOut(circuitDigestVar) + return verifierOnlyCircuitData +} diff --git a/variables/deserialize_test.go b/variables/deserialize_test.go new file mode 100644 index 0000000..a1d9017 --- /dev/null +++ b/variables/deserialize_test.go @@ -0,0 +1,17 @@ +package variables + +import ( + "testing" + + "github.com/succinctlabs/gnark-plonky2-verifier/types" +) + +func TestDeserializeProofWithPublicInputs(t *testing.T) { + proofWithPis := DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("../testdata/decode_block/proof_with_public_inputs.json")) + t.Logf("%+v\n", proofWithPis) +} + +func TestDeserializeVerifierOnlyCircuitData(t *testing.T) { + verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("../testdata/decode_block/verifier_only_circuit_data.json")) + t.Logf("%+v\n", verifierOnlyCircuitData) +} diff --git a/types/fri.go b/variables/fri.go similarity index 88% rename from types/fri.go rename to variables/fri.go index aaea0f3..3042a87 100644 --- a/types/fri.go +++ b/variables/fri.go @@ -1,4 +1,4 @@ -package types +package variables import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" @@ -13,25 +13,6 @@ func NewPolynomialCoeffs(numCoeffs uint64) PolynomialCoeffs { return PolynomialCoeffs{Coeffs: make([]gl.QuadraticExtensionVariable, numCoeffs)} } -type FriConfig struct { - RateBits uint64 - CapHeight uint64 - ProofOfWorkBits uint64 - NumQueryRounds uint64 - // TODO: add FriReductionStrategy -} - -func (fc *FriConfig) Rate() float64 { - return 1.0 / float64((uint64(1) << fc.RateBits)) -} - -type FriParams struct { - Config FriConfig - Hiding bool - DegreeBits uint64 - ReductionArityBits []uint64 -} - type FriMerkleCap = []poseidon.BN254HashOut func NewFriMerkleCap(capHeight uint64) FriMerkleCap { diff --git a/types/plonk.go b/variables/plonk.go similarity index 98% rename from types/plonk.go rename to variables/plonk.go index f0b03b9..1fc30f7 100644 --- a/types/plonk.go +++ b/variables/plonk.go @@ -1,4 +1,4 @@ -package types +package variables import gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" diff --git a/verifier/deserialize.go b/verifier/deserialize.go deleted file mode 100644 index ac52565..0000000 --- a/verifier/deserialize.go +++ /dev/null @@ -1,435 +0,0 @@ -package verifier - -import ( - "encoding/json" - "io" - "math/big" - "os" - - "github.com/consensys/gnark/frontend" - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" - "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" - "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" - "github.com/succinctlabs/gnark-plonky2-verifier/types" -) - -type ProofWithPublicInputsRaw struct { - Proof struct { - WiresCap []string `json:"wires_cap"` - PlonkZsPartialProductsCap []string `json:"plonk_zs_partial_products_cap"` - QuotientPolysCap []string `json:"quotient_polys_cap"` - Openings struct { - Constants [][]uint64 `json:"constants"` - PlonkSigmas [][]uint64 `json:"plonk_sigmas"` - Wires [][]uint64 `json:"wires"` - PlonkZs [][]uint64 `json:"plonk_zs"` - PlonkZsNext [][]uint64 `json:"plonk_zs_next"` - PartialProducts [][]uint64 `json:"partial_products"` - QuotientPolys [][]uint64 `json:"quotient_polys"` - } `json:"openings"` - OpeningProof struct { - CommitPhaseMerkleCaps [][]string `json:"commit_phase_merkle_caps"` - QueryRoundProofs []struct { - InitialTreesProof struct { - EvalsProofs []EvalProofRaw `json:"evals_proofs"` - } `json:"initial_trees_proof"` - Steps []struct { - Evals [][]uint64 `json:"evals"` - MerkleProof struct { - Siblings []string `json:"siblings"` - } `json:"merkle_proof"` - } `json:"steps"` - } `json:"query_round_proofs"` - FinalPoly struct { - Coeffs [][]uint64 `json:"coeffs"` - } `json:"final_poly"` - PowWitness uint64 `json:"pow_witness"` - } `json:"opening_proof"` - } `json:"proof"` - PublicInputs []uint64 `json:"public_inputs"` -} - -type EvalProofRaw struct { - LeafElements []uint64 - MerkleProof MerkleProofRaw -} - -func (e *EvalProofRaw) UnmarshalJSON(data []byte) error { - return json.Unmarshal(data, &[]interface{}{&e.LeafElements, &e.MerkleProof}) -} - -type MerkleProofRaw struct { - Hash []string -} - -func (m *MerkleProofRaw) UnmarshalJSON(data []byte) error { - type SiblingObject struct { - Siblings []string // "siblings" - } - - var siblings SiblingObject - if err := json.Unmarshal(data, &siblings); err != nil { - panic(err) - } - - m.Hash = make([]string, len(siblings.Siblings)) - copy(m.Hash[:], siblings.Siblings) - - return nil -} - -type CommonCircuitDataRaw struct { - Config struct { - NumWires uint64 `json:"num_wires"` - NumRoutedWires uint64 `json:"num_routed_wires"` - NumConstants uint64 `json:"num_constants"` - UseBaseArithmeticGate bool `json:"use_base_arithmetic_gate"` - SecurityBits uint64 `json:"security_bits"` - NumChallenges uint64 `json:"num_challenges"` - ZeroKnowledge bool `json:"zero_knowledge"` - MaxQuotientDegreeFactor uint64 `json:"max_quotient_degree_factor"` - FriConfig struct { - RateBits uint64 `json:"rate_bits"` - CapHeight uint64 `json:"cap_height"` - ProofOfWorkBits uint64 `json:"proof_of_work_bits"` - ReductionStrategy struct { - ConstantArityBits []uint64 `json:"ConstantArityBits"` - } `json:"reduction_strategy"` - NumQueryRounds uint64 `json:"num_query_rounds"` - } `json:"fri_config"` - } `json:"config"` - FriParams struct { - Config struct { - RateBits uint64 `json:"rate_bits"` - CapHeight uint64 `json:"cap_height"` - ProofOfWorkBits uint64 `json:"proof_of_work_bits"` - ReductionStrategy struct { - ConstantArityBits []uint64 `json:"ConstantArityBits"` - } `json:"reduction_strategy"` - NumQueryRounds uint64 `json:"num_query_rounds"` - } `json:"config"` - Hiding bool `json:"hiding"` - DegreeBits uint64 `json:"degree_bits"` - ReductionArityBits []uint64 `json:"reduction_arity_bits"` - } `json:"fri_params"` - Gates []string `json:"gates"` - SelectorsInfo struct { - SelectorIndices []uint64 `json:"selector_indices"` - Groups []struct { - Start uint64 `json:"start"` - End uint64 `json:"end"` - } `json:"groups"` - } `json:"selectors_info"` - QuotientDegreeFactor uint64 `json:"quotient_degree_factor"` - NumGateConstraints uint64 `json:"num_gate_constraints"` - NumConstants uint64 `json:"num_constants"` - NumPublicInputs uint64 `json:"num_public_inputs"` - KIs []uint64 `json:"k_is"` - NumPartialProducts uint64 `json:"num_partial_products"` -} - -type ProofChallengesRaw struct { - PlonkBetas []uint64 `json:"plonk_betas"` - PlonkGammas []uint64 `json:"plonk_gammas"` - PlonkAlphas []uint64 `json:"plonk_alphas"` - PlonkZeta []uint64 `json:"plonk_zeta"` - FriChallenges struct { - FriAlpha []uint64 `json:"fri_alpha"` - FriBetas [][]uint64 `json:"fri_betas"` - FriPowResponse uint64 `json:"fri_pow_response"` - FriQueryIndices []uint64 `json:"fri_query_indices"` - } `json:"fri_challenges"` -} - -type VerifierOnlyCircuitDataRaw struct { - ConstantsSigmasCap []string `json:"constants_sigmas_cap"` - CircuitDigest string `json:"circuit_digest"` -} - -func DeserializeMerkleCap(merkleCapRaw []string) types.FriMerkleCap { - n := len(merkleCapRaw) - merkleCap := make([]poseidon.BN254HashOut, n) - for i := 0; i < n; i++ { - capBigInt, _ := new(big.Int).SetString(merkleCapRaw[i], 10) - merkleCap[i] = frontend.Variable(capBigInt) - } - return merkleCap -} - -func DeserializeMerkleProof(merkleProofRaw struct{ Siblings []interface{} }) types.FriMerkleProof { - n := len(merkleProofRaw.Siblings) - var mp types.FriMerkleProof - mp.Siblings = make([]poseidon.BN254HashOut, n) - for i := 0; i < n; i++ { - element := merkleProofRaw.Siblings[i].(struct{ Elements []uint64 }) - mp.Siblings[i] = gl.Uint64ArrayToVariableArray(element.Elements) - } - return mp -} - -func DeserializeOpeningSet(openingSetRaw struct { - Constants [][]uint64 - PlonkSigmas [][]uint64 - Wires [][]uint64 - PlonkZs [][]uint64 - PlonkZsNext [][]uint64 - PartialProducts [][]uint64 - QuotientPolys [][]uint64 -}) types.OpeningSet { - return types.OpeningSet{ - Constants: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Constants), - PlonkSigmas: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkSigmas), - Wires: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.Wires), - PlonkZs: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZs), - PlonkZsNext: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PlonkZsNext), - PartialProducts: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.PartialProducts), - QuotientPolys: gl.Uint64ArrayToQuadraticExtensionArray(openingSetRaw.QuotientPolys), - } -} - -func StringArrayToHashBN254Array(rawHashes []string) []poseidon.BN254HashOut { - hashes := []poseidon.BN254HashOut{} - - for i := 0; i < len(rawHashes); i++ { - hashBigInt, _ := new(big.Int).SetString(rawHashes[i], 10) - hashVar := frontend.Variable(hashBigInt) - hashes = append(hashes, poseidon.BN254HashOut(hashVar)) - } - - return hashes -} - -func DeserializeFriProof(openingProofRaw struct { - CommitPhaseMerkleCaps [][]string - QueryRoundProofs []struct { - InitialTreesProof struct { - EvalsProofs []EvalProofRaw - } - Steps []struct { - Evals [][]uint64 - MerkleProof struct { - Siblings []string - } - } - } - FinalPoly struct { - Coeffs [][]uint64 - } - PowWitness uint64 -}) types.FriProof { - var openingProof types.FriProof - openingProof.PowWitness = gl.NewVariable(openingProofRaw.PowWitness) - openingProof.FinalPoly.Coeffs = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.FinalPoly.Coeffs) - - openingProof.CommitPhaseMerkleCaps = make([]types.FriMerkleCap, len(openingProofRaw.CommitPhaseMerkleCaps)) - for i := 0; i < len(openingProofRaw.CommitPhaseMerkleCaps); i++ { - openingProof.CommitPhaseMerkleCaps[i] = StringArrayToHashBN254Array(openingProofRaw.CommitPhaseMerkleCaps[i]) - } - - numQueryRoundProofs := len(openingProofRaw.QueryRoundProofs) - openingProof.QueryRoundProofs = make([]types.FriQueryRound, numQueryRoundProofs) - - for i := 0; i < numQueryRoundProofs; i++ { - numEvalProofs := len(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs) - openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs = make([]types.FriEvalProof, numEvalProofs) - for j := 0; j < numEvalProofs; j++ { - openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].Elements = gl.Uint64ArrayToVariableArray(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].LeafElements) - openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs[j].MerkleProof.Hash) - } - - numSteps := len(openingProofRaw.QueryRoundProofs[i].Steps) - openingProof.QueryRoundProofs[i].Steps = make([]types.FriQueryStep, numSteps) - for j := 0; j < numSteps; j++ { - openingProof.QueryRoundProofs[i].Steps[j].Evals = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.QueryRoundProofs[i].Steps[j].Evals) - openingProof.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings = StringArrayToHashBN254Array(openingProofRaw.QueryRoundProofs[i].Steps[j].MerkleProof.Siblings) - } - } - - return openingProof -} - -func DeserializeProofWithPublicInputs(path string) types.ProofWithPublicInputs { - jsonFile, err := os.Open(path) - if err != nil { - panic(err) - } - - defer jsonFile.Close() - rawBytes, _ := io.ReadAll(jsonFile) - - var raw ProofWithPublicInputsRaw - err = json.Unmarshal(rawBytes, &raw) - if err != nil { - panic(err) - } - - var proofWithPis types.ProofWithPublicInputs - proofWithPis.Proof.WiresCap = DeserializeMerkleCap(raw.Proof.WiresCap) - proofWithPis.Proof.PlonkZsPartialProductsCap = DeserializeMerkleCap(raw.Proof.PlonkZsPartialProductsCap) - proofWithPis.Proof.QuotientPolysCap = DeserializeMerkleCap(raw.Proof.QuotientPolysCap) - proofWithPis.Proof.Openings = DeserializeOpeningSet(struct { - Constants [][]uint64 - PlonkSigmas [][]uint64 - Wires [][]uint64 - PlonkZs [][]uint64 - PlonkZsNext [][]uint64 - PartialProducts [][]uint64 - QuotientPolys [][]uint64 - }(raw.Proof.Openings)) - proofWithPis.Proof.OpeningProof = DeserializeFriProof(struct { - CommitPhaseMerkleCaps [][]string - QueryRoundProofs []struct { - InitialTreesProof struct { - EvalsProofs []EvalProofRaw - } - Steps []struct { - Evals [][]uint64 - MerkleProof struct { - Siblings []string - } - } - } - FinalPoly struct{ Coeffs [][]uint64 } - PowWitness uint64 - }(raw.Proof.OpeningProof)) - proofWithPis.PublicInputs = gl.Uint64ArrayToVariableArray(raw.PublicInputs) - - return proofWithPis -} - -func DeserializeProofChallenges(path string) types.ProofChallenges { - jsonFile, err := os.Open(path) - if err != nil { - panic(err) - } - - defer jsonFile.Close() - rawBytes, _ := io.ReadAll(jsonFile) - - var raw ProofChallengesRaw - err = json.Unmarshal(rawBytes, &raw) - if err != nil { - panic(err) - } - - var proofChallenges types.ProofChallenges - proofChallenges.PlonkBetas = gl.Uint64ArrayToVariableArray(raw.PlonkBetas) - proofChallenges.PlonkGammas = gl.Uint64ArrayToVariableArray(raw.PlonkGammas) - proofChallenges.PlonkAlphas = gl.Uint64ArrayToVariableArray(raw.PlonkAlphas) - proofChallenges.PlonkZeta = gl.Uint64ArrayToQuadraticExtension(raw.PlonkZeta) - proofChallenges.FriChallenges.FriAlpha = gl.Uint64ArrayToQuadraticExtension(raw.FriChallenges.FriAlpha) - proofChallenges.FriChallenges.FriBetas = gl.Uint64ArrayToQuadraticExtensionArray(raw.FriChallenges.FriBetas) - proofChallenges.FriChallenges.FriPowResponse = gl.NewVariable(raw.FriChallenges.FriPowResponse) - proofChallenges.FriChallenges.FriQueryIndices = gl.Uint64ArrayToVariableArray(raw.FriChallenges.FriQueryIndices) - - return proofChallenges -} - -func ReductionArityBits( - arityBits uint64, - finalPolyBits uint64, - degreeBits uint64, - rateBits uint64, - capHeight uint64, -) []uint64 { - returnArr := make([]uint64, 0) - - for degreeBits > finalPolyBits && degreeBits+rateBits-arityBits >= capHeight { - returnArr = append(returnArr, arityBits) - if degreeBits < arityBits { - panic("degreeBits < arityBits") - } - degreeBits -= arityBits - } - - return returnArr -} - -func DeserializeCommonCircuitData(path string) types.CommonCircuitData { - jsonFile, err := os.Open(path) - if err != nil { - panic(err) - } - - defer jsonFile.Close() - rawBytes, _ := io.ReadAll(jsonFile) - - var raw CommonCircuitDataRaw - err = json.Unmarshal(rawBytes, &raw) - if err != nil { - panic(err) - } - - var commonCircuitData types.CommonCircuitData - commonCircuitData.Config.NumWires = raw.Config.NumWires - commonCircuitData.Config.NumRoutedWires = raw.Config.NumRoutedWires - commonCircuitData.Config.NumConstants = raw.Config.NumConstants - commonCircuitData.Config.UseBaseArithmeticGate = raw.Config.UseBaseArithmeticGate - commonCircuitData.Config.SecurityBits = raw.Config.SecurityBits - commonCircuitData.Config.NumChallenges = raw.Config.NumChallenges - commonCircuitData.Config.ZeroKnowledge = raw.Config.ZeroKnowledge - commonCircuitData.Config.MaxQuotientDegreeFactor = raw.Config.MaxQuotientDegreeFactor - - commonCircuitData.Config.FriConfig.RateBits = raw.Config.FriConfig.RateBits - commonCircuitData.Config.FriConfig.CapHeight = raw.Config.FriConfig.CapHeight - commonCircuitData.Config.FriConfig.ProofOfWorkBits = raw.Config.FriConfig.ProofOfWorkBits - commonCircuitData.Config.FriConfig.NumQueryRounds = raw.Config.FriConfig.NumQueryRounds - - commonCircuitData.FriParams.DegreeBits = raw.FriParams.DegreeBits - commonCircuitData.DegreeBits = raw.FriParams.DegreeBits - commonCircuitData.FriParams.Config.RateBits = raw.FriParams.Config.RateBits - commonCircuitData.FriParams.Config.CapHeight = raw.FriParams.Config.CapHeight - commonCircuitData.FriParams.Config.ProofOfWorkBits = raw.FriParams.Config.ProofOfWorkBits - commonCircuitData.FriParams.Config.NumQueryRounds = raw.FriParams.Config.NumQueryRounds - commonCircuitData.FriParams.ReductionArityBits = raw.FriParams.ReductionArityBits - - commonCircuitData.Gates = []gates.Gate{} - for _, gate := range raw.Gates { - commonCircuitData.Gates = append(commonCircuitData.Gates, gates.GateInstanceFromId(gate)) - } - - selectorGroupStart := []uint64{} - selectorGroupEnd := []uint64{} - for _, group := range raw.SelectorsInfo.Groups { - selectorGroupStart = append(selectorGroupStart, group.Start) - selectorGroupEnd = append(selectorGroupEnd, group.End) - } - - commonCircuitData.SelectorsInfo = *gates.NewSelectorsInfo( - raw.SelectorsInfo.SelectorIndices, - selectorGroupStart, - selectorGroupEnd, - ) - - commonCircuitData.QuotientDegreeFactor = raw.QuotientDegreeFactor - commonCircuitData.NumGateConstraints = raw.NumGateConstraints - commonCircuitData.NumConstants = raw.NumConstants - commonCircuitData.NumPublicInputs = raw.NumPublicInputs - commonCircuitData.KIs = gl.Uint64ArrayToVariableArray(raw.KIs) - commonCircuitData.NumPartialProducts = raw.NumPartialProducts - - return commonCircuitData -} - -func DeserializeVerifierOnlyCircuitData(path string) types.VerifierOnlyCircuitData { - jsonFile, err := os.Open(path) - if err != nil { - panic(err) - } - - defer jsonFile.Close() - rawBytes, _ := io.ReadAll(jsonFile) - - var raw VerifierOnlyCircuitDataRaw - err = json.Unmarshal(rawBytes, &raw) - if err != nil { - panic(err) - } - - var verifierOnlyCircuitData types.VerifierOnlyCircuitData - verifierOnlyCircuitData.ConstantSigmasCap = DeserializeMerkleCap(raw.ConstantsSigmasCap) - circuitDigestBigInt, _ := new(big.Int).SetString(raw.CircuitDigest, 10) - circuitDigestVar := frontend.Variable(circuitDigestBigInt) - verifierOnlyCircuitData.CircuitDigest = poseidon.BN254HashOut(circuitDigestVar) - return verifierOnlyCircuitData -} diff --git a/verifier/deserialize_test.go b/verifier/deserialize_test.go deleted file mode 100644 index 0151764..0000000 --- a/verifier/deserialize_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package verifier - -import ( - "fmt" - "testing" -) - -func TestDeserializeProofWithPublicInputs(t *testing.T) { - proofWithPis := DeserializeProofWithPublicInputs("../data/decode_block/proof_with_public_inputs.json") - fmt.Printf("%+v\n", proofWithPis) - panic("look at stdout") -} - -func TestDeserializeCommonCircuitData(t *testing.T) { - commonCircuitData := DeserializeCommonCircuitData("../data/decode_block/common_circuit_data.json") - fmt.Printf("%+v\n", commonCircuitData) - panic("look at stdout") -} - -func TestDeserializeVerifierOnlyCircuitData(t *testing.T) { - verifierOnlyCircuitData := DeserializeVerifierOnlyCircuitData("../data/decode_block/verifier_only_circuit_data.json") - fmt.Printf("%+v\n", verifierOnlyCircuitData) - panic("look at stdout") -} diff --git a/verifier/util.go b/verifier/util.go new file mode 100644 index 0000000..150ada5 --- /dev/null +++ b/verifier/util.go @@ -0,0 +1,24 @@ +package verifier + +import ( + "github.com/consensys/gnark/frontend" + gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" + "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" +) + +type ExampleVerifierCircuit struct { + PublicInputs []gl.Variable `gnark:",public"` + Proof variables.Proof `gnark:"-"` + VerifierOnlyCircuitData variables.VerifierOnlyCircuitData `gnark:"-"` + + // This is configuration for the circuit, it is a constant not a variable + CommonCircuitData types.CommonCircuitData +} + +func (c *ExampleVerifierCircuit) Define(api frontend.API) error { + verifierChip := NewVerifierChip(api, c.CommonCircuitData) + verifierChip.Verify(c.Proof, c.PublicInputs, c.VerifierOnlyCircuitData) + + return nil +} diff --git a/verifier/verifier.go b/verifier/verifier.go index ca0d0f7..2ec08e5 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -8,6 +8,7 @@ import ( "github.com/succinctlabs/gnark-plonky2-verifier/plonk" "github.com/succinctlabs/gnark-plonky2-verifier/poseidon" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type VerifierChip struct { @@ -17,11 +18,12 @@ type VerifierChip struct { poseidonBN254Chip *poseidon.BN254Chip `gnark:"-"` plonkChip *plonk.PlonkChip `gnark:"-"` friChip *fri.Chip `gnark:"-"` + commonData types.CommonCircuitData `gnark:"-"` } func NewVerifierChip(api frontend.API, commonCircuitData types.CommonCircuitData) *VerifierChip { - glChip := gl.NewChip(api) - friChip := fri.NewChip(api, &commonCircuitData.FriParams) + glChip := gl.New(api) + friChip := fri.NewChip(api, &commonCircuitData, &commonCircuitData.FriParams) plonkChip := plonk.NewPlonkChip(api, commonCircuitData) poseidonGlChip := poseidon.NewGoldilocksChip(api) poseidonBN254Chip := poseidon.NewBN254Chip(api) @@ -32,6 +34,7 @@ func NewVerifierChip(api frontend.API, commonCircuitData types.CommonCircuitData poseidonBN254Chip: poseidonBN254Chip, plonkChip: plonkChip, friChip: friChip, + commonData: commonCircuitData, } } @@ -40,12 +43,11 @@ func (c *VerifierChip) GetPublicInputsHash(publicInputs []gl.Variable) poseidon. } func (c *VerifierChip) GetChallenges( - proof types.Proof, + proof variables.Proof, publicInputsHash poseidon.GoldilocksHashOut, - commonData types.CommonCircuitData, - verifierData types.VerifierOnlyCircuitData, -) types.ProofChallenges { - config := commonData.Config + verifierData variables.VerifierOnlyCircuitData, +) variables.ProofChallenges { + config := c.commonData.Config numChallenges := config.NumChallenges challenger := challenger.NewChip(c.api) @@ -63,9 +65,9 @@ func (c *VerifierChip) GetChallenges( challenger.ObserveCap(proof.QuotientPolysCap) plonkZeta := challenger.GetExtensionChallenge() - challenger.ObserveOpenings(fri.ToOpenings(proof.Openings)) + challenger.ObserveOpenings(c.friChip.ToOpenings(proof.Openings)) - return types.ProofChallenges{ + return variables.ProofChallenges{ PlonkBetas: plonkBetas, PlonkGammas: plonkGammas, PlonkAlphas: plonkAlphas, @@ -74,80 +76,13 @@ func (c *VerifierChip) GetChallenges( proof.OpeningProof.CommitPhaseMerkleCaps, proof.OpeningProof.FinalPoly, proof.OpeningProof.PowWitness, - commonData.DegreeBits, + c.commonData.DegreeBits, config.FriConfig, ), } } -/* -func (c *VerifierChip) generateProofInput(commonData common.CommonCircuitData) common.ProofWithPublicInputs { - // Generate the parts of the witness that is for the plonky2 proof input - - capHeight := commonData.Config.FriConfig.CapHeight - - friCommitPhaseMerkleCaps := []common.MerkleCap{} - for i := 0; i < len(commonData.FriParams.ReductionArityBits); i++ { - friCommitPhaseMerkleCaps = append(friCommitPhaseMerkleCaps, common.NewMerkleCap(capHeight)) - } - - salt := commonData.SaltSize() - numLeavesPerOracle := []uint{ - commonData.NumPreprocessedPolys(), - commonData.Config.NumWires + salt, - commonData.NumZsPartialProductsPolys() + salt, - commonData.NumQuotientPolys() + salt, - } - friQueryRoundProofs := []common.FriQueryRound{} - for i := uint64(0); i < commonData.FriParams.Config.NumQueryRounds; i++ { - evalProofs := []common.EvalProof{} - merkleProofLen := commonData.FriParams.LDEBits() - capHeight - for _, numLeaves := range numLeavesPerOracle { - leaves := make([]field.F, numLeaves) - merkleProof := common.NewMerkleProof(merkleProofLen) - evalProofs = append(evalProofs, common.NewEvalProof(leaves, merkleProof)) - } - - initialTreesProof := common.NewFriInitialTreeProof(evalProofs) - steps := []common.FriQueryStep{} - for _, arityBit := range commonData.FriParams.ReductionArityBits { - if merkleProofLen < arityBit { - panic("merkleProofLen < arityBits") - } - - steps = append(steps, common.NewFriQueryStep(arityBit, merkleProofLen)) - } - - friQueryRoundProofs = append(friQueryRoundProofs, common.NewFriQueryRound(steps, initialTreesProof)) - } - - proofInput := common.ProofWithPublicInputs{ - Proof: common.Proof{ - WiresCap: common.NewMerkleCap(capHeight), - PlonkZsPartialProductsCap: common.NewMerkleCap(capHeight), - QuotientPolysCap: common.NewMerkleCap(capHeight), - Openings: common.NewOpeningSet( - commonData.Config.NumConstants, - commonData.Config.NumRoutedWires, - commonData.Config.NumWires, - commonData.Config.NumChallenges, - commonData.NumPartialProducts, - commonData.QuotientDegreeFactor, - ), - OpeningProof: common.FriProof{ - CommitPhaseMerkleCaps: friCommitPhaseMerkleCaps, - QueryRoundProofs: friQueryRoundProofs, - FinalPoly: common.NewPolynomialCoeffs(commonData.FriParams.FinalPolyLen()), - }, - }, - PublicInputs: make([]field.F, commonData.NumPublicInputs), - } - - return proofInput -} -*/ - -func (c *VerifierChip) rangeCheckProof(proof types.Proof) { +func (c *VerifierChip) rangeCheckProof(proof variables.Proof) { // Need to verify the plonky2 proof's openings, openings proof (other than the sibling elements), fri's final poly, pow witness. // Note that this is NOT range checking the public inputs (first 32 elements should be no more than 8 bits and the last 4 elements should be no more than 64 bits). Since this is currently being inputted via the smart contract, @@ -205,20 +140,19 @@ func (c *VerifierChip) rangeCheckProof(proof types.Proof) { } func (c *VerifierChip) Verify( - proof types.Proof, + proof variables.Proof, publicInputs []gl.Variable, - verifierData types.VerifierOnlyCircuitData, - commonData types.CommonCircuitData, + verifierData variables.VerifierOnlyCircuitData, ) { c.rangeCheckProof(proof) // Generate the parts of the witness that is for the plonky2 proof input publicInputsHash := c.GetPublicInputsHash(publicInputs) - proofChallenges := c.GetChallenges(proof, publicInputsHash, commonData, verifierData) + proofChallenges := c.GetChallenges(proof, publicInputsHash, verifierData) c.plonkChip.Verify(proofChallenges, proof.Openings, publicInputsHash) - initialMerkleCaps := []types.FriMerkleCap{ + initialMerkleCaps := []variables.FriMerkleCap{ verifierData.ConstantSigmasCap, proof.WiresCap, proof.PlonkZsPartialProductsCap, @@ -226,8 +160,8 @@ func (c *VerifierChip) Verify( } c.friChip.VerifyFriProof( - fri.GetInstance(&commonData, c.glChip, proofChallenges.PlonkZeta, commonData.DegreeBits), - fri.ToOpenings(proof.Openings), + c.friChip.GetInstance(proofChallenges.PlonkZeta), + c.friChip.ToOpenings(proof.Openings), &proofChallenges.FriChallenges, initialMerkleCaps, &proof.OpeningProof, diff --git a/verifier/verifier_test.go b/verifier/verifier_test.go index 10d1790..c41c9e8 100644 --- a/verifier/verifier_test.go +++ b/verifier/verifier_test.go @@ -4,52 +4,34 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/emulated" "github.com/consensys/gnark/test" - gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" "github.com/succinctlabs/gnark-plonky2-verifier/verifier" ) -type TestVerifierCircuit struct { - Proof types.Proof - PublicInputs []gl.Variable `gnark:",public"` - - verifierChip *verifier.VerifierChip `gnark:"-"` - plonky2CircuitName string `gnark:"-"` -} - -func (c *TestVerifierCircuit) Define(api frontend.API) error { - circuitDirname := "./data/" + c.plonky2CircuitName + "/" - commonCircuitData := verifier.DeserializeCommonCircuitData(circuitDirname + "common_circuit_data.json") - verifierOnlyCircuitData := verifier.DeserializeVerifierOnlyCircuitData(circuitDirname + "verifier_only_circuit_data.json") - - c.verifierChip = verifier.NewVerifierChip(api, commonCircuitData) - - c.verifierChip.Verify(c.Proof, c.PublicInputs, verifierOnlyCircuitData, commonCircuitData) - - return nil -} - func TestStepVerifier(t *testing.T) { assert := test.NewAssert(t) testCase := func() { plonky2Circuit := "step" - proofWithPis := verifier.DeserializeProofWithPublicInputs("./data/" + plonky2Circuit + "/proof_with_public_inputs.json") - circuit := TestVerifierCircuit{ - plonky2CircuitName: plonky2Circuit, - Proof: proofWithPis.Proof, - PublicInputs: proofWithPis.PublicInputs, + commonCircuitData := types.ReadCommonCircuitData("../testdata/" + plonky2Circuit + "/common_circuit_data.json") + + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("../testdata/" + plonky2Circuit + "/proof_with_public_inputs.json")) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("../testdata/" + plonky2Circuit + "/verifier_only_circuit_data.json")) + + circuit := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + CommonCircuitData: commonCircuitData, } - proofWithPis2 := verifier.DeserializeProofWithPublicInputs("./data/" + plonky2Circuit + "/proof_with_public_inputs.json") - witness := TestVerifierCircuit{ - plonky2CircuitName: plonky2Circuit, - Proof: proofWithPis2.Proof, - PublicInputs: proofWithPis2.PublicInputs, + witness := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + CommonCircuitData: commonCircuitData, } err := test.IsSolved(&circuit, &witness, ecc.BN254.ScalarField()) @@ -57,66 +39,3 @@ func TestStepVerifier(t *testing.T) { } testCase() } - -func TestStepVerifier2(t *testing.T) { - assert := test.NewAssert(t) - - plonky2Circuit := "step" - proofWithPis := verifier.DeserializeProofWithPublicInputs("./data/" + plonky2Circuit + "/proof_with_public_inputs.json") - circuit := TestVerifierCircuit{ - plonky2CircuitName: plonky2Circuit, - Proof: proofWithPis.Proof, - PublicInputs: proofWithPis.PublicInputs, - } - - proofWithPis2 := verifier.DeserializeProofWithPublicInputs("./data/" + plonky2Circuit + "/proof_with_public_inputs.json") - witness := TestVerifierCircuit{ - plonky2CircuitName: plonky2Circuit, - Proof: proofWithPis2.Proof, - PublicInputs: proofWithPis2.PublicInputs, - } - - assert.ProverSucceeded( - &circuit, - &witness, - test.WithBackends(backend.GROTH16), - test.WithCurves(ecc.BN254), - test.NoFuzzing(), - test.NoSerialization(), - ) -} - -type testCircuit struct { - Arr [2]emulated.Element[emulated.Secp256k1Fp] - Expected emulated.Element[emulated.Secp256k1Fp] -} - -func (circuit *testCircuit) Define(api frontend.API) error { - field, _ := emulated.NewField[emulated.Secp256k1Fp](api) - - mulRes := field.Mul(&circuit.Arr[0], &circuit.Arr[1]) - field.AssertIsEqual(mulRes, &circuit.Expected) - - return nil -} - -func TestMain(t *testing.T) { - assert := test.NewAssert(t) - - var circuit testCircuit - - assert.ProverSucceeded( - &circuit, - &testCircuit{ - Arr: [2]emulated.Element[emulated.Secp256k1Fp]{ - emulated.ValueOf[emulated.Secp256k1Fp](42), - emulated.ValueOf[emulated.Secp256k1Fp](24), - }, - Expected: emulated.ValueOf[emulated.Secp256k1Fp](1008), - }, - test.WithBackends(backend.GROTH16), - test.WithCurves(ecc.BN254), - test.NoFuzzing(), - test.NoSerialization(), - ) -}