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 ad468fd..3fbf46f 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 variables.Proof - PublicInputs []gl.Variable `gnark:",public"` +func runBenchmark(plonky2Circuit string, proofSystem string, profileCircuit bool, dummy bool, saveArtifacts bool) { + commonCircuitData := types.DeserializeCommonCircuitData("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) - - circuit.verifierChip.Verify(circuit.Proof, circuit.PublicInputs, verifierOnlyCircuitData, commonCircuitData) - - return nil -} + proofWithPis := variables.DeserializeProofWithPublicInputs(types.ReadProofWithPublicInputs("testdata/" + plonky2Circuit + "/proof_with_public_inputs.json")) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData(types.ReadVerifierOnlyCircuitData("testdata/" + plonky2Circuit + "/verifier_only_circuit_data.json")) -func compileCircuit(plonky2Circuit string, profileCircuit bool, serialize bool, outputSolidity bool, dummy 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,16 +67,116 @@ func compileCircuit(plonky2Circuit string, profileCircuit bool, serialize bool, println("r1cs.GetNbInternalVariables(): ", r1cs.GetNbInternalVariables()) } + witness := verifier.ExampleVerifierCircuit{ + Proof: proofWithPis.Proof, + PublicInputs: proofWithPis.PublicInputs, + VerifierOnlyCircuitData: verifierOnlyCircuitData, + CommonCircuitData: commonCircuitData, + } + + if proofSystem == "plonk" { + plonkProof(r1cs, witness, dummy, saveArtifacts) + } else if proofSystem == "groth16" { + groth16Proof(r1cs, witness, dummy, saveArtifacts) + } else { + panic("Please provide a valid proof system to benchmark, we only support plonk and groth16") + } +} + +func plonkProof(r1cs constraint.ConstraintSystem, assignment verifier.ExampleVerifierCircuit, dummy bool, saveArtifacts bool) { + var pk plonk.ProvingKey + var vk plonk.VerifyingKey + var err error + // 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()) + 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 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()) + 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 groth16Proof(r1cs constraint.ConstraintSystem, assignment verifier.ExampleVerifierCircuit, dummy bool, saveArtifacts bool) { var pk groth16.ProvingKey var vk groth16.VerifyingKey + var err error + + // 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() + // } fmt.Println("Running circuit setup", time.Now()) if dummy { @@ -92,37 +191,25 @@ func compileCircuit(plonky2Circuit string, profileCircuit bool, serialize bool, 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 -} - -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") - - // Witness - assignment := &BenchmarkPlonky2VerifierCircuit{ - Proof: proofWithPis.Proof, - PublicInputs: proofWithPis.PublicInputs, - } - 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() @@ -134,15 +221,15 @@ 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 step") - return proof + fmt.Println("vk is nil, means you're using dummy setup and we skip verification of proof") + return } fmt.Println("Verifying proof", time.Now()) @@ -184,14 +271,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() @@ -200,6 +287,12 @@ func main() { os.Exit(1) } - r1cs, pk, vk := compileCircuit(*plonky2Circuit, *profileCircuit, *serialize, *outputSolidity, true) - 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 9089d17..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/variables" - "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 variables.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/fri/fri.go b/fri/fri.go index 9483ace..cd13cf1 100644 --- a/fri/fri.go +++ b/fri/fri.go @@ -18,22 +18,60 @@ type Chip struct { api frontend.API `gnark:"-"` gl gl.Chip `gnark:"-"` poseidonBN254Chip *poseidon.BN254Chip + commonData *types.CommonCircuitData 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.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 diff --git a/fri/fri_test.go b/fri/fri_test.go index 4a8e507..19fb51d 100644 --- a/fri/fri_test.go +++ b/fri/fri_test.go @@ -10,24 +10,24 @@ import ( "github.com/succinctlabs/gnark-plonky2-verifier/fri" 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" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" ) 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.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, @@ -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.DeserializeCommonCircuitData(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 aacd5e1..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/variables" + "github.com/succinctlabs/gnark-plonky2-verifier/types" ) -type OpeningBatch struct { - Values []gl.QuadraticExtensionVariable -} - -type Openings struct { - Batches []OpeningBatch -} - -func 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}} -} - 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 @@ -70,7 +39,7 @@ var QUOTIENT = PlonkOracle{ blinding: true, } -func polynomialInfoFromRange(c *variables.CommonCircuitData, oracleIdx uint64, startPolyIdx uint64, endPolyIdx uint64) []PolynomialInfo { +func polynomialInfoFromRange(c *types.CommonCircuitData, oracleIdx uint64, startPolyIdx uint64, endPolyIdx uint64) []PolynomialInfo { returnArr := make([]PolynomialInfo, 0) for i := startPolyIdx; i < endPolyIdx; i++ { returnArr = append(returnArr, @@ -84,7 +53,7 @@ func polynomialInfoFromRange(c *variables.CommonCircuitData, oracleIdx uint64, s } // Range of the sigma polynomials in the `constants_sigmas_commitment`. -func sigmasRange(c *variables.CommonCircuitData) []uint64 { +func sigmasRange(c *types.CommonCircuitData) []uint64 { returnArr := make([]uint64, 0) for i := c.NumConstants; i <= c.NumConstants+c.Config.NumRoutedWires; i++ { returnArr = append(returnArr, i) @@ -93,20 +62,20 @@ func sigmasRange(c *variables.CommonCircuitData) []uint64 { return returnArr } -func numPreprocessedPolys(c *variables.CommonCircuitData) uint64 { +func numPreprocessedPolys(c *types.CommonCircuitData) uint64 { sigmasRange := sigmasRange(c) return sigmasRange[len(sigmasRange)-1] } -func numZSPartialProductsPolys(c *variables.CommonCircuitData) uint64 { +func numZSPartialProductsPolys(c *types.CommonCircuitData) uint64 { return c.Config.NumChallenges * (1 + c.NumPartialProducts) } -func numQuotientPolys(c *variables.CommonCircuitData) uint64 { +func numQuotientPolys(c *types.CommonCircuitData) uint64 { return c.Config.NumChallenges * c.QuotientDegreeFactor } -func friPreprocessedPolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friPreprocessedPolys(c *types.CommonCircuitData) []PolynomialInfo { return polynomialInfoFromRange( c, CONSTANTS_SIGMAS.index, @@ -115,12 +84,12 @@ func friPreprocessedPolys(c *variables.CommonCircuitData) []PolynomialInfo { ) } -func friWirePolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friWirePolys(c *types.CommonCircuitData) []PolynomialInfo { numWirePolys := c.Config.NumWires return polynomialInfoFromRange(c, WIRES.index, 0, numWirePolys) } -func friZSPartialProductsPolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friZSPartialProductsPolys(c *types.CommonCircuitData) []PolynomialInfo { return polynomialInfoFromRange( c, ZS_PARTIAL_PRODUCTS.index, @@ -129,7 +98,7 @@ func friZSPartialProductsPolys(c *variables.CommonCircuitData) []PolynomialInfo ) } -func friQuotientPolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friQuotientPolys(c *types.CommonCircuitData) []PolynomialInfo { return polynomialInfoFromRange( c, QUOTIENT.index, @@ -138,7 +107,7 @@ func friQuotientPolys(c *variables.CommonCircuitData) []PolynomialInfo { ) } -func friZSPolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friZSPolys(c *types.CommonCircuitData) []PolynomialInfo { return polynomialInfoFromRange( c, ZS_PARTIAL_PRODUCTS.index, @@ -147,7 +116,7 @@ func friZSPolys(c *variables.CommonCircuitData) []PolynomialInfo { ) } -func friOracles(c *variables.CommonCircuitData) []OracleInfo { +func friOracles(c *types.CommonCircuitData) []OracleInfo { return []OracleInfo{ { NumPolys: numPreprocessedPolys(c), @@ -168,7 +137,7 @@ func friOracles(c *variables.CommonCircuitData) []OracleInfo { } } -func friAllPolys(c *variables.CommonCircuitData) []PolynomialInfo { +func friAllPolys(c *types.CommonCircuitData) []PolynomialInfo { returnArr := make([]PolynomialInfo, 0) returnArr = append(returnArr, friPreprocessedPolys(c)...) returnArr = append(returnArr, friWirePolys(c)...) @@ -177,26 +146,3 @@ func friAllPolys(c *variables.CommonCircuitData) []PolynomialInfo { return returnArr } - -func GetInstance(c *variables.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/plonk/gates/gates_test.go b/plonk/gates/gates_test.go index 3409e46..0f64769 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,7 +690,7 @@ type TestGateCircuit struct { } func (circuit *TestGateCircuit) Define(api frontend.API) error { - commonCircuitData := verifier.DeserializeCommonCircuitData("../../data/decode_block/common_circuit_data.json") + commonCircuitData := types.DeserializeCommonCircuitData("../../testdata/decode_block/common_circuit_data.json") numSelectors := commonCircuitData.SelectorsInfo.NumSelectors() glApi := gl.New(api) diff --git a/plonk/plonk.go b/plonk/plonk.go index b94a3d4..945611f 100644 --- a/plonk/plonk.go +++ b/plonk/plonk.go @@ -5,27 +5,37 @@ 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/types" "github.com/succinctlabs/gnark-plonky2-verifier/variables" ) type PlonkChip struct { api frontend.API `gnark:"-"` - commonData variables.CommonCircuitData `gnark:"-"` + 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 } -func NewPlonkChip(api frontend.API, commonData variables.CommonCircuitData) *PlonkChip { +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,6 +48,7 @@ func NewPlonkChip(api frontend.API, commonData variables.CommonCircuitData) *Plo 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, } @@ -117,7 +128,7 @@ func (p *PlonkChip) evalVanishingPoly( 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) diff --git a/plonk/plonk_test.go b/plonk/plonk_test.go index 5cb00dc..5a7b898 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.DeserializeCommonCircuitData(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/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/common_data.go b/types/common_data.go new file mode 100644 index 0000000..3c386f3 --- /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 DeserializeCommonCircuitData(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..865a569 --- /dev/null +++ b/types/common_data_test.go @@ -0,0 +1,9 @@ +package types + +import ( + "testing" +) + +func TestDeserializeCommonCircuitData(t *testing.T) { + DeserializeCommonCircuitData("../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 index c9b744f..c206e1b 100644 --- a/types/types.go +++ b/types/types.go @@ -1,5 +1,7 @@ package types +import "github.com/succinctlabs/gnark-plonky2-verifier/plonk/gates" + type FriConfig struct { RateBits uint64 CapHeight uint64 @@ -18,3 +20,29 @@ type FriParams struct { 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 index fe73b22..829d362 100644 --- a/variables/circuit.go +++ b/variables/circuit.go @@ -2,9 +2,7 @@ package variables 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/types" ) type Proof struct { @@ -24,29 +22,3 @@ 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 types.FriConfig -} - -type CommonCircuitData struct { - Config CircuitConfig - FriParams types.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/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/verifier/deserialize.go b/verifier/deserialize.go deleted file mode 100644 index 7bdd8d2..0000000 --- a/verifier/deserialize.go +++ /dev/null @@ -1,436 +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/variables" -) - -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) variables.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{} }) variables.FriMerkleProof { - n := len(merkleProofRaw.Siblings) - var mp variables.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 -}) variables.OpeningSet { - return variables.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 -}) variables.FriProof { - var openingProof variables.FriProof - openingProof.PowWitness = gl.NewVariable(openingProofRaw.PowWitness) - openingProof.FinalPoly.Coeffs = gl.Uint64ArrayToQuadraticExtensionArray(openingProofRaw.FinalPoly.Coeffs) - - openingProof.CommitPhaseMerkleCaps = make([]variables.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([]variables.FriQueryRound, numQueryRoundProofs) - - for i := 0; i < numQueryRoundProofs; i++ { - numEvalProofs := len(openingProofRaw.QueryRoundProofs[i].InitialTreesProof.EvalsProofs) - openingProof.QueryRoundProofs[i].InitialTreesProof.EvalsProofs = make([]variables.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([]variables.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) variables.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 variables.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 -} - -// TODO: this seemed unused? -// func DeserializeProofChallenges(path string) variables.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 variables.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) variables.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 variables.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) variables.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 variables.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..6449189 --- /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 + VerifierOnlyCircuitData variables.VerifierOnlyCircuitData + + // 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 0ff36ba..2ec08e5 100644 --- a/verifier/verifier.go +++ b/verifier/verifier.go @@ -7,6 +7,7 @@ import ( gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks" "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" ) @@ -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 variables.CommonCircuitData) *VerifierChip { +func NewVerifierChip(api frontend.API, commonCircuitData types.CommonCircuitData) *VerifierChip { glChip := gl.New(api) - friChip := fri.NewChip(api, &commonCircuitData.FriParams) + 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 variables.CommonCircuit poseidonBN254Chip: poseidonBN254Chip, plonkChip: plonkChip, friChip: friChip, + commonData: commonCircuitData, } } @@ -42,10 +45,9 @@ func (c *VerifierChip) GetPublicInputsHash(publicInputs []gl.Variable) poseidon. func (c *VerifierChip) GetChallenges( proof variables.Proof, publicInputsHash poseidon.GoldilocksHashOut, - commonData variables.CommonCircuitData, verifierData variables.VerifierOnlyCircuitData, ) variables.ProofChallenges { - config := commonData.Config + config := c.commonData.Config numChallenges := config.NumChallenges challenger := challenger.NewChip(c.api) @@ -63,7 +65,7 @@ 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 variables.ProofChallenges{ PlonkBetas: plonkBetas, @@ -74,7 +76,7 @@ func (c *VerifierChip) GetChallenges( proof.OpeningProof.CommitPhaseMerkleCaps, proof.OpeningProof.FinalPoly, proof.OpeningProof.PowWitness, - commonData.DegreeBits, + c.commonData.DegreeBits, config.FriConfig, ), } @@ -141,13 +143,12 @@ func (c *VerifierChip) Verify( proof variables.Proof, publicInputs []gl.Variable, verifierData variables.VerifierOnlyCircuitData, - commonData variables.CommonCircuitData, ) { 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) @@ -159,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 aa3ac11..901dcd5 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 variables.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.DeserializeCommonCircuitData("../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.NoSerializationChecks(), - ) -} - -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.NoSerializationChecks(), - ) -}