From 450364d041bcb64f3070d34ae229d61556fc5762 Mon Sep 17 00:00:00 2001 From: Arya Tabaie Date: Tue, 14 Feb 2023 12:56:45 -0500 Subject: [PATCH 1/3] Gc 304 companion (#454) * refactor: no non-mont on bls12-377 * refactor: groth16 backend tests pass * refactor: MSM takes Montgomery only - Plonk * fix:`ToBigIntRegular` => `BigInt` * fix: more `ToBigIntRegular` => `BigInt` * feat: experiments with solving * chore: some efforts from before christmas break * refactor: all in one package * feat: development done for bn254. to test and generify * test: fails. pointer issue * feat: "generic" top sort * refactor: improved, simplified solver; compiler to match * refactor: use mostly no-ptr data. better information silos * fix: inconsistencies re assignments alignment * feat: simple compilation test passes * test: solver error found * fix: solving works on the simplest example * test: with dependency. err: inputs are modified * fix: solver works. prover doesn't. possibly deeper gkr issue * bench: merkle tree * refactor: hint-lite, has import cycle * fix: import cycle * test: basic permutation tests passing * test: end-to-end: can't use test engine (for now) * fix: propagating gkrInfo * test: "doubling" circuit passes * fix: race condition * test: with dependency * fix: small mimc test * bench: gkr inefficient * feat: support more operations * fix: minor stuff, some code generation * feat: codegen * feat: yet more codegen * fix: no gkr for tinyfield * fix: no defineGkrHints for tinyfield and more * fix: mod tidy * fix: go mod tidy * refactor: some cleanup - bn254 only * fix: a small bug and some new benchmarks * perf: replace Add(Mul) by MulAdd * perf: add frontend.WithCompressThreshold in compile test opts * perf: replace intSet by bitset * perf: use cpt in topo sort * build: make linter happy * build: reran go generate * perf: factorize MultiLin.Evaluate hot loop * revert: bn254/gkr changes * build: generify bn254/gkr changes * test: more instances * perf: reflect new gc gkr opts and parallelize solving * fix: bn254 mem pool * test: only the gkr solver * fix: solving bug - bn254 * build: generify * fix: dumping error and solver test * fix: avoid overlogging * fix: log correction * feat: string to integer and its use in fiat-shamir * fix: eddsa works * fix: when using mimc for merkle tree, leaves should be fr.Elements * revert: no SignNum and VerifyNum * fix: new mimc round count * style: mimc.Sum documentation * fix: hard coded hashes in emulated groth16 tests * fix: update stats * build: gnark-crypto@develop * fix: reflect gkr changes in gnark-crypto * fix: witness-related functions no longer return ptrs * fix: pad eddsa test messages * fix: remove printfs * refactor: updated gkr test vectors from gc * refactor: replace map hash with const hash in gkr tests * style: gofmt * build: update go.mod gnark-crypto dev * fix: fix Vector pointer type update in witness package * fix: cherry pick witness update commit + ran go generate * fix: match hardcoded-string prefix with gnark-crypto * revert: no gkr api on this branch! * fix: remove fr dependence in Merkle test * refactor: hardcoded_strings.String -> constant.HashedBytes * style: remove unnecessary added new lines * refactor: remove std/utils package from this PR --------- Co-authored-by: Gautam Botrel --- backend/hint/registry.go | 2 +- backend/witness/witness.go | 22 +- constant/constant.go | 84 ++++++ constraint/bls12-377/solution.go | 2 +- constraint/bls12-381/solution.go | 2 +- constraint/bls24-315/solution.go | 2 +- constraint/bls24-317/solution.go | 2 +- constraint/bn254/solution.go | 2 +- constraint/bw6-633/solution.go | 2 +- constraint/bw6-761/solution.go | 2 +- constraint/tinyfield/solution.go | 2 +- examples/mimc/mimc_test.go | 2 +- frontend/circuit.go | 2 +- frontend/cs/r1cs/builder.go | 2 +- frontend/cs/scs/builder.go | 4 +- go.mod | 3 +- go.sum | 8 +- internal/generator/backend/main.go | 9 + .../backend/template/imports.go.tmpl | 4 + .../template/representations/constant.go.tmpl | 34 +++ .../template/representations/r1cs.go.tmpl | 3 +- .../template/representations/solution.go.tmpl | 2 +- internal/stats/latest.stats | Bin 2801 -> 2803 bytes internal/tinyfield/vector.go | 10 +- std/accumulator/merkle/verify_test.go | 23 +- std/fiat-shamir/transcript.go | 8 +- std/fiat-shamir/transcript_test.go | 51 +--- std/gkr/gkr.go | 39 ++- std/gkr/gkr_test.go | 269 +++++++++++++----- std/gkr/registry.go | 29 ++ .../mimc_five_levels_two_instances._json | 2 +- std/gkr/test_vectors/resources/hash.json | 100 ------- .../single_identity_gate_two_instances.json | 11 +- ...nput_two_identity_gates_two_instances.json | 21 +- .../single_input_two_outs_two_instances.json | 23 +- .../single_mimc_gate_four_instances.json | 41 +-- .../single_mimc_gate_two_instances.json | 23 +- .../single_mul_gate_two_instances.json | 15 +- ...s_composed_single_input_two_instances.json | 13 +- ...uts_select-input-3_gate_two_instances.json | 13 +- std/groth16_bls12377/verifier_test.go | 2 +- std/groth16_bls24315/verifier_test.go | 2 +- std/hash/mimc/mimc.go | 2 +- std/polynomial/polynomial.go | 11 +- std/signature/eddsa/eddsa_test.go | 8 +- std/sumcheck/sumcheck.go | 1 + std/test_vector_utils/test_vector_utils.go | 239 ---------------- .../test_vector_utils_test.go | 148 ---------- test/engine.go | 41 ++- 49 files changed, 606 insertions(+), 736 deletions(-) create mode 100644 constant/constant.go create mode 100644 internal/generator/backend/template/representations/constant.go.tmpl create mode 100644 std/gkr/registry.go delete mode 100644 std/gkr/test_vectors/resources/hash.json delete mode 100644 std/test_vector_utils/test_vector_utils.go delete mode 100644 std/test_vector_utils/test_vector_utils_test.go diff --git a/backend/hint/registry.go b/backend/hint/registry.go index 619c3a1715..dfd5a66b8d 100644 --- a/backend/hint/registry.go +++ b/backend/hint/registry.go @@ -9,7 +9,7 @@ import ( var registry = make(map[ID]Function) var registryM sync.RWMutex -// Register registers an hint function in the global registry. +// Register registers a hint function in the global registry. func Register(hintFns ...Function) { registryM.Lock() defer registryM.Unlock() diff --git a/backend/witness/witness.go b/backend/witness/witness.go index 8b240654e3..437ffcd35c 100644 --- a/backend/witness/witness.go +++ b/backend/witness/witness.go @@ -171,7 +171,27 @@ func (w *witness) WriteTo(wr io.Writer) (n int64, err error) { n += 4 // write the vector - m, err := w.vector.(io.WriterTo).WriteTo(wr) + var m int64 + switch t := w.vector.(type) { + case fr_bn254.Vector: + m, err = t.WriteTo(wr) + case fr_bls12377.Vector: + m, err = t.WriteTo(wr) + case fr_bls12381.Vector: + m, err = t.WriteTo(wr) + case fr_bw6761.Vector: + m, err = t.WriteTo(wr) + case fr_bls24317.Vector: + m, err = t.WriteTo(wr) + case fr_bls24315.Vector: + m, err = t.WriteTo(wr) + case fr_bw6633.Vector: + m, err = t.WriteTo(wr) + case tinyfield.Vector: + m, err = t.WriteTo(wr) + default: + panic("invalid input") + } n += m return n, err } diff --git a/constant/constant.go b/constant/constant.go new file mode 100644 index 0000000000..237a3ba935 --- /dev/null +++ b/constant/constant.go @@ -0,0 +1,84 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +package constant + +import ( + "fmt" + "github.com/consensys/gnark-crypto/ecc" + bls12_377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + bls12_381 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" + bls24_315 "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + bls24_317 "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" + bn254 "github.com/consensys/gnark-crypto/ecc/bn254/fr" + bw6_633 "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" + bw6_761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/consensys/gnark/frontend" + "math/big" +) + +func HashedBytes(api frontend.API, str []byte) (frontend.Variable, error) { + field := api.Compiler().Field() + dst := []byte("string:") + var res big.Int + + if field.Cmp(ecc.BLS12_377.ScalarField()) == 0 { + if x, err := bls12_377.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BLS12_381.ScalarField()) == 0 { + if x, err := bls12_381.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BN254.ScalarField()) == 0 { + if x, err := bn254.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BW6_761.ScalarField()) == 0 { + if x, err := bw6_761.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BLS24_315.ScalarField()) == 0 { + if x, err := bls24_315.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BLS24_317.ScalarField()) == 0 { + if x, err := bls24_317.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else if field.Cmp(ecc.BW6_633.ScalarField()) == 0 { + if x, err := bw6_633.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + } else { + return nil, fmt.Errorf("unknown curve") + } + return res, nil +} diff --git a/constraint/bls12-377/solution.go b/constraint/bls12-377/solution.go index a34c2e2a87..8849a30acb 100644 --- a/constraint/bls12-377/solution.go +++ b/constraint/bls12-377/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bls12-381/solution.go b/constraint/bls12-381/solution.go index 74f60c23d2..dbf96fadc4 100644 --- a/constraint/bls12-381/solution.go +++ b/constraint/bls12-381/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bls24-315/solution.go b/constraint/bls24-315/solution.go index 5f018c69b8..f65a2821b1 100644 --- a/constraint/bls24-315/solution.go +++ b/constraint/bls24-315/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bls24-317/solution.go b/constraint/bls24-317/solution.go index ef035c4826..d26c56f931 100644 --- a/constraint/bls24-317/solution.go +++ b/constraint/bls24-317/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bn254/solution.go b/constraint/bn254/solution.go index 690ffc879f..3346fc9f1c 100644 --- a/constraint/bn254/solution.go +++ b/constraint/bn254/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bw6-633/solution.go b/constraint/bw6-633/solution.go index aba13f6d22..aea0b28a49 100644 --- a/constraint/bw6-633/solution.go +++ b/constraint/bw6-633/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/bw6-761/solution.go b/constraint/bw6-761/solution.go index f82b98e0c9..54e9eee0bb 100644 --- a/constraint/bw6-761/solution.go +++ b/constraint/bw6-761/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/constraint/tinyfield/solution.go b/constraint/tinyfield/solution.go index 7fd9dbc8f8..2f5eb4caec 100644 --- a/constraint/tinyfield/solution.go +++ b/constraint/tinyfield/solution.go @@ -82,7 +82,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/examples/mimc/mimc_test.go b/examples/mimc/mimc_test.go index 0a2910807e..5583193bf6 100644 --- a/examples/mimc/mimc_test.go +++ b/examples/mimc/mimc_test.go @@ -33,7 +33,7 @@ func TestPreimage(t *testing.T) { assert.ProverSucceeded(&mimcCircuit, &Circuit{ PreImage: "16130099170765464552823636852555369511329944820189892919423002775646948828469", - Hash: "8674594860895598770446879254410848023850744751986836044725552747672873438975", + Hash: "12886436712380113721405259596386800092738845035233065858332878701083870690753", }, test.WithCurves(ecc.BN254)) } diff --git a/frontend/circuit.go b/frontend/circuit.go index f561011b0a..a0ae6eed51 100644 --- a/frontend/circuit.go +++ b/frontend/circuit.go @@ -17,7 +17,7 @@ limitations under the License. package frontend // Circuit must be implemented by user-defined circuit. The best way to define a -// circuit is define a type which contains all the witness elements as fields +// circuit is to define a type which contains all the witness elements as fields // and declare `Define` method on the type. // // For example, the following is a minimal valid circuit: diff --git a/frontend/cs/r1cs/builder.go b/frontend/cs/r1cs/builder.go index 493603372f..d06bd9c3d9 100644 --- a/frontend/cs/r1cs/builder.go +++ b/frontend/cs/r1cs/builder.go @@ -163,7 +163,7 @@ func (builder *builder) FieldBitLen() int { return builder.cs.FieldBitLen() } -// newR1C clones the linear expression associated with the Variables (to avoid offseting the ID multiple time) +// newR1C clones the linear expression associated with the Variables (to avoid offsetting the ID multiple time) // and return a R1C func (builder *builder) newR1C(l, r, o frontend.Variable) constraint.R1C { L := builder.getLinearExpression(l) diff --git a/frontend/cs/scs/builder.go b/frontend/cs/scs/builder.go index dce80a0aeb..b5d2881a86 100644 --- a/frontend/cs/scs/builder.go +++ b/frontend/cs/scs/builder.go @@ -17,6 +17,7 @@ limitations under the License. package scs import ( + "fmt" "math/big" "reflect" "sort" @@ -357,8 +358,7 @@ func (builder *scs) splitProd(acc expr.TermToRefactor, r expr.LinearExpressionTo } func (builder *scs) Commit(v ...frontend.Variable) (frontend.Variable, error) { - //TODO implement me - panic("not implemented") + return nil, fmt.Errorf("not implemented") } // newDebugInfo this is temporary to restore debug logs diff --git a/go.mod b/go.mod index b9bfdb81db..89131d4135 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,10 @@ module github.com/consensys/gnark go 1.18 require ( + github.com/bits-and-blooms/bitset v1.5.0 github.com/blang/semver/v4 v4.0.0 github.com/consensys/bavard v0.1.13 - github.com/consensys/gnark-crypto v0.9.1-0.20230209152347-632763cd3e3c + github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535 github.com/fxamacker/cbor/v2 v2.2.0 github.com/google/go-cmp v0.5.8 github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 diff --git a/go.sum b/go.sum index ab4ab008f5..b3ee48f09d 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ +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/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= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.9.1-0.20230209152151-ec280f158fc8 h1:nx43IGJ1NfENl3Mrsnuvsh5Vw877whsTtwRhiqwblPU= -github.com/consensys/gnark-crypto v0.9.1-0.20230209152151-ec280f158fc8/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA= -github.com/consensys/gnark-crypto v0.9.1-0.20230209152347-632763cd3e3c h1:15LTi7eD7N/L0EN4lSbyyfO/tTqiEhpRtJjEfdMOEQ8= -github.com/consensys/gnark-crypto v0.9.1-0.20230209152347-632763cd3e3c/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA= +github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535 h1:dJ7+JcKMc+T4GeYMHFxmFT05Y/83JpR6ADhaq3R6s+0= +github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= github.com/coreos/go-systemd/v22 v22.3.2/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= diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index 1e790fe37b..3173b1a6e6 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -199,6 +199,15 @@ func main() { } + wg.Add(1) + go func() { + if err = bgen.Generate(datas, "constant", "./template/representations/", + bavard.Entry{File: filepath.Join("../../../constant", "constant.go"), Templates: []string{"constant.go.tmpl"}}); err != nil { + panic(err) + } + wg.Done() + }() + wg.Wait() // run go fmt on whole directory diff --git a/internal/generator/backend/template/imports.go.tmpl b/internal/generator/backend/template/imports.go.tmpl index 09352cb941..56639bdfb2 100644 --- a/internal/generator/backend/template/imports.go.tmpl +++ b/internal/generator/backend/template/imports.go.tmpl @@ -56,4 +56,8 @@ {{- define "import_pedersen"}} "github.com/consensys/gnark-crypto/ecc/{{ toLower .Curve }}/fr/pedersen" +{{- end}} + +{{- define "import_gkr"}} + "github.com/consensys/gnark-crypto/ecc/{{ toLower .Curve }}/fr/gkr" {{- end}} \ No newline at end of file diff --git a/internal/generator/backend/template/representations/constant.go.tmpl b/internal/generator/backend/template/representations/constant.go.tmpl new file mode 100644 index 0000000000..8feabc0997 --- /dev/null +++ b/internal/generator/backend/template/representations/constant.go.tmpl @@ -0,0 +1,34 @@ +import ( + "fmt" + "github.com/consensys/gnark-crypto/ecc" + {{- range $i := . }} + {{- if ne .Curve "tinyfield"}} + {{toLower .CurveID}} "github.com/consensys/gnark-crypto/ecc/{{toLower .Curve}}/fr" + {{- end}} + {{- end}} + "github.com/consensys/gnark/frontend" + "math/big" +) + +func HashedBytes(api frontend.API, str []byte) (frontend.Variable, error) { + field := api.Compiler().Field() + dst := []byte("string:") + var res big.Int + + {{ $if := "if"}} + {{- range $i := . }} + {{- if ne .Curve "tinyfield"}} + {{$if}} field.Cmp(ecc.{{.CurveID}}.ScalarField()) == 0 { + if x, err := {{toLower .CurveID}}.Hash(str, dst, 1); err == nil { + x[0].BigInt(&res) + } else { + return nil, err + } + {{- $if = "} else if" }} + {{- end}} + {{- end}} + } else { + return nil, fmt.Errorf("unknown curve") + } + return res, nil +} \ No newline at end of file diff --git a/internal/generator/backend/template/representations/r1cs.go.tmpl b/internal/generator/backend/template/representations/r1cs.go.tmpl index fdcb706b24..d65b171737 100644 --- a/internal/generator/backend/template/representations/r1cs.go.tmpl +++ b/internal/generator/backend/template/representations/r1cs.go.tmpl @@ -66,9 +66,8 @@ func (cs *R1CS) AddConstraint(r1c constraint.R1C, debugInfo ...constraint.DebugI func (cs *R1CS) Solve(witness, a, b, c fr.Vector, opt backend.ProverConfig) (fr.Vector, error) { log := logger.Logger().With().Int("nbConstraints", len(cs.Constraints)).Str("backend", "groth16").Logger() - nbWires := len(cs.Public) + len(cs.Secret) + cs.NbInternalVariables - solution, err := newSolution( nbWires, opt.HintFunctions, cs.MHintsDependencies, cs.MHints, cs.Coefficients, &cs.System.SymbolTable) + solution, err := newSolution(nbWires, opt.HintFunctions, cs.MHintsDependencies, cs.MHints, cs.Coefficients, &cs.System.SymbolTable) if err != nil { return make(fr.Vector, nbWires), err } diff --git a/internal/generator/backend/template/representations/solution.go.tmpl b/internal/generator/backend/template/representations/solution.go.tmpl index cf2ef77192..e323a614f5 100644 --- a/internal/generator/backend/template/representations/solution.go.tmpl +++ b/internal/generator/backend/template/representations/solution.go.tmpl @@ -63,7 +63,7 @@ func (s *solution) isValid() bool { return int(s.nbSolved) == len(s.values) } -// computeTerm computes coef*variable +// computeTerm computes coeff*variable func (s *solution) computeTerm(t constraint.Term) fr.Element { cID, vID := t.CoeffID(), t.WireID() if cID != 0 && !s.solved[vID] { diff --git a/internal/stats/latest.stats b/internal/stats/latest.stats index debd58dfe4c750dca62909a07d87e02dedc2eaa4..be2ff23c987ac84a1cd466308e97c9f046513b88 100644 GIT binary patch delta 248 zcmew;`dM^BDr@im4hFu-vFr~gJ1|PrPhtGWG=%|3(zlP9u@PY&aFGI;{))5%txK>!HgR~G;P delta 267 zcmew?`cZU3Dr@)u4hFu-OITYbbFv53b7mwKXXxi<<|eanFhBtZ<3A=P5N2TfzYT~O z5PY}L0}H@{-u q#W-1m?IEN1 - // it writes the domain separators as bytes - // in gnark, we write them as field element - // to ensure consistency in this test, we ensure the challengeIDs have a fix byte len (the one of fr.Element) - frSize := utils.ByteLen(scalarField) - alpha, beta, gamma := make([]byte, frSize), make([]byte, frSize), make([]byte, frSize) - _alpha := []byte("alpha") - _beta := []byte("beta") - _gamma := []byte("gamma") - copy(alpha, _alpha) // -> alpha = [xxxxx||0....0] - copy(beta, _beta) - copy(gamma, _gamma) - - return string(alpha), string(beta), string(gamma) -} - func TestFiatShamir(t *testing.T) { assert := test.NewAssert(t) @@ -117,12 +97,9 @@ func TestFiatShamir(t *testing.T) { // compute the witness for each curve for curveID, h := range testData { - // get the domain separators, correctly formatted so they match the frontend.Variable size - // (which under the hood is a fr.Element) - alpha, beta, gamma := getChallenges(curveID.ScalarField()) // instantiate the hash and the transcript in plain go - ts := fiatshamir.NewTranscript(h.New(), alpha, beta, gamma) + ts := fiatshamir.NewTranscript(h.New(), "alpha", "beta", "gamma") var bindings [3][4]big.Int for i := 0; i < 3; i++ { @@ -133,21 +110,21 @@ func TestFiatShamir(t *testing.T) { frSize := utils.ByteLen(curveID.ScalarField()) buf := make([]byte, frSize) for i := 0; i < 4; i++ { - err := ts.Bind(alpha, bindings[0][i].FillBytes(buf)) + err := ts.Bind("alpha", bindings[0][i].FillBytes(buf)) assert.NoError(err) - err = ts.Bind(beta, bindings[1][i].FillBytes(buf)) + err = ts.Bind("beta", bindings[1][i].FillBytes(buf)) assert.NoError(err) - err = ts.Bind(gamma, bindings[2][i].FillBytes(buf)) + err = ts.Bind("gamma", bindings[2][i].FillBytes(buf)) assert.NoError(err) } var expectedChallenges [3][]byte var err error - expectedChallenges[0], err = ts.ComputeChallenge(alpha) + expectedChallenges[0], err = ts.ComputeChallenge("alpha") assert.NoError(err) - expectedChallenges[1], err = ts.ComputeChallenge(beta) + expectedChallenges[1], err = ts.ComputeChallenge("beta") assert.NoError(err) - expectedChallenges[2], err = ts.ComputeChallenge(gamma) + expectedChallenges[2], err = ts.ComputeChallenge("gamma") assert.NoError(err) // instantiate the circuit with provided inputs diff --git a/std/gkr/gkr.go b/std/gkr/gkr.go index 1e9640314f..e10e0c98b5 100644 --- a/std/gkr/gkr.go +++ b/std/gkr/gkr.go @@ -23,7 +23,6 @@ type Wire struct { Gate Gate Inputs []*Wire // if there are no Inputs, the wire is assumed an input wire nbUniqueOutputs int // number of other wires using it as input, not counting duplicates (i.e. providing two inputs to the same gate counts as one) - //nbUniqueInputs int // number of inputs, not counting duplicates } type Circuit []Wire @@ -425,6 +424,8 @@ func statusList(c Circuit) []int { return res } +// TODO: Have this use algo_utils.TopologicalSort underneath + // topologicalSort sorts the wires in order of dependence. Such that for any wire, any one it depends on // occurs before it. It tries to stick to the input order as much as possible. An already sorted list will remain unchanged. // It also sets the nbOutput flags, and a dummy IdentityGate for input wires. @@ -450,14 +451,18 @@ func topologicalSort(c Circuit) []*Wire { func (a WireAssignment) NumInstances() int { for _, aW := range a { - return len(aW) + if aW != nil { + return len(aW) + } } panic("empty assignment") } func (a WireAssignment) NumVars() int { for _, aW := range a { - return aW.NumVars() + if aW != nil { + return aW.NumVars() + } } panic("empty assignment") } @@ -526,3 +531,31 @@ func DeserializeProof(sorted []*Wire, serializedProof []frontend.Variable) (Proo } return proof, nil } + +type MulGate struct{} + +func (g MulGate) Evaluate(api frontend.API, x ...frontend.Variable) frontend.Variable { + if len(x) != 2 { + panic("mul has fan-in 2") + } + return api.Mul(x[0], x[1]) +} + +// TODO: Degree must take nbInputs as an argument and return degree = nbInputs +func (g MulGate) Degree() int { + return 2 +} + +type AddGate struct{} + +func (a AddGate) Evaluate(api frontend.API, v ...frontend.Variable) frontend.Variable { + var rest []frontend.Variable + if len(v) >= 2 { + rest = v[2:] + } + return api.Add(v[0], v[1], rest...) +} + +func (a AddGate) Degree() int { + return 1 +} diff --git a/std/gkr/gkr_test.go b/std/gkr/gkr_test.go index 5e68171452..29060c59b6 100644 --- a/std/gkr/gkr_test.go +++ b/std/gkr/gkr_test.go @@ -3,17 +3,18 @@ package gkr import ( "encoding/json" "fmt" + "os" + "path/filepath" + "reflect" + "testing" + "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" fiatshamir "github.com/consensys/gnark/std/fiat-shamir" + "github.com/consensys/gnark/std/hash" "github.com/consensys/gnark/std/polynomial" - "github.com/consensys/gnark/std/test_vector_utils" "github.com/consensys/gnark/test" "github.com/stretchr/testify/assert" - "os" - "path/filepath" - "reflect" - "testing" ) func TestGkrVectors(t *testing.T) { @@ -25,12 +26,10 @@ func TestGkrVectors(t *testing.T) { } for _, dirEntry := range dirEntries { if !dirEntry.IsDir() && filepath.Ext(dirEntry.Name()) == ".json" { - path := filepath.Join(testDirPath, dirEntry.Name()) noExt := dirEntry.Name()[:len(dirEntry.Name())-len(".json")] t.Run(noExt, generateTestVerifier(path)) - } } } @@ -61,7 +60,7 @@ func generateTestVerifier(path string, options ...option) func(t *testing.T) { Input: testCase.Input, Output: testCase.Output, SerializedProof: testCase.Proof.Serialize(), - PerturbHash: false, + ToFail: false, TestCaseName: path, } @@ -69,7 +68,7 @@ func generateTestVerifier(path string, options ...option) func(t *testing.T) { Input: make([][]frontend.Variable, len(testCase.Input)), Output: make([][]frontend.Variable, len(testCase.Output)), SerializedProof: make([]frontend.Variable, len(assignment.SerializedProof)), - PerturbHash: false, + ToFail: false, TestCaseName: path, } @@ -81,8 +80,8 @@ func generateTestVerifier(path string, options ...option) func(t *testing.T) { } if !opts.noFail { - assignment.PerturbHash = true // TODO: This one doesn't matter right? - circuit.PerturbHash = true + assignment.ToFail = true // TODO: This one doesn't matter right? + circuit.ToFail = true test.NewAssert(t).SolvingFailed(circuit, assignment, test.WithBackends(backend.GROTH16)) } } @@ -92,7 +91,7 @@ type GkrVerifierCircuit struct { Input [][]frontend.Variable Output [][]frontend.Variable `gnark:",public"` SerializedProof []frontend.Variable - PerturbHash bool + ToFail bool TestCaseName string } @@ -111,12 +110,16 @@ func (c *GkrVerifierCircuit) Define(api frontend.API) error { } assignment := makeInOutAssignment(testCase.Circuit, c.Input, c.Output) - var baseChallenge []frontend.Variable - if c.PerturbHash { - baseChallenge = []frontend.Variable{1} + var hsh hash.Hash + if c.ToFail { + hsh = NewMessageCounter(api, 1, 1) + } else { + if hsh, err = HashFromDescription(api, testCase.Hash); err != nil { + return err + } } - return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(&test_vector_utils.MapHash{Map: testCase.ElementMap, API: api}, baseChallenge...)) + return Verify(api, testCase.Circuit, assignment, proof, fiatshamir.WithHash(hsh)) } func makeInOutAssignment(c Circuit, inputValues [][]frontend.Variable, outputValues [][]frontend.Variable) WireAssignment { @@ -142,15 +145,15 @@ func fillWithBlanks(slice [][]frontend.Variable, size int) { } type TestCase struct { - Circuit Circuit - ElementMap test_vector_utils.ElementMap - Proof Proof - Input [][]frontend.Variable - Output [][]frontend.Variable - Name string + Circuit Circuit + Hash HashDescription + Proof Proof + Input [][]frontend.Variable + Output [][]frontend.Variable + Name string } type TestCaseInfo struct { - Hash string `json:"hash"` + Hash HashDescription `json:"hash"` Circuit string `json:"circuit"` Input [][]interface{} `json:"input"` Output [][]interface{} `json:"output"` @@ -181,15 +184,11 @@ func getTestCase(path string) (*TestCase, error) { return nil, err } - if cse.ElementMap, err = test_vector_utils.ElementMapFromFile(filepath.Join(dir, info.Hash)); err != nil { - return nil, err - } - cse.Proof = unmarshalProof(info.Proof) - cse.Input = test_vector_utils.ToVariableSliceSlice(info.Input) - cse.Output = test_vector_utils.ToVariableSliceSlice(info.Output) - + cse.Input = ToVariableSliceSlice(info.Input) + cse.Output = ToVariableSliceSlice(info.Output) + cse.Hash = info.Hash cse.Name = path testCases[path] = cse } else { @@ -241,7 +240,7 @@ func (c CircuitInfo) toCircuit() (circuit Circuit, err error) { } var found bool - if circuit[i].Gate, found = gates[wireInfo.Gate]; !found && wireInfo.Gate != "" { + if circuit[i].Gate, found = RegisteredGates[wireInfo.Gate]; !found && wireInfo.Gate != "" { err = fmt.Errorf("undefined gate \"%s\"", wireInfo.Gate) } } @@ -249,49 +248,12 @@ func (c CircuitInfo) toCircuit() (circuit Circuit, err error) { return } -var gates map[string]Gate +type _select int func init() { - gates = make(map[string]Gate) - gates["identity"] = IdentityGate{} - gates["mul"] = mulGate{} - gates["mimc"] = mimcCipherGate{ark: 0} //TODO: Add ark - gates["select-input-3"] = _select(2) -} - -type mulGate struct{} - -func (g mulGate) Evaluate(api frontend.API, x ...frontend.Variable) frontend.Variable { - if len(x) != 2 { - panic("mul has fan-in 2") - } - return api.Mul(x[0], x[1]) -} - -func (g mulGate) Degree() int { - return 2 -} - -type mimcCipherGate struct { - ark frontend.Variable -} - -func (m mimcCipherGate) Evaluate(api frontend.API, input ...frontend.Variable) frontend.Variable { - if len(input) != 2 { - panic("mimc has fan-in 2") - } - sum := api.Add(input[0], input[1], m.ark) - - sumCubed := api.Mul(sum, sum, sum) // sum^3 - return api.Mul(sumCubed, sumCubed, sum) -} - -func (m mimcCipherGate) Degree() int { - return 7 + RegisteredGates["select-input-3"] = _select(2) } -type _select int - func (g _select) Evaluate(_ frontend.API, in ...frontend.Variable) frontend.Variable { return in[g] } @@ -315,7 +277,7 @@ func unmarshalProof(printable PrintableProof) (proof Proof) { finalEvalSlice := reflect.ValueOf(printable[i].FinalEvalProof) finalEvalProof := make([]frontend.Variable, finalEvalSlice.Len()) for k := range finalEvalProof { - finalEvalProof[k] = test_vector_utils.ToVariable(finalEvalSlice.Index(k).Interface()) + finalEvalProof[k] = ToVariable(finalEvalSlice.Index(k).Interface()) } proof[i].FinalEvalProof = finalEvalProof } else { @@ -324,7 +286,7 @@ func unmarshalProof(printable PrintableProof) (proof Proof) { proof[i].PartialSumPolys = make([]polynomial.Polynomial, len(printable[i].PartialSumPolys)) for k := range printable[i].PartialSumPolys { - proof[i].PartialSumPolys[k] = test_vector_utils.ToVariableSlice(printable[i].PartialSumPolys[k]) + proof[i].PartialSumPolys[k] = ToVariableSlice(printable[i].PartialSumPolys[k]) } } return @@ -357,3 +319,166 @@ func TestLoadCircuit(t *testing.T) { assert.Equal(t, []*Wire{&c[1]}, c[2].Inputs) } + +func TestTopSortTrivial(t *testing.T) { + c := make(Circuit, 2) + c[0].Inputs = []*Wire{&c[1]} + sorted := topologicalSort(c) + assert.Equal(t, []*Wire{&c[1], &c[0]}, sorted) +} + +func TestTopSortSingleGate(t *testing.T) { + c := make(Circuit, 3) + c[0].Inputs = []*Wire{&c[1], &c[2]} + sorted := topologicalSort(c) + expected := []*Wire{&c[1], &c[2], &c[0]} + assert.True(t, SliceEqual(sorted, expected)) //TODO: Remove + AssertSliceEqual(t, sorted, expected) + assert.Equal(t, c[0].nbUniqueOutputs, 0) + assert.Equal(t, c[1].nbUniqueOutputs, 1) + assert.Equal(t, c[2].nbUniqueOutputs, 1) +} + +func TestTopSortDeep(t *testing.T) { + c := make(Circuit, 4) + c[0].Inputs = []*Wire{&c[2]} + c[1].Inputs = []*Wire{&c[3]} + c[2].Inputs = []*Wire{} + c[3].Inputs = []*Wire{&c[0]} + sorted := topologicalSort(c) + assert.Equal(t, []*Wire{&c[2], &c[0], &c[3], &c[1]}, sorted) +} + +func TestTopSortWide(t *testing.T) { + c := make(Circuit, 10) + c[0].Inputs = []*Wire{&c[3], &c[8]} + c[1].Inputs = []*Wire{&c[6]} + c[2].Inputs = []*Wire{&c[4]} + c[3].Inputs = []*Wire{} + c[4].Inputs = []*Wire{} + c[5].Inputs = []*Wire{&c[9]} + c[6].Inputs = []*Wire{&c[9]} + c[7].Inputs = []*Wire{&c[9], &c[5], &c[2]} + c[8].Inputs = []*Wire{&c[4], &c[3]} + c[9].Inputs = []*Wire{} + + sorted := topologicalSort(c) + sortedExpected := []*Wire{&c[3], &c[4], &c[2], &c[8], &c[0], &c[9], &c[5], &c[6], &c[1], &c[7]} + + assert.Equal(t, sortedExpected, sorted) +} + +func ToVariable(v interface{}) frontend.Variable { + switch vT := v.(type) { + case float64: + return int(vT) + default: + return v + } +} + +func ToVariableSlice[V any](slice []V) (variableSlice []frontend.Variable) { + variableSlice = make([]frontend.Variable, len(slice)) + for i := range slice { + variableSlice[i] = ToVariable(slice[i]) + } + return +} + +func ToVariableSliceSlice[V any](sliceSlice [][]V) (variableSliceSlice [][]frontend.Variable) { + variableSliceSlice = make([][]frontend.Variable, len(sliceSlice)) + for i := range sliceSlice { + variableSliceSlice[i] = ToVariableSlice(sliceSlice[i]) + } + return +} + +func AssertSliceEqual[T comparable](t *testing.T, expected, seen []T) { + assert.Equal(t, len(expected), len(seen)) + for i := range seen { + assert.True(t, expected[i] == seen[i], "@%d: %v != %v", i, expected[i], seen[i]) // assert.Equal is not strict enough when comparing pointers, i.e. it compares what they refer to + } +} + +func SliceEqual[T comparable](expected, seen []T) bool { + if len(expected) != len(seen) { + return false + } + for i := range seen { + if expected[i] != seen[i] { + return false + } + } + return true +} + +type HashDescription map[string]interface{} + +func HashFromDescription(api frontend.API, d HashDescription) (hash.Hash, error) { + if _type, ok := d["type"]; ok { + switch _type { + case "const": + startState := int64(d["val"].(float64)) + return &MessageCounter{startState: startState, step: 0, state: startState, api: api}, nil + default: + return nil, fmt.Errorf("unknown fake hash type \"%s\"", _type) + } + } + return nil, fmt.Errorf("hash description missing type") +} + +type MessageCounter struct { + startState int64 + state int64 + step int64 + + // cheap trick to avoid unconstrained input errors + api frontend.API + zero frontend.Variable +} + +func (m *MessageCounter) Write(data ...frontend.Variable) { + + for i := range data { + sq1, sq2 := m.api.Mul(data[i], data[i]), m.api.Mul(data[i], data[i]) + m.zero = m.api.Sub(sq1, sq2, m.zero) + } + + m.state += int64(len(data)) * m.step +} + +func (m *MessageCounter) Sum() frontend.Variable { + return m.api.Add(m.state, m.zero) +} + +func (m *MessageCounter) Reset() { + m.zero = 0 + m.state = m.startState +} + +func NewMessageCounter(api frontend.API, startState, step int) hash.Hash { + transcript := &MessageCounter{startState: int64(startState), state: int64(startState), step: int64(step), api: api} + return transcript +} + +func NewMessageCounterGenerator(startState, step int) func(frontend.API) hash.Hash { + return func(api frontend.API) hash.Hash { + return NewMessageCounter(api, startState, step) + } +} + +type constHashCircuit struct { + X frontend.Variable +} + +func (c *constHashCircuit) Define(api frontend.API) error { + hsh := NewMessageCounter(api, 0, 0) + hsh.Reset() + hsh.Write(c.X) + api.AssertIsEqual(hsh.Sum(), 0) + return nil +} + +func TestConstHash(t *testing.T) { + test.NewAssert(t).SolvingSucceeded(&constHashCircuit{}, &constHashCircuit{X: 1}) +} diff --git a/std/gkr/registry.go b/std/gkr/registry.go new file mode 100644 index 0000000000..07e60abc91 --- /dev/null +++ b/std/gkr/registry.go @@ -0,0 +1,29 @@ +package gkr + +import "github.com/consensys/gnark/frontend" + +var RegisteredGates = map[string]Gate{ + "identity": IdentityGate{}, + "add": AddGate{}, + "mul": MulGate{}, + "mimc": MiMCCipherGate{Ark: 0}, //TODO: Add ark +} + +type MiMCCipherGate struct { + Ark frontend.Variable +} + +func (m MiMCCipherGate) Evaluate(api frontend.API, input ...frontend.Variable) frontend.Variable { + + if len(input) != 2 { + panic("mimc has fan-in 2") + } + sum := api.Add(input[0], input[1], m.Ark) + + sumCubed := api.Mul(sum, sum, sum) // sum^3 + return api.Mul(sumCubed, sumCubed, sum) +} + +func (m MiMCCipherGate) Degree() int { + return 7 +} diff --git a/std/gkr/test_vectors/mimc_five_levels_two_instances._json b/std/gkr/test_vectors/mimc_five_levels_two_instances._json index 94f45f4a92..446d23fdb2 100644 --- a/std/gkr/test_vectors/mimc_five_levels_two_instances._json +++ b/std/gkr/test_vectors/mimc_five_levels_two_instances._json @@ -1,5 +1,5 @@ { - "hash": "resources/hash.json", + "hash": {"type": "const", "val": -1}, "circuit": "resources/mimc_five_levels.json", "input": [[1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3]], "output": [[4, 3]], diff --git a/std/gkr/test_vectors/resources/hash.json b/std/gkr/test_vectors/resources/hash.json deleted file mode 100644 index 9a96e4aaa5..0000000000 --- a/std/gkr/test_vectors/resources/hash.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "-1145901219840000000,5":1, - "-364381509670404096,1":5, - "-95630089550561280,-5":1, - "-19168501451194368,5":-5, - "-2559554567012352,-1":5, - "-2559554567012352,1":5, - "-172523520000000,-1":1, - "-172523520000000,2":-1, - "-2861958168576,-2":-1, - "-2861958168576,0":2, - "-191032962484,-4":4, - "-191032962484,4":-5, - "-72481259520,4":-4, - "-72481259520,5":4, - "-24034694442,0":4, - "-24034694442,3":5, - "-6664736128,-5":0, - "-6664736128,1":3, - "-1440000120,-5":-5, - "-1440000120,2":1, - "-520000000,0":5, - "-408944640,-5":0, - "-408944640,4":-2, - "-215233605,-4":0, - "-215233605,1":0, - "-213909504,-3":-5, - "-213909504,-1":2, - "-79691776,-4":-4, - "-79691776,-1":1, - "-25529833,-1":-4, - "-25529833,4":-1, - "-16796110,-5":-3, - "-16796110,-1":-1, - "-6718464,-4":-1, - "-6718464,3":4, - "-1328125,1":-4, - "-1328125,2":3, - "-292992,-2":-1, - "-292992,1":-5, - "-163840,1":2, - "-163840,2":1, - "-6561,-3":1, - "-6561,1":2, - "-272,0":0, - "-128,-3":5, - "-90,-3":0, - "-85,0":5, - "-85,1":0, - "-80,-3":1, - "-80,-2":0, - "-40,-4":-5, - "-40,-1":3, - "-40,4":-4, - "-33,-2":-2, - "-33,0":-3, - "-30,-2":-5, - "-30,0":-3, - "-27,-3":-2, - "-27,1":-3, - "-20,-4":1, - "-20,-2":3, - "-12,-2":-3, - "-9,-5":-1, - "-9,-4":-2, - "-9,-3":4, - "-9,-2":-4, - "-9,1":-4, - "-6,-3":-2, - "-6,1":0, - "-5,-4":-2, - "-5,-2":-1, - "-5,4":-5, - "-4,-4":-3, - "-4,-2":-4, - "-4,-1":-3, - "-3,-4":1, - "-3,-3":-3, - "-3,-2":-2, - "-3,-1":-5, - "-3,1":2, - "-2,-2":-2, - "-2,5":0, - "0,-3":-2, - "0,-2":-4, - "0,2":5, - "1,-3":-4, - "1,-2":1, - "1,5":-3, - "3,-3":-2, - "4,4":4, - "5,-2":-2, - "1715678768":-3, - "1715678769":-1, - "33548498023443810":-3, - "8588415549364514352":5, - "8588697024341225008":-2, - "8588978499317935664":-4, - "8588978499317935665":4 -} \ No newline at end of file diff --git a/std/gkr/test_vectors/single_identity_gate_two_instances.json b/std/gkr/test_vectors/single_identity_gate_two_instances.json index 01cf5b7c46..ce326d0a63 100644 --- a/std/gkr/test_vectors/single_identity_gate_two_instances.json +++ b/std/gkr/test_vectors/single_identity_gate_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_identity_gate.json", "input": [ [ @@ -20,12 +23,12 @@ }, { "finalEvalProof": [ - 3 + 5 ], "partialSumPolys": [ [ - -9, - -20 + -3, + -8 ] ] } diff --git a/std/gkr/test_vectors/single_input_two_identity_gates_two_instances.json b/std/gkr/test_vectors/single_input_two_identity_gates_two_instances.json index d926fc2b66..2c95f044f2 100644 --- a/std/gkr/test_vectors/single_input_two_identity_gates_two_instances.json +++ b/std/gkr/test_vectors/single_input_two_identity_gates_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_input_two_identity_gates.json", "input": [ [ @@ -22,30 +25,30 @@ "finalEvalProof": [], "partialSumPolys": [ [ - -33, - -128 + 0, + 0 ] ] }, { "finalEvalProof": [ - 5 + 1 ], "partialSumPolys": [ [ - -9, - -40 + -3, + -16 ] ] }, { "finalEvalProof": [ - -3 + 1 ], "partialSumPolys": [ [ - -9, - -40 + -3, + -16 ] ] } diff --git a/std/gkr/test_vectors/single_input_two_outs_two_instances.json b/std/gkr/test_vectors/single_input_two_outs_two_instances.json index 39ad0f0ac5..d348303d0e 100644 --- a/std/gkr/test_vectors/single_input_two_outs_two_instances.json +++ b/std/gkr/test_vectors/single_input_two_outs_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_input_two_outs.json", "input": [ [ @@ -22,31 +25,31 @@ "finalEvalProof": [], "partialSumPolys": [ [ - -6, - -33 + 0, + 0 ] ] }, { "finalEvalProof": [ - 1 + 0 ], "partialSumPolys": [ [ - -12, - -90, - -272 + -4, + -36, + -112 ] ] }, { "finalEvalProof": [ - -2 + 0 ], "partialSumPolys": [ [ - -6, - -30 + -2, + -12 ] ] } diff --git a/std/gkr/test_vectors/single_mimc_gate_four_instances.json b/std/gkr/test_vectors/single_mimc_gate_four_instances.json index 8c50ed09a2..525459ecb1 100644 --- a/std/gkr/test_vectors/single_mimc_gate_four_instances.json +++ b/std/gkr/test_vectors/single_mimc_gate_four_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_mimc_gate.json", "input": [ [ @@ -34,29 +37,29 @@ }, { "finalEvalProof": [ - 1, - 7 + -1, + -3 ], "partialSumPolys": [ [ - -292992, - -16796110, - "-213909504", - "-1440000120", - "-6664736128", - "-24034694442", - "-72481259520", - "-191032962484" + -32640, + -2239484, + -29360128, + "-200000010", + "-931628672", + "-3373267120", + "-10200858624", + "-26939400158" ], [ - "-408944640", - "-2861958168576", - "-172523520000000", - "-2559554567012352", - "-19168501451194368", - "-95630089550561280", - "-364381509670404096", - "-1145901219840000000" + -81920, + -41943040, + "-1254113280", + "-13421772800", + "-83200000000", + "-366917713920", + "-1281828208640", + "-3779571220480" ] ] } diff --git a/std/gkr/test_vectors/single_mimc_gate_two_instances.json b/std/gkr/test_vectors/single_mimc_gate_two_instances.json index 488c9aa24e..7fa23ce4b1 100644 --- a/std/gkr/test_vectors/single_mimc_gate_two_instances.json +++ b/std/gkr/test_vectors/single_mimc_gate_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_mimc_gate.json", "input": [ [ @@ -29,18 +32,18 @@ { "finalEvalProof": [ 1, - 6 + 0 ], "partialSumPolys": [ [ - -6561, - -163840, - -1328125, - -6718464, - -25529833, - -79691776, - "-215233605", - "-520000000" + -2187, + -65536, + -546875, + -2799360, + -10706059, + -33554432, + -90876411, + "-220000000" ] ] } diff --git a/std/gkr/test_vectors/single_mul_gate_two_instances.json b/std/gkr/test_vectors/single_mul_gate_two_instances.json index bc01efd879..75c1d59c3d 100644 --- a/std/gkr/test_vectors/single_mul_gate_two_instances.json +++ b/std/gkr/test_vectors/single_mul_gate_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/single_mul_gate.json", "input": [ [ @@ -28,14 +31,14 @@ }, { "finalEvalProof": [ - 4, - 2 + 5, + 1 ], "partialSumPolys": [ [ - -27, - -80, - -85 + -9, + -32, + -35 ] ] } diff --git a/std/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json b/std/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json index 4daede5890..10e5f1ff3c 100644 --- a/std/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json +++ b/std/gkr/test_vectors/two_identity_gates_composed_single_input_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/two_identity_gates_composed_single_input.json", "input": [ [ @@ -20,22 +23,22 @@ }, { "finalEvalProof": [ - 6 + 3 ], "partialSumPolys": [ [ - 5, + -1, 0 ] ] }, { "finalEvalProof": [ - -3 + 3 ], "partialSumPolys": [ [ - -3, + -1, 0 ] ] diff --git a/std/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json b/std/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json index ed4fc4f181..19e127df71 100644 --- a/std/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json +++ b/std/gkr/test_vectors/two_inputs_select-input-3_gate_two_instances.json @@ -1,5 +1,8 @@ { - "hash": "resources/hash.json", + "hash": { + "type": "const", + "val": -1 + }, "circuit": "resources/two_inputs_select-input-3_gate.json", "input": [ [ @@ -28,13 +31,13 @@ }, { "finalEvalProof": [ - -5, - -3 + -1, + 1 ], "partialSumPolys": [ [ - -9, - -40 + -3, + -16 ] ] } diff --git a/std/groth16_bls12377/verifier_test.go b/std/groth16_bls12377/verifier_test.go index 21d86eeb47..10bcd53d8b 100644 --- a/std/groth16_bls12377/verifier_test.go +++ b/std/groth16_bls12377/verifier_test.go @@ -35,7 +35,7 @@ import ( const ( preImage = "4992816046196248432836492760315135318126925090839638585255611512962528270024" - publicHash = "4458332240632096997117977163518118563548842578509780924154021342053538349576" + publicHash = "7831393781387060555412927989411398077996792073838215843928284475008119358174" ) type mimcCircuit struct { diff --git a/std/groth16_bls24315/verifier_test.go b/std/groth16_bls24315/verifier_test.go index 0287d84815..7144014e8d 100644 --- a/std/groth16_bls24315/verifier_test.go +++ b/std/groth16_bls24315/verifier_test.go @@ -38,7 +38,7 @@ import ( const ( preImage = "4992816046196248432836492760315135318126925090839638585255611512962528270024" - publicHash = "740442171083661049659184837119506324904268940878674425328909705936292585001" + publicHash = "4875439939758844840941638351757981379945701574516438614845550995673793857363" ) type mimcCircuit struct { diff --git a/std/hash/mimc/mimc.go b/std/hash/mimc/mimc.go index 1a1dcc44db..a85c4e04bd 100644 --- a/std/hash/mimc/mimc.go +++ b/std/hash/mimc/mimc.go @@ -55,7 +55,7 @@ func (h *MiMC) Reset() { h.h = 0 } -// Hash hash (in r1cs form) using Miyaguchi–Preneel: +// Sum hash (in r1cs form) using Miyaguchi–Preneel: // https://en.wikipedia.org/wiki/One-way_compression_function // The XOR operation is replaced by field addition. // See github.com/consensys/gnark-crypto for reference implementation. diff --git a/std/polynomial/polynomial.go b/std/polynomial/polynomial.go index f5a9155d2e..72cc329f9f 100644 --- a/std/polynomial/polynomial.go +++ b/std/polynomial/polynomial.go @@ -1,8 +1,9 @@ package polynomial import ( - "github.com/consensys/gnark/frontend" "math/bits" + + "github.com/consensys/gnark/frontend" ) type Polynomial []frontend.Variable @@ -15,19 +16,15 @@ func (m MultiLin) Evaluate(api frontend.API, at []frontend.Variable) frontend.Va eqs[0] = 1 for i, rI := range at { prevSize := 1 << i - oneMinusRI := api.Sub(1, rI) for j := prevSize - 1; j >= 0; j-- { eqs[2*j+1] = api.Mul(rI, eqs[j]) - eqs[2*j] = api.Mul(oneMinusRI, eqs[j]) + eqs[2*j] = api.Sub(eqs[j], eqs[2*j+1]) // eq[2j] == (1 - rI) * eq[j] } } evaluation := frontend.Variable(0) for j := range m { - evaluation = api.Add( - evaluation, - api.Mul(eqs[j], m[j]), - ) + evaluation = api.MulAcc(evaluation, eqs[j], m[j]) } return evaluation } diff --git a/std/signature/eddsa/eddsa_test.go b/std/signature/eddsa/eddsa_test.go index 3a71937fb6..657d337010 100644 --- a/std/signature/eddsa/eddsa_test.go +++ b/std/signature/eddsa/eddsa_test.go @@ -99,15 +99,17 @@ func TestEddsa(t *testing.T) { var msg big.Int msg.Rand(randomness, snarkField) t.Log("msg to sign", msg.String()) - msgData := msg.Bytes() + msgDataUnpadded := msg.Bytes() + msgData := make([]byte, len(snarkField.Bytes())) + copy(msgData[len(msgData)-len(msgDataUnpadded):], msgDataUnpadded) // generate signature - signature, err := privKey.Sign(msgData[:], conf.hash.New()) + signature, err := privKey.Sign(msgData, conf.hash.New()) assert.NoError(err, "signing message") // check if there is no problem in the signature pubKey := privKey.Public() - checkSig, err := pubKey.Verify(signature, msgData[:], conf.hash.New()) + checkSig, err := pubKey.Verify(signature, msgData, conf.hash.New()) assert.NoError(err, "verifying signature") assert.True(checkSig, "signature verification failed") diff --git a/std/sumcheck/sumcheck.go b/std/sumcheck/sumcheck.go index c3fb993d7e..e002dfc489 100644 --- a/std/sumcheck/sumcheck.go +++ b/std/sumcheck/sumcheck.go @@ -56,6 +56,7 @@ func next(transcript *fiatshamir.Transcript, bindings []frontend.Variable, remai } func Verify(api frontend.API, claims LazyClaims, proof Proof, transcriptSettings fiatshamir.Settings) error { + remainingChallengeNames, err := setupTranscript(api, claims.ClaimsNum(), claims.VarsNum(), &transcriptSettings) transcript := transcriptSettings.Transcript if err != nil { diff --git a/std/test_vector_utils/test_vector_utils.go b/std/test_vector_utils/test_vector_utils.go deleted file mode 100644 index 1e1b6a11cd..0000000000 --- a/std/test_vector_utils/test_vector_utils.go +++ /dev/null @@ -1,239 +0,0 @@ -package test_vector_utils - -import ( - "encoding/json" - "github.com/consensys/gnark/frontend" - "os" - "path/filepath" - "strconv" - "strings" -) - -// These data structures fail to equate different representations of the same number. i.e. 5 = -10/-2 -// @Tabaie TODO Replace with proper lookup tables - -type Map struct { - keys []frontend.Variable - values []frontend.Variable -} - -func getDelta(api frontend.API, x frontend.Variable, deltaIndex int, keys []frontend.Variable) frontend.Variable { - num := frontend.Variable(1) - den := frontend.Variable(1) - - for i, key := range keys { - if i != deltaIndex { - num = api.Mul(num, api.Sub(key, x)) - den = api.Mul(den, api.Sub(key, keys[deltaIndex])) - } - } - - return api.Div(num, den) -} - -// Get returns garbage if key is not present -func (m Map) Get(api frontend.API, key frontend.Variable) frontend.Variable { - res := frontend.Variable(0) - - for i := range m.keys { - deltaI := getDelta(api, key, i, m.keys) - res = api.Add(res, api.Mul(deltaI, m.values[i])) - } - - return res -} - -// The keys in a DoubleMap must be constant. i.e. known at setup time -type DoubleMap struct { - keys1 []frontend.Variable - keys2 []frontend.Variable - values [][]frontend.Variable -} - -// Get is very inefficient. Do not use outside testing -func (m DoubleMap) Get(api frontend.API, key1, key2 frontend.Variable) frontend.Variable { - deltas1 := make([]frontend.Variable, len(m.keys1)) - deltas2 := make([]frontend.Variable, len(m.keys2)) - - for i := range deltas1 { - deltas1[i] = getDelta(api, key1, i, m.keys1) - } - - for j := range deltas2 { - deltas2[j] = getDelta(api, key2, j, m.keys2) - } - - res := frontend.Variable(0) - - for i := range deltas1 { - for j := range deltas2 { - if m.values[i][j] != nil { - deltaIJ := api.Mul(deltas1[i], deltas2[j], m.values[i][j]) - res = api.Add(res, deltaIJ) - } - } - } - - return res -} - -func register[K comparable](m map[K]int, key K) { - if _, ok := m[key]; !ok { - m[key] = len(m) - } -} - -func orderKeys[K comparable](order map[K]int) (ordered []K) { - ordered = make([]K, len(order)) - for k, i := range order { - ordered[i] = k - } - return -} - -type ElementMap struct { - single Map - double DoubleMap -} - -func ReadMap(in map[string]interface{}) ElementMap { - single := Map{ - keys: make([]frontend.Variable, 0), - values: make([]frontend.Variable, 0), - } - - keys1 := make(map[string]int) - keys2 := make(map[string]int) - - for k, v := range in { - - kSep := strings.Split(k, ",") - switch len(kSep) { - case 1: - single.keys = append(single.keys, k) - single.values = append(single.values, ToVariable(v)) - case 2: - - register(keys1, kSep[0]) - register(keys2, kSep[1]) - - default: - panic("too many keys") - } - } - - vals := make([][]frontend.Variable, len(keys1)) - for i := range vals { - vals[i] = make([]frontend.Variable, len(keys2)) - } - - for k, v := range in { - kSep := strings.Split(k, ",") - if len(kSep) == 2 { - i1 := keys1[kSep[0]] - i2 := keys2[kSep[1]] - vals[i1][i2] = ToVariable(v) - } - } - - double := DoubleMap{ - keys1: ToVariableSlice(orderKeys(keys1)), - keys2: ToVariableSlice(orderKeys(keys2)), - values: vals, - } - - return ElementMap{ - single: single, - double: double, - } -} - -func ToVariable(v interface{}) frontend.Variable { - switch vT := v.(type) { - case float64: - return int(vT) - default: - return v - } -} - -func ToVariableSlice[V any](slice []V) (variableSlice []frontend.Variable) { - variableSlice = make([]frontend.Variable, len(slice)) - for i := range slice { - variableSlice[i] = ToVariable(slice[i]) - } - return -} - -func ToVariableSliceSlice[V any](sliceSlice [][]V) (variableSliceSlice [][]frontend.Variable) { - variableSliceSlice = make([][]frontend.Variable, len(sliceSlice)) - for i := range sliceSlice { - variableSliceSlice[i] = ToVariableSlice(sliceSlice[i]) - } - return -} - -func ToMap(keys1, keys2, values []frontend.Variable) map[string]interface{} { - res := make(map[string]interface{}, len(keys1)) - for i := range keys1 { - str := strconv.Itoa(keys1[i].(int)) + "," + strconv.Itoa(keys2[i].(int)) - res[str] = values[i].(int) - } - return res -} - -var MapCache = make(map[string]ElementMap) // @Tabaie: global bad? - -func ElementMapFromFile(path string) (ElementMap, error) { - path, err := filepath.Abs(path) - if err != nil { - return ElementMap{}, err - } - if h, ok := MapCache[path]; ok { - return h, nil - } - var bytes []byte - if bytes, err = os.ReadFile(path); err == nil { - var asMap map[string]interface{} - if err = json.Unmarshal(bytes, &asMap); err != nil { - return ElementMap{}, err - } - - res := ReadMap(asMap) - MapCache[path] = res - return res, nil - - } else { - return ElementMap{}, err - } -} - -type MapHash struct { - Map ElementMap - state frontend.Variable - API frontend.API - stateValid bool -} - -func (m *MapHash) Sum() frontend.Variable { - return m.state -} - -func (m *MapHash) Write(data ...frontend.Variable) { - for _, x := range data { - m.write(x) - } -} - -func (m *MapHash) Reset() { - m.stateValid = false -} - -func (m *MapHash) write(x frontend.Variable) { - if m.stateValid { - m.state = m.Map.double.Get(m.API, x, m.state) - } else { - m.state = m.Map.single.Get(m.API, x) - } - m.stateValid = true -} diff --git a/std/test_vector_utils/test_vector_utils_test.go b/std/test_vector_utils/test_vector_utils_test.go deleted file mode 100644 index 2193fb789e..0000000000 --- a/std/test_vector_utils/test_vector_utils_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package test_vector_utils - -import ( - "fmt" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/test" - "github.com/stretchr/testify/assert" - "testing" -) - -type TestSingleMapCircuit struct { - M Map `gnark:"-"` - Values []frontend.Variable -} - -func (c *TestSingleMapCircuit) Define(api frontend.API) error { - - for i, k := range c.M.keys { - v := c.M.Get(api, k) - api.AssertIsEqual(v, c.Values[i]) - } - - return nil -} - -func TestSingleMap(t *testing.T) { - m := map[string]interface{}{ - "1": -2, - "4": 1, - "6": 7, - } - single := ReadMap(m).single - - assignment := TestSingleMapCircuit{ - M: single, - Values: single.values, - } - - circuit := TestSingleMapCircuit{ - M: single, - Values: make([]frontend.Variable, len(m)), // Okay to use the same object? - } - - test.NewAssert(t).ProverSucceeded(&circuit, &assignment, test.WithBackends(backend.GROTH16)) -} - -type TestDoubleMapCircuit struct { - M DoubleMap `gnark:"-"` - Values []frontend.Variable - Keys1 []frontend.Variable `gnark:"-"` - Keys2 []frontend.Variable `gnark:"-"` -} - -func (c *TestDoubleMapCircuit) Define(api frontend.API) error { - - for i := range c.Keys1 { - v := c.M.Get(api, c.Keys1[i], c.Keys2[i]) - api.AssertIsEqual(v, c.Values[i]) - } - - return nil -} - -func TestReadDoubleMap(t *testing.T) { - keys1 := []frontend.Variable{1, 2} - keys2 := []frontend.Variable{1, 0} - values := []frontend.Variable{3, 1} - - for i := 0; i < 100; i++ { - m := ToMap(keys1, keys2, values) - double := ReadMap(m).double - valuesOrdered := [][]frontend.Variable{{3, nil}, {nil, 1}} - - assert.True(t, double.keys1[0] == "1" && double.keys1[1] == "2" || double.keys1[0] == "2" && double.keys1[1] == "1") - assert.True(t, double.keys2[0] == "1" && double.keys2[1] == "0" || double.keys2[0] == "0" && double.keys2[1] == "1") - - if double.keys1[0] != "1" { - valuesOrdered[0], valuesOrdered[1] = valuesOrdered[1], valuesOrdered[0] - } - - if double.keys2[0] != "1" { - valuesOrdered[0][0], valuesOrdered[0][1] = valuesOrdered[0][1], valuesOrdered[0][0] - valuesOrdered[1][0], valuesOrdered[1][1] = valuesOrdered[1][1], valuesOrdered[1][0] - } - - assert.True(t, slice2Eq(valuesOrdered, double.values)) - - } - -} - -func slice2Eq(s1, s2 [][]frontend.Variable) bool { - if len(s1) != len(s2) { - return false - } - for i := range s1 { - if !sliceEq(s1[i], s2[i]) { - return false - } - } - return true -} - -func sliceEq(s1, s2 []frontend.Variable) bool { - if len(s1) != len(s2) { - return false - } - for i := range s1 { - if s1[i] != s2[i] { - return false - } - } - return true -} - -func TestDoubleMap(t *testing.T) { - keys1 := []frontend.Variable{1, 5, 5, 3} - keys2 := []frontend.Variable{1, -5, 4, 4} - values := []frontend.Variable{0, 2, 3, 0} - - m := ToMap(keys1, keys2, values) - double := ReadMap(m).double - - fmt.Println(double) - - assignment := TestDoubleMapCircuit{ - M: double, - Values: values, - Keys1: keys1, - Keys2: keys2, - } - - circuit := TestDoubleMapCircuit{ - M: double, - Keys1: keys1, - Keys2: keys2, - Values: make([]frontend.Variable, len(m)), // Okay to use the same object? - } - - test.NewAssert(t).ProverSucceeded(&circuit, &assignment, test.WithBackends(backend.GROTH16)) -} - -func TestDoubleMapManyTimes(t *testing.T) { - for i := 0; i < 100; i++ { - TestDoubleMap(t) - } -} diff --git a/test/engine.go b/test/engine.go index 58311214d2..ab459573df 100644 --- a/test/engine.go +++ b/test/engine.go @@ -417,23 +417,37 @@ func (e *engine) Println(a ...frontend.Variable) { } for i := 0; i < len(a); i++ { - if s, ok := a[i].(string); ok { - sbb.WriteString(s) - } else { - v := e.toBigInt(a[i]) - var vAsNeg big.Int - vAsNeg.Sub(v, e.q) - if vAsNeg.IsInt64() { - sbb.WriteString(strconv.FormatInt(vAsNeg.Int64(), 10)) - } else { - sbb.WriteString(v.String()) - } - } + e.print(&sbb, a[i]) sbb.WriteByte(' ') } fmt.Println(sbb.String()) } +func (e *engine) print(sbb *strings.Builder, x interface{}) { + switch v := x.(type) { + case string: + sbb.WriteString(v) + case []frontend.Variable: + sbb.WriteRune('[') + for i := range v { + e.print(sbb, v[i]) + if i+1 != len(v) { + sbb.WriteRune(',') + } + } + sbb.WriteRune(']') + default: + i := e.toBigInt(v) + var iAsNeg big.Int + iAsNeg.Sub(i, e.q) + if iAsNeg.IsInt64() { + sbb.WriteString(strconv.FormatInt(iAsNeg.Int64(), 10)) + } else { + sbb.WriteString(i.String()) + } + } +} + func (e *engine) NewHint(f hint.Function, nbOutputs int, inputs ...frontend.Variable) ([]frontend.Variable, error) { if nbOutputs <= 0 { @@ -571,6 +585,5 @@ func (e *engine) Compiler() frontend.Compiler { } func (e *engine) Commit(v ...frontend.Variable) (frontend.Variable, error) { - //TODO implement me - panic("implement me") + panic("not implemented") } From f8673c4817eedfbc53261485ebbaad7ae5eff4bc Mon Sep 17 00:00:00 2001 From: Gautam Botrel Date: Tue, 14 Feb 2023 14:27:00 -0600 Subject: [PATCH 2/3] perf: more precomputation in plonk/iop (#471) * perf: batch convert to lagrange coset * refactor: revert previous commit and move tests in backend/plonk * perf: pre-allocate some slices capacity to avoid mem moves * perf: pre compute lagrange coset basis poly once * refactor: kill EvaluationPermutationBigDomainBitReversed unused * refactor: keeping up * fix: fix Vector pointer type update in witness package * build: latest gnark-crypto * build: update to gnark-crypto v0.9.1 * docs: update comment * build: re-ran gogenerate * build: update go.mod deps * build: update go.mod deps * fix: recompute lagrange poly after deserializing step plonk pk --- backend/plonk/plonk_test.go | 169 +++++++++++++ go.mod | 23 +- go.sum | 86 +++---- internal/backend/bls12-377/plonk/marshal.go | 4 +- .../backend/bls12-377/plonk/marshal_test.go | 11 +- .../backend/bls12-377/plonk/plonk_test.go | 223 ------------------ internal/backend/bls12-377/plonk/prove.go | 163 +++++++------ internal/backend/bls12-377/plonk/setup.go | 61 ++++- internal/backend/bls12-381/plonk/marshal.go | 4 +- .../backend/bls12-381/plonk/marshal_test.go | 11 +- .../backend/bls12-381/plonk/plonk_test.go | 223 ------------------ internal/backend/bls12-381/plonk/prove.go | 163 +++++++------ internal/backend/bls12-381/plonk/setup.go | 61 ++++- internal/backend/bls24-315/plonk/marshal.go | 4 +- .../backend/bls24-315/plonk/marshal_test.go | 11 +- .../backend/bls24-315/plonk/plonk_test.go | 223 ------------------ internal/backend/bls24-315/plonk/prove.go | 163 +++++++------ internal/backend/bls24-315/plonk/setup.go | 61 ++++- internal/backend/bls24-317/plonk/marshal.go | 4 +- .../backend/bls24-317/plonk/marshal_test.go | 11 +- .../backend/bls24-317/plonk/plonk_test.go | 223 ------------------ internal/backend/bls24-317/plonk/prove.go | 163 +++++++------ internal/backend/bls24-317/plonk/setup.go | 61 ++++- internal/backend/bn254/plonk/marshal.go | 4 +- internal/backend/bn254/plonk/marshal_test.go | 11 +- internal/backend/bn254/plonk/plonk_test.go | 223 ------------------ internal/backend/bn254/plonk/prove.go | 163 +++++++------ internal/backend/bn254/plonk/setup.go | 61 ++++- internal/backend/bw6-633/plonk/marshal.go | 4 +- .../backend/bw6-633/plonk/marshal_test.go | 11 +- internal/backend/bw6-633/plonk/plonk_test.go | 223 ------------------ internal/backend/bw6-633/plonk/prove.go | 163 +++++++------ internal/backend/bw6-633/plonk/setup.go | 61 ++++- internal/backend/bw6-761/plonk/marshal.go | 4 +- .../backend/bw6-761/plonk/marshal_test.go | 11 +- internal/backend/bw6-761/plonk/plonk_test.go | 223 ------------------ internal/backend/bw6-761/plonk/prove.go | 163 +++++++------ internal/backend/bw6-761/plonk/setup.go | 61 ++++- internal/generator/backend/main.go | 7 +- .../zkpschemes/plonk/plonk.marshal.go.tmpl | 4 +- .../zkpschemes/plonk/plonk.prove.go.tmpl | 163 +++++++------ .../zkpschemes/plonk/plonk.setup.go.tmpl | 59 ++++- .../zkpschemes/plonk/tests/marshal.go.tmpl | 11 +- .../zkpschemes/plonk/tests/plonk.go.tmpl | 197 ---------------- 44 files changed, 1365 insertions(+), 2588 deletions(-) create mode 100644 backend/plonk/plonk_test.go delete mode 100644 internal/backend/bls12-377/plonk/plonk_test.go delete mode 100644 internal/backend/bls12-381/plonk/plonk_test.go delete mode 100644 internal/backend/bls24-315/plonk/plonk_test.go delete mode 100644 internal/backend/bls24-317/plonk/plonk_test.go delete mode 100644 internal/backend/bn254/plonk/plonk_test.go delete mode 100644 internal/backend/bw6-633/plonk/plonk_test.go delete mode 100644 internal/backend/bw6-761/plonk/plonk_test.go delete mode 100644 internal/generator/backend/template/zkpschemes/plonk/tests/plonk.go.tmpl diff --git a/backend/plonk/plonk_test.go b/backend/plonk/plonk_test.go new file mode 100644 index 0000000000..211ef6a243 --- /dev/null +++ b/backend/plonk/plonk_test.go @@ -0,0 +1,169 @@ +package plonk_test + +import ( + "bytes" + "math/big" + "testing" + + "github.com/consensys/gnark" + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/kzg" + "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/test" + "github.com/stretchr/testify/require" +) + +//--------------------// +// benches // +//--------------------// + +func TestProver(t *testing.T) { + + for _, curve := range getCurves() { + t.Run(curve.String(), func(t *testing.T) { + var b1, b2 bytes.Buffer + assert := require.New(t) + + ccs, _solution, srs := referenceCircuit(curve) + fullWitness, err := frontend.NewWitness(_solution, curve.ScalarField()) + assert.NoError(err) + + publicWitness, err := fullWitness.Public() + assert.NoError(err) + + pk, vk, err := plonk.Setup(ccs, srs) + assert.NoError(err) + + // write the PK to ensure it is not mutated + _, err = pk.WriteTo(&b1) + assert.NoError(err) + + proof, err := plonk.Prove(ccs, pk, fullWitness) + assert.NoError(err) + + // check pk + _, err = pk.WriteTo(&b2) + assert.NoError(err) + + assert.True(bytes.Equal(b1.Bytes(), b2.Bytes()), "plonk prover mutated the proving key") + + err = plonk.Verify(proof, vk, publicWitness) + assert.NoError(err) + + }) + + } +} + +func BenchmarkSetup(b *testing.B) { + for _, curve := range getCurves() { + b.Run(curve.String(), func(b *testing.B) { + ccs, _, srs := referenceCircuit(curve) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, _ = plonk.Setup(ccs, srs) + } + }) + } +} + +func BenchmarkProver(b *testing.B) { + for _, curve := range getCurves() { + b.Run(curve.String(), func(b *testing.B) { + ccs, _solution, srs := referenceCircuit(curve) + fullWitness, err := frontend.NewWitness(_solution, curve.ScalarField()) + if err != nil { + b.Fatal(err) + } + pk, _, err := plonk.Setup(ccs, srs) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = plonk.Prove(ccs, pk, fullWitness) + } + }) + } +} + +func BenchmarkVerifier(b *testing.B) { + for _, curve := range getCurves() { + b.Run(curve.String(), func(b *testing.B) { + ccs, _solution, srs := referenceCircuit(curve) + fullWitness, err := frontend.NewWitness(_solution, curve.ScalarField()) + if err != nil { + b.Fatal(err) + } + publicWitness, err := fullWitness.Public() + if err != nil { + b.Fatal(err) + } + + pk, vk, err := plonk.Setup(ccs, srs) + if err != nil { + b.Fatal(err) + } + proof, err := plonk.Prove(ccs, pk, fullWitness) + if err != nil { + panic(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = plonk.Verify(proof, vk, publicWitness) + } + }) + } +} + +type refCircuit struct { + nbConstraints int + X frontend.Variable + Y frontend.Variable `gnark:",public"` +} + +func (circuit *refCircuit) Define(api frontend.API) error { + for i := 0; i < circuit.nbConstraints; i++ { + circuit.X = api.Mul(circuit.X, circuit.X) + } + api.AssertIsEqual(circuit.X, circuit.Y) + return nil +} + +func referenceCircuit(curve ecc.ID) (constraint.ConstraintSystem, frontend.Circuit, kzg.SRS) { + const nbConstraints = 40000 + circuit := refCircuit{ + nbConstraints: nbConstraints, + } + ccs, err := frontend.Compile(curve.ScalarField(), scs.NewBuilder, &circuit) + if err != nil { + panic(err) + } + + var good refCircuit + good.X = 2 + + // compute expected Y + expectedY := new(big.Int).SetUint64(2) + exp := big.NewInt(1) + exp.Lsh(exp, nbConstraints) + expectedY.Exp(expectedY, exp, curve.ScalarField()) + + good.Y = expectedY + srs, err := test.NewKZGSRS(ccs) + if err != nil { + panic(err) + } + return ccs, &good, srs +} + +func getCurves() []ecc.ID { + if testing.Short() { + return []ecc.ID{ecc.BN254} + } + return gnark.Curves() +} diff --git a/go.mod b/go.mod index 89131d4135..f0b879473e 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,28 @@ module github.com/consensys/gnark go 1.18 require ( - github.com/bits-and-blooms/bitset v1.5.0 github.com/blang/semver/v4 v4.0.0 github.com/consensys/bavard v0.1.13 - github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535 - github.com/fxamacker/cbor/v2 v2.2.0 - github.com/google/go-cmp v0.5.8 - github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 + github.com/consensys/gnark-crypto v0.9.1 + github.com/fxamacker/cbor/v2 v2.4.0 + github.com/google/go-cmp v0.5.9 + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 github.com/leanovate/gopter v0.2.9 - github.com/rs/zerolog v1.26.1 - github.com/stretchr/testify v1.8.0 - golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 + github.com/rs/zerolog v1.29.0 + github.com/stretchr/testify v1.8.1 + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/sys v0.2.0 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index b3ee48f09d..8749f1a378 100644 --- a/go.sum +++ b/go.sum @@ -1,89 +1,67 @@ -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/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= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535 h1:dJ7+JcKMc+T4GeYMHFxmFT05Y/83JpR6ADhaq3R6s+0= -github.com/consensys/gnark-crypto v0.9.1-0.20230213001010-7444bbbba535/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +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/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/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.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= -github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1 h1:8pyqKJvrJqUYaKS851Ule26pwWvey6IDMiczaBLDKLQ= -github.com/google/pprof v0.0.0-20220729232143-a41b82acbcb1/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 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= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= -github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= +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/rs/xid v1.4.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/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= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75 h1:x03zeu7B2B11ySp+daztnwM5oBJ/8wGUSqrwcw9L0RA= -golang.org/x/exp v0.0.0-20220713135740-79cabaa25d75/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/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/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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/backend/bls12-377/plonk/marshal.go b/internal/backend/bls12-377/plonk/marshal.go index bc2fbfd458..64daa4c899 100644 --- a/internal/backend/bls12-377/plonk/marshal.go +++ b/internal/backend/bls12-377/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bls12-377/plonk/marshal_test.go b/internal/backend/bls12-377/plonk/marshal_test.go index 946de7eee4..94a69f6295 100644 --- a/internal/backend/bls12-377/plonk/marshal_test.go +++ b/internal/backend/bls12-377/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bls12-377/plonk/plonk_test.go b/internal/backend/bls12-377/plonk/plonk_test.go deleted file mode 100644 index 245ac5fbc6..0000000000 --- a/internal/backend/bls12-377/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bls12-377" - - "github.com/consensys/gnark/constraint/bls12-377" - - bls12_377plonk "github.com/consensys/gnark/internal/backend/bls12-377/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bls12_377plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls12_377plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bls12_377plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bls12_377plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls12_377plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bls12_377plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls12_377plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls12_377plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bls12_377plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bls12_377plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bls12_377plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bls12_377plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bls12_377plonk.Proof binary serialization - b.Run("proof: binary serialization (bls12_377plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bls12_377plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bls12_377plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bls12-377/plonk/prove.go b/internal/backend/bls12-377/plonk/prove.go index fcc622b886..489ab0dc61 100644 --- a/internal/backend/bls12-377/plonk/prove.go +++ b/internal/backend/bls12-377/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bls12-377/plonk/setup.go b/internal/backend/bls12-377/plonk/setup.go index fef8cc0180..e7f0b7a553 100644 --- a/internal/backend/bls12-377/plonk/setup.go +++ b/internal/backend/bls12-377/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/iop" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/kzg" "github.com/consensys/gnark/constraint/bls12-377" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bls12-381/plonk/marshal.go b/internal/backend/bls12-381/plonk/marshal.go index 6e934cf716..cdb2e18749 100644 --- a/internal/backend/bls12-381/plonk/marshal.go +++ b/internal/backend/bls12-381/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bls12-381/plonk/marshal_test.go b/internal/backend/bls12-381/plonk/marshal_test.go index 021122ea59..0e0380208d 100644 --- a/internal/backend/bls12-381/plonk/marshal_test.go +++ b/internal/backend/bls12-381/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bls12-381/plonk/plonk_test.go b/internal/backend/bls12-381/plonk/plonk_test.go deleted file mode 100644 index 3f1a2cdcdc..0000000000 --- a/internal/backend/bls12-381/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bls12-381" - - "github.com/consensys/gnark/constraint/bls12-381" - - bls12_381plonk "github.com/consensys/gnark/internal/backend/bls12-381/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bls12_381plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls12_381plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bls12_381plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bls12_381plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls12_381plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bls12_381plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls12_381plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls12_381plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bls12_381plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bls12_381plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bls12_381plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bls12_381plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bls12_381plonk.Proof binary serialization - b.Run("proof: binary serialization (bls12_381plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bls12_381plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bls12_381plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bls12-381/plonk/prove.go b/internal/backend/bls12-381/plonk/prove.go index cfc605ecd9..8d1402e02f 100644 --- a/internal/backend/bls12-381/plonk/prove.go +++ b/internal/backend/bls12-381/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bls12-381/plonk/setup.go b/internal/backend/bls12-381/plonk/setup.go index 66ed6cc6e9..e86f945c71 100644 --- a/internal/backend/bls12-381/plonk/setup.go +++ b/internal/backend/bls12-381/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/iop" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/kzg" "github.com/consensys/gnark/constraint/bls12-381" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bls24-315/plonk/marshal.go b/internal/backend/bls24-315/plonk/marshal.go index e4f10fbaaf..69aad24641 100644 --- a/internal/backend/bls24-315/plonk/marshal.go +++ b/internal/backend/bls24-315/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bls24-315/plonk/marshal_test.go b/internal/backend/bls24-315/plonk/marshal_test.go index a8576bee66..485366fd77 100644 --- a/internal/backend/bls24-315/plonk/marshal_test.go +++ b/internal/backend/bls24-315/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bls24-315/plonk/plonk_test.go b/internal/backend/bls24-315/plonk/plonk_test.go deleted file mode 100644 index 573d3ce2e8..0000000000 --- a/internal/backend/bls24-315/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bls24-315" - - "github.com/consensys/gnark/constraint/bls24-315" - - bls24_315plonk "github.com/consensys/gnark/internal/backend/bls24-315/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bls24_315plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls24_315plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bls24_315plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bls24_315plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls24_315plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bls24_315plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls24_315plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls24_315plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bls24_315plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bls24_315plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bls24_315plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bls24_315plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bls24_315plonk.Proof binary serialization - b.Run("proof: binary serialization (bls24_315plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bls24_315plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bls24_315plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bls24-315/plonk/prove.go b/internal/backend/bls24-315/plonk/prove.go index bd4f3eef1f..eccaefd544 100644 --- a/internal/backend/bls24-315/plonk/prove.go +++ b/internal/backend/bls24-315/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bls24-315/plonk/setup.go b/internal/backend/bls24-315/plonk/setup.go index d8bed7cb67..0bdfcb2ff9 100644 --- a/internal/backend/bls24-315/plonk/setup.go +++ b/internal/backend/bls24-315/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/iop" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/kzg" "github.com/consensys/gnark/constraint/bls24-315" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bls24-317/plonk/marshal.go b/internal/backend/bls24-317/plonk/marshal.go index 69610d14a7..5b9eb8b6f1 100644 --- a/internal/backend/bls24-317/plonk/marshal.go +++ b/internal/backend/bls24-317/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bls24-317/plonk/marshal_test.go b/internal/backend/bls24-317/plonk/marshal_test.go index d321e9a2d4..64618d989e 100644 --- a/internal/backend/bls24-317/plonk/marshal_test.go +++ b/internal/backend/bls24-317/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bls24-317/plonk/plonk_test.go b/internal/backend/bls24-317/plonk/plonk_test.go deleted file mode 100644 index 0c588e0e77..0000000000 --- a/internal/backend/bls24-317/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bls24-317" - - "github.com/consensys/gnark/constraint/bls24-317" - - bls24_317plonk "github.com/consensys/gnark/internal/backend/bls24-317/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bls24_317plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls24_317plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bls24_317plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bls24_317plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls24_317plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bls24_317plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bls24_317plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bls24_317plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bls24_317plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bls24_317plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bls24_317plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bls24_317plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bls24_317plonk.Proof binary serialization - b.Run("proof: binary serialization (bls24_317plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bls24_317plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bls24_317plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bls24-317/plonk/prove.go b/internal/backend/bls24-317/plonk/prove.go index b9475a75d0..162b2a6e28 100644 --- a/internal/backend/bls24-317/plonk/prove.go +++ b/internal/backend/bls24-317/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bls24-317/plonk/setup.go b/internal/backend/bls24-317/plonk/setup.go index 5cf275c6f8..08c3689967 100644 --- a/internal/backend/bls24-317/plonk/setup.go +++ b/internal/backend/bls24-317/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/iop" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/kzg" "github.com/consensys/gnark/constraint/bls24-317" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bn254/plonk/marshal.go b/internal/backend/bn254/plonk/marshal.go index 7978b000f4..400e50e808 100644 --- a/internal/backend/bn254/plonk/marshal.go +++ b/internal/backend/bn254/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bn254/plonk/marshal_test.go b/internal/backend/bn254/plonk/marshal_test.go index 1c65702398..8840ad242e 100644 --- a/internal/backend/bn254/plonk/marshal_test.go +++ b/internal/backend/bn254/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bn254/plonk/plonk_test.go b/internal/backend/bn254/plonk/plonk_test.go deleted file mode 100644 index 8f4e676197..0000000000 --- a/internal/backend/bn254/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bn254/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bn254" - - "github.com/consensys/gnark/constraint/bn254" - - bn254plonk "github.com/consensys/gnark/internal/backend/bn254/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bn254/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bn254plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bn254plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bn254plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bn254plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bn254plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bn254plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bn254plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bn254plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bn254plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bn254plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bn254plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bn254plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bn254plonk.Proof binary serialization - b.Run("proof: binary serialization (bn254plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bn254plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bn254plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bn254/plonk/prove.go b/internal/backend/bn254/plonk/prove.go index 54bc9e343e..0f5042889d 100644 --- a/internal/backend/bn254/plonk/prove.go +++ b/internal/backend/bn254/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bn254/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bn254/plonk/setup.go b/internal/backend/bn254/plonk/setup.go index 5be63c2c90..1cefa3c469 100644 --- a/internal/backend/bn254/plonk/setup.go +++ b/internal/backend/bn254/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/iop" "github.com/consensys/gnark-crypto/ecc/bn254/fr/kzg" "github.com/consensys/gnark/constraint/bn254" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bw6-633/plonk/marshal.go b/internal/backend/bw6-633/plonk/marshal.go index cceca0e846..6ae656e777 100644 --- a/internal/backend/bw6-633/plonk/marshal.go +++ b/internal/backend/bw6-633/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bw6-633/plonk/marshal_test.go b/internal/backend/bw6-633/plonk/marshal_test.go index 805c83f35f..09064a8900 100644 --- a/internal/backend/bw6-633/plonk/marshal_test.go +++ b/internal/backend/bw6-633/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bw6-633/plonk/plonk_test.go b/internal/backend/bw6-633/plonk/plonk_test.go deleted file mode 100644 index 4207d91f47..0000000000 --- a/internal/backend/bw6-633/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bw6-633" - - "github.com/consensys/gnark/constraint/bw6-633" - - bw6_633plonk "github.com/consensys/gnark/internal/backend/bw6-633/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bw6_633plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bw6_633plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bw6_633plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bw6_633plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bw6_633plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bw6_633plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bw6_633plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bw6_633plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bw6_633plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bw6_633plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bw6_633plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bw6_633plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bw6_633plonk.Proof binary serialization - b.Run("proof: binary serialization (bw6_633plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bw6_633plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bw6_633plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bw6-633/plonk/prove.go b/internal/backend/bw6-633/plonk/prove.go index 59fa589f0f..bd6f3ff24b 100644 --- a/internal/backend/bw6-633/plonk/prove.go +++ b/internal/backend/bw6-633/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bw6-633/plonk/setup.go b/internal/backend/bw6-633/plonk/setup.go index 47c492adca..4e6d650887 100644 --- a/internal/backend/bw6-633/plonk/setup.go +++ b/internal/backend/bw6-633/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/iop" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/kzg" "github.com/consensys/gnark/constraint/bw6-633" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/backend/bw6-761/plonk/marshal.go b/internal/backend/bw6-761/plonk/marshal.go index 6755dd99d1..d51e9ef211 100644 --- a/internal/backend/bw6-761/plonk/marshal.go +++ b/internal/backend/bw6-761/plonk/marshal.go @@ -123,7 +123,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -169,7 +168,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -182,6 +180,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/backend/bw6-761/plonk/marshal_test.go b/internal/backend/bw6-761/plonk/marshal_test.go index 15bd9468a5..548044159d 100644 --- a/internal/backend/bw6-761/plonk/marshal_test.go +++ b/internal/backend/bw6-761/plonk/marshal_test.go @@ -67,12 +67,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -88,12 +88,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -122,11 +122,12 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/backend/bw6-761/plonk/plonk_test.go b/internal/backend/bw6-761/plonk/plonk_test.go deleted file mode 100644 index 03ef0f2b6f..0000000000 --- a/internal/backend/bw6-761/plonk/plonk_test.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2020 ConsenSys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -package plonk_test - -import ( - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" - - curve "github.com/consensys/gnark-crypto/ecc/bw6-761" - - "github.com/consensys/gnark/constraint/bw6-761" - - bw6_761plonk "github.com/consensys/gnark/internal/backend/bw6-761/plonk" - - "bytes" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/kzg" - "math/big" - "reflect" - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" -) - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define(api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints)+3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = bw6_761plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bw6_761plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = bw6_761plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution, fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := bw6_761plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bw6_761plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = bw6_761plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution, fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := bw6_761plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := bw6_761plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - // --------------------------------------------------------------------------------------------- - // bw6_761plonk.ProvingKey binary serialization - b.Run("pk: binary serialization (bw6_761plonk.ProvingKey)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - }) - b.Run("pk: binary deserialization (bw6_761plonk.ProvingKey)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - var pkReconstructed bw6_761plonk.ProvingKey - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = pkReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = pk.WriteTo(&buf) - } - - // --------------------------------------------------------------------------------------------- - // bw6_761plonk.Proof binary serialization - b.Run("proof: binary serialization (bw6_761plonk.Proof)", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - }) - b.Run("proof: binary deserialization (bw6_761plonk.Proof)", func(b *testing.B) { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - var proofReconstructed bw6_761plonk.Proof - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = proofReconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = proof.WriteTo(&buf) - } - -} - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} diff --git a/internal/backend/bw6-761/plonk/prove.go b/internal/backend/bw6-761/plonk/prove.go index d530571798..5a372eb87b 100644 --- a/internal/backend/bw6-761/plonk/prove.go +++ b/internal/backend/bw6-761/plonk/prove.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" @@ -92,22 +93,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall, lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall, lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall, lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall, lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall, lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall, lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -135,7 +136,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -147,9 +152,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -171,50 +176,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -231,10 +234,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -252,18 +254,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -287,7 +283,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -305,9 +301,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -319,22 +315,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -351,6 +360,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -362,7 +373,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -383,9 +394,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -404,9 +415,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -544,12 +555,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/backend/bw6-761/plonk/setup.go b/internal/backend/bw6-761/plonk/setup.go index 313302f9b3..096aba6f95 100644 --- a/internal/backend/bw6-761/plonk/setup.go +++ b/internal/backend/bw6-761/plonk/setup.go @@ -20,6 +20,7 @@ import ( "errors" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/fft" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/iop" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/kzg" "github.com/consensys/gnark/constraint/bw6-761" @@ -38,9 +39,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -52,8 +58,10 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element - S1Canonical, S2Canonical, S3Canonical []fr.Element + S1Canonical, S2Canonical, S3Canonical []fr.Element + + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 @@ -163,6 +171,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -254,6 +265,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -290,16 +337,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/generator/backend/main.go b/internal/generator/backend/main.go index 3173b1a6e6..df801f8650 100644 --- a/internal/generator/backend/main.go +++ b/internal/generator/backend/main.go @@ -178,12 +178,7 @@ func main() { panic(err) } - entries = []bavard.Entry{ - {File: filepath.Join(plonkDir, "plonk_test.go"), Templates: []string{"plonk/tests/plonk.go.tmpl", importCurve}}, - } - if err := bgen.Generate(d, "plonk_test", "./template/zkpschemes/", entries...); err != nil { - panic(err) - } + os.Remove(filepath.Join(plonkDir, "plonk_test.go")) // plonkfri entries = []bavard.Entry{ diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.marshal.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.marshal.go.tmpl index 0210eabcef..760f22b3d1 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.marshal.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.marshal.go.tmpl @@ -104,7 +104,6 @@ func (pk *ProvingKey) WriteTo(w io.Writer) (n int64, err error) { ([]fr.Element)(pk.Qo), ([]fr.Element)(pk.CQk), ([]fr.Element)(pk.LQk), - ([]fr.Element)(pk.EvaluationPermutationBigDomainBitReversed), ([]fr.Element)(pk.S1Canonical), ([]fr.Element)(pk.S2Canonical), ([]fr.Element)(pk.S3Canonical), @@ -150,7 +149,6 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { (*[]fr.Element)(&pk.Qo), (*[]fr.Element)(&pk.CQk), (*[]fr.Element)(&pk.LQk), - (*[]fr.Element)(&pk.EvaluationPermutationBigDomainBitReversed), (*[]fr.Element)(&pk.S1Canonical), (*[]fr.Element)(&pk.S2Canonical), (*[]fr.Element)(&pk.S3Canonical), @@ -163,6 +161,8 @@ func (pk *ProvingKey) ReadFrom(r io.Reader) (int64, error) { } } + pk.computeLagrangeCosetPolys() + return n + dec.BytesRead(), nil } diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl index 6192c01b76..af921fded2 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl @@ -3,6 +3,7 @@ import ( "math/big" "runtime" "time" + "sync" {{ template "import_fr" . }} {{ template "import_curve" . }} @@ -70,22 +71,22 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen evaluationLDomainSmall, evaluationRDomainSmall, evaluationODomainSmall := evaluateLROSmallDomain(spr, pk, solution) lagReg := iop.Form{Basis: iop.Lagrange, Layout: iop.Regular} - liop := iop.NewPolynomial(evaluationLDomainSmall,lagReg) - riop := iop.NewPolynomial(evaluationRDomainSmall,lagReg) - oiop := iop.NewPolynomial(evaluationODomainSmall,lagReg) - wliop := iop.NewWrappedPolynomial(liop) - wriop := iop.NewWrappedPolynomial(riop) - woiop := iop.NewWrappedPolynomial(oiop) + liop := iop.NewPolynomial(&evaluationLDomainSmall,lagReg) + riop := iop.NewPolynomial(&evaluationRDomainSmall,lagReg) + oiop := iop.NewPolynomial(&evaluationODomainSmall,lagReg) + wliop := liop.ShallowClone() + wriop := riop.ShallowClone() + woiop := oiop.ShallowClone() wliop.ToCanonical(&pk.Domain[0]).ToRegular() wriop.ToCanonical(&pk.Domain[0]).ToRegular() woiop.ToCanonical(&pk.Domain[0]).ToRegular() // Blind l, r, o before committing - // TODO @gbotrel check need for clone here. - bwliop := wliop.Clone().Blind(1) - bwriop := wriop.Clone().Blind(1) - bwoiop := woiop.Clone().Blind(1) - if err := commitToLRO(bwliop.Coefficients, bwriop.Coefficients, bwoiop.Coefficients, proof, pk.Vk.KZGSRS); err != nil { + // we set the underlying slice capacity to domain[1].Cardinality to minimize mem moves. + bwliop := wliop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwriop := wriop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + bwoiop := woiop.Clone(int(pk.Domain[1].Cardinality)).Blind(1) + if err := commitToLRO(bwliop.Coefficients(), bwriop.Coefficients(), bwoiop.Coefficients(), proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -113,7 +114,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // We could have not copied them at the cost of doing one more bit reverse // per poly... ziop, err := iop.BuildRatioCopyConstraint( - []*iop.Polynomial{liop.Clone(), riop.Clone(), oiop.Clone()}, + []*iop.Polynomial{ + liop.Clone(), + riop.Clone(), + oiop.Clone(), + }, pk.Permutation, beta, gamma, @@ -125,9 +130,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // commit to the blinded version of z - bwziop := iop.NewWrappedPolynomial(&ziop) + bwziop := ziop // iop.NewWrappedPolynomial(&ziop) bwziop.Blind(2) - proof.Z, err = kzg.Commit(bwziop.Coefficients, pk.Vk.KZGSRS, runtime.NumCPU()*2) + proof.Z, err = kzg.Commit(bwziop.Coefficients(), pk.Vk.KZGSRS, runtime.NumCPU()*2) if err != nil { return proof, err } @@ -149,50 +154,48 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen bwliop.ToLagrangeCoset(&pk.Domain[1]) bwriop.ToLagrangeCoset(&pk.Domain[1]) bwoiop.ToLagrangeCoset(&pk.Domain[1]) + + lagrangeCosetBitReversed := iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse} + + // we don't mutate so no need to clone the coefficients from the proving key. + wqliop := iop.NewPolynomial(&pk.lQl, lagrangeCosetBitReversed) + wqriop := iop.NewPolynomial(&pk.lQr, lagrangeCosetBitReversed) + wqmiop := iop.NewPolynomial(&pk.lQm, lagrangeCosetBitReversed) + wqoiop := iop.NewPolynomial(&pk.lQo, lagrangeCosetBitReversed) + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} - wqliop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Ql, canReg)) - wqriop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qr, canReg)) - wqmiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qm, canReg)) - wqoiop := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.Qo, canReg)) - - wqkiop := iop.NewWrappedPolynomial(iop.NewPolynomial(qkCompletedCanonical, canReg)) - wqliop.ToLagrangeCoset(&pk.Domain[1]) - wqriop.ToLagrangeCoset(&pk.Domain[1]) - wqmiop.ToLagrangeCoset(&pk.Domain[1]) - wqoiop.ToLagrangeCoset(&pk.Domain[1]) + wqkiop := iop.NewPolynomial(&qkCompletedCanonical, canReg) wqkiop.ToLagrangeCoset(&pk.Domain[1]) // storing Id id := make([]fr.Element, pk.Domain[1].Cardinality) id[1].SetOne() - widiop := iop.NewWrappedPolynomial(iop.NewPolynomial(id, canReg)) + widiop := iop.NewPolynomial(&id, canReg) widiop.ToLagrangeCoset(&pk.Domain[1]) - // put the permutations in LagrangeCoset - ws1 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S1Canonical, canReg)) - ws1.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws2 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S2Canonical, canReg)) - ws2.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) - - ws3 := iop.NewWrappedPolynomial(iop.NewPolynomial(pk.S3Canonical, canReg)) - ws3.ToCanonical(&pk.Domain[0]).ToRegular().ToLagrangeCoset(&pk.Domain[1]) + // permutations in LagrangeCoset: we don't mutate so no need to clone the coefficients from the + // proving key. + ws1 := iop.NewPolynomial(&pk.lS1LagrangeCoset, lagrangeCosetBitReversed) + ws2 := iop.NewPolynomial(&pk.lS2LagrangeCoset, lagrangeCosetBitReversed) + ws3 := iop.NewPolynomial(&pk.lS3LagrangeCoset, lagrangeCosetBitReversed) // Store z(g*x), without reallocating a slice - // TODO @gbotrel check this refactor need shallow copy - bwsziop := *bwziop - bwsziop.Shift(1) - bwziop.ToLagrangeCoset(&pk.Domain[1]) + bwsziop := bwziop.ShallowClone().Shift(1) + bwsziop.ToLagrangeCoset(&pk.Domain[1]) // L_{g^{0}} - lone := make([]fr.Element, pk.Domain[0].Cardinality) + cap := pk.Domain[1].Cardinality + if cap < pk.Domain[0].Cardinality { + cap = pk.Domain[0].Cardinality // sanity check + } + lone := make([]fr.Element, pk.Domain[0].Cardinality, cap) lone[0].SetOne() - loneiop := iop.NewPolynomial(lone, lagReg) - wloneiop := iop.NewWrappedPolynomial(loneiop.ToCanonical(&pk.Domain[0]). + loneiop := iop.NewPolynomial(&lone, lagReg) + wloneiop := loneiop.ToCanonical(&pk.Domain[0]). ToRegular(). - ToLagrangeCoset(&pk.Domain[1])) + ToLagrangeCoset(&pk.Domain[1]) - // Full capture usigng latest gnark crypto... + // Full capture using latest gnark crypto... fic := func(fql, fqr, fqm, fqo, fqk, l, r, o fr.Element) fr.Element { var ic, tmp fr.Element @@ -209,10 +212,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } fo := func(l, r, o, fid, fs1, fs2, fs3, fz, fzs fr.Element) fr.Element { - - var u, uu fr.Element - u.Set(&pk.Domain[0].FrMultiplicativeGen) - uu.Mul(&u, &pk.Domain[0].FrMultiplicativeGen) + var uu fr.Element + u := pk.Domain[0].FrMultiplicativeGen + uu.Mul(&u, &u) var a, b, tmp fr.Element a.Mul(&beta, &fid).Add(&a, &l).Add(&a, &gamma) @@ -230,18 +232,12 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen b.Sub(&b, &a) return b - } fone := func(fz, flone fr.Element) fr.Element { - - var one fr.Element - one.SetOne() - + one := fr.One() one.Sub(&fz, &one).Mul(&one, &flone) - return one - } // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -265,7 +261,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen ws2, ws3, bwziop, - &bwsziop, + bwsziop, wqliop, wqriop, wqmiop, @@ -283,9 +279,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen // compute kzg commitments of h1, h2 and h3 if err := commitToQuotient( - h.Coefficients[:pk.Domain[0].Cardinality+2], - h.Coefficients[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], - h.Coefficients[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[:pk.Domain[0].Cardinality+2], + h.Coefficients()[pk.Domain[0].Cardinality+2:2*(pk.Domain[0].Cardinality+2)], + h.Coefficients()[2*(pk.Domain[0].Cardinality+2):3*(pk.Domain[0].Cardinality+2)], proof, pk.Vk.KZGSRS); err != nil { return nil, err } @@ -297,22 +293,35 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen } // compute evaluations of (blinded version of) l, r, o, z at zeta - bwliop.ToCanonical(&pk.Domain[1]).ToRegular() - bwriop.ToCanonical(&pk.Domain[1]).ToRegular() - bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + var blzeta, brzeta, bozeta fr.Element + + var wgEvals sync.WaitGroup + wgEvals.Add(3) - // var blzeta, brzeta, bozeta fr.Element - blzeta := bwliop.Evaluate(zeta) - brzeta := bwriop.Evaluate(zeta) - bozeta := bwoiop.Evaluate(zeta) - // -> CORRECT + go func() { + bwliop.ToCanonical(&pk.Domain[1]).ToRegular() + blzeta = bwliop.Evaluate(zeta) + wgEvals.Done() + }() + go func() { + bwriop.ToCanonical(&pk.Domain[1]).ToRegular() + brzeta = bwriop.Evaluate(zeta) + wgEvals.Done() + }() + + go func() { + bwoiop.ToCanonical(&pk.Domain[1]).ToRegular() + bozeta = bwoiop.Evaluate(zeta) + wgEvals.Done() + }() + // open blinded Z at zeta*z bwziop.ToCanonical(&pk.Domain[1]).ToRegular() var zetaShifted fr.Element zetaShifted.Mul(&zeta, &pk.Vk.Generator) proof.ZShiftedOpening, err = kzg.Open( - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], zetaShifted, pk.Vk.KZGSRS, ) @@ -329,6 +338,8 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen errLPoly error ) + wgEvals.Wait() // wait for the evaluations + // compute the linearization polynomial r at zeta // (goal: save committing separately to z, ql, qr, qm, qo, k linearizedPolynomialCanonical = computeLinearizedPolynomial( @@ -340,7 +351,7 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen gamma, zeta, bzuzeta, - bwziop.Coefficients[:bwziop.BlindedSize()], + bwziop.Coefficients()[:bwziop.BlindedSize()], pk, ) @@ -361,9 +372,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen foldedHDigest.Add(&foldedHDigest, &proof.H[0]) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) + Comm(h1) // foldedH = h1 + ζ*h2 + ζ²*h3 - foldedH := h.Coefficients[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] - h2 := h.Coefficients[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] - h1 := h.Coefficients[:pk.Domain[0].Cardinality+2] + foldedH := h.Coefficients()[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)] + h2 := h.Coefficients()[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)] + h1 := h.Coefficients()[:pk.Domain[0].Cardinality+2] utils.Parallelize(len(foldedH), func(start, end int) { for i := start; i < end; i++ { foldedH[i].Mul(&foldedH[i], &zetaPowerm) // ζᵐ⁺²*h3 @@ -382,9 +393,9 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness fr.Vector, opt backen [][]fr.Element{ foldedH, linearizedPolynomialCanonical, - bwliop.Coefficients[:bwliop.BlindedSize()], - bwriop.Coefficients[:bwriop.BlindedSize()], - bwoiop.Coefficients[:bwoiop.BlindedSize()], + bwliop.Coefficients()[:bwliop.BlindedSize()], + bwriop.Coefficients()[:bwriop.BlindedSize()], + bwoiop.Coefficients()[:bwoiop.BlindedSize()], pk.S1Canonical, pk.S2Canonical, }, @@ -522,12 +533,12 @@ func computeLinearizedPolynomial(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - ps1 := iop.NewPolynomial(pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps1 := iop.NewPolynomial(&pk.S1Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) s1 = ps1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - ps2 := iop.NewPolynomial(pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) tmp := ps2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl index 8a3734fb1a..810078c132 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl @@ -4,6 +4,7 @@ import ( {{- template "import_fr" . }} {{- template "import_fft" . }} {{- template "import_backend_cs" . }} + "github.com/consensys/gnark-crypto/ecc/{{toLower .Curve}}/fr/iop" kzgg "github.com/consensys/gnark-crypto/kzg" ) @@ -20,9 +21,14 @@ type ProvingKey struct { // Verifying Key is embedded into the proving key (needed by Prove) Vk *VerifyingKey + // TODO store iop.Polynomial here, not []fr.Element for more "type safety" + // qr,ql,qm,qo (in canonical basis). Ql, Qr, Qm, Qo []fr.Element + // qr,ql,qm,qo (in lagrange coset basis) --> these are not serialized, but computed from Ql, Qr, Qm, Qo once. + lQl, lQr, lQm, lQo []fr.Element + // LQk (CQk) qk in Lagrange basis (canonical basis), prepended with as many zeroes as public inputs. // Storing LQk in Lagrange basis saves a fft... CQk, LQk []fr.Element @@ -34,9 +40,11 @@ type ProvingKey struct { // Domain[0], Domain[1] fft.Domain // Permutation polynomials - EvaluationPermutationBigDomainBitReversed []fr.Element S1Canonical, S2Canonical, S3Canonical []fr.Element + // in lagrange coset basis --> these are not serialized, but computed from S1Canonical, S2Canonical, S3Canonical once. + lS1LagrangeCoset, lS2LagrangeCoset, lS3LagrangeCoset []fr.Element + // position -> permuted position (position in [0,3*sizeSystem-1]) Permutation []int64 } @@ -145,6 +153,9 @@ func Setup(spr *cs.SparseR1CS, srs *kzg.SRS) (*ProvingKey, *VerifyingKey, error) // set s1, s2, s3 ccomputePermutationPolynomials(&pk) + // compute the lagrange coset basis versions (not serialized) + pk.computeLagrangeCosetPolys() + // Commit to the polynomials to set up the verifying key var err error if vk.Ql, err = kzg.Commit(pk.Ql, vk.KZGSRS); err != nil { @@ -236,6 +247,42 @@ func buildPermutation(spr *cs.SparseR1CS, pk *ProvingKey) { } } +func (pk *ProvingKey) computeLagrangeCosetPolys() { + canReg := iop.Form{Basis: iop.Canonical, Layout: iop.Regular} + wqliop := iop.NewPolynomial(clone(pk.Ql, pk.Domain[1].Cardinality), canReg) + wqriop := iop.NewPolynomial(clone(pk.Qr, pk.Domain[1].Cardinality), canReg) + wqmiop := iop.NewPolynomial(clone(pk.Qm, pk.Domain[1].Cardinality), canReg) + wqoiop := iop.NewPolynomial(clone(pk.Qo, pk.Domain[1].Cardinality), canReg) + + ws1 := iop.NewPolynomial(clone(pk.S1Canonical, pk.Domain[1].Cardinality), canReg) + ws2 := iop.NewPolynomial(clone(pk.S2Canonical, pk.Domain[1].Cardinality), canReg) + ws3 := iop.NewPolynomial(clone(pk.S3Canonical, pk.Domain[1].Cardinality), canReg) + + wqliop.ToLagrangeCoset(&pk.Domain[1]) + wqriop.ToLagrangeCoset(&pk.Domain[1]) + wqmiop.ToLagrangeCoset(&pk.Domain[1]) + wqoiop.ToLagrangeCoset(&pk.Domain[1]) + + ws1.ToLagrangeCoset(&pk.Domain[1]) + ws2.ToLagrangeCoset(&pk.Domain[1]) + ws3.ToLagrangeCoset(&pk.Domain[1]) + + pk.lQl = wqliop.Coefficients() + pk.lQr = wqriop.Coefficients() + pk.lQm = wqmiop.Coefficients() + pk.lQo = wqoiop.Coefficients() + + pk.lS1LagrangeCoset = ws1.Coefficients() + pk.lS2LagrangeCoset = ws2.Coefficients() + pk.lS3LagrangeCoset = ws3.Coefficients() +} + +func clone(input []fr.Element, capacity uint64) *[]fr.Element { + res := make([]fr.Element, len(input), capacity) + copy(res, input) + return &res +} + // ccomputePermutationPolynomials computes the LDE (Lagrange basis) of the permutations // s1, s2, s3. // @@ -269,16 +316,6 @@ func ccomputePermutationPolynomials(pk *ProvingKey) { fft.BitReverse(pk.S1Canonical) fft.BitReverse(pk.S2Canonical) fft.BitReverse(pk.S3Canonical) - - // evaluation of permutation on the big domain - pk.EvaluationPermutationBigDomainBitReversed = make([]fr.Element, 3*pk.Domain[1].Cardinality) - copy(pk.EvaluationPermutationBigDomainBitReversed, pk.S1Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:], pk.S2Canonical) - copy(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], pk.S3Canonical) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[:pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[pk.Domain[1].Cardinality:2*pk.Domain[1].Cardinality], fft.DIF, true) - pk.Domain[1].FFT(pk.EvaluationPermutationBigDomainBitReversed[2*pk.Domain[1].Cardinality:], fft.DIF, true) - } // getIDSmallDomain returns the Lagrange form of ID on the small domain diff --git a/internal/generator/backend/template/zkpschemes/plonk/tests/marshal.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/tests/marshal.go.tmpl index 5922b565ea..2c5ed3824d 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/tests/marshal.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/tests/marshal.go.tmpl @@ -51,12 +51,12 @@ func roundTripCheck(t *testing.T, from io.WriterTo, reconstructed io.ReaderFrom) var buf bytes.Buffer written, err := from.WriteTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -72,12 +72,12 @@ func roundTripCheckRaw(t *testing.T, from gnarkio.WriterRawTo, reconstructed io. var buf bytes.Buffer written, err := from.WriteRawTo(&buf) if err != nil { - t.Fatal("coudln't serialize", err) + t.Fatal("couldn't serialize", err) } read, err := reconstructed.ReadFrom(&buf) if err != nil { - t.Fatal("coudln't deserialize", err) + t.Fatal("couldn't deserialize", err) } if !reflect.DeepEqual(from, reconstructed) { @@ -107,12 +107,13 @@ func (pk *ProvingKey) randomize() { pk.S1Canonical = randomScalars(n) pk.S2Canonical = randomScalars(n) pk.S3Canonical = randomScalars(n) - pk.EvaluationPermutationBigDomainBitReversed = randomScalars(n) pk.Permutation = make([]int64, 3*pk.Domain[0].Cardinality) pk.Permutation[0] = -12 pk.Permutation[len(pk.Permutation)-1] = 8888 + + pk.computeLagrangeCosetPolys() } func (vk *VerifyingKey) randomize() { diff --git a/internal/generator/backend/template/zkpschemes/plonk/tests/plonk.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/tests/plonk.go.tmpl deleted file mode 100644 index 4c086681f1..0000000000 --- a/internal/generator/backend/template/zkpschemes/plonk/tests/plonk.go.tmpl +++ /dev/null @@ -1,197 +0,0 @@ -import ( - {{ template "import_fr" . }} - {{ template "import_curve" . }} - {{ template "import_backend_cs" . }} - {{ template "import_plonk" . }} - {{ template "import_kzg" . }} - "bytes" - "math/big" - "testing" - "reflect" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/backend" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend/cs/scs" - -) - -{{/* TODO this is duplicate with groth16 tests tempalte */}} - - -//--------------------// -// benches // -//--------------------// - -type refCircuit struct { - nbConstraints int - X frontend.Variable - Y frontend.Variable `gnark:",public"` -} - -func (circuit *refCircuit) Define( api frontend.API) error { - for i := 0; i < circuit.nbConstraints; i++ { - circuit.X = api.Mul(circuit.X, circuit.X) - } - api.AssertIsEqual(circuit.X, circuit.Y) - return nil -} - -func referenceCircuit() (constraint.ConstraintSystem, frontend.Circuit, *kzg.SRS) { - const nbConstraints = 40000 - circuit := refCircuit{ - nbConstraints: nbConstraints, - } - ccs, err := frontend.Compile(curve.ID.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - - var good refCircuit - good.X = (2) - - // compute expected Y - var expectedY fr.Element - expectedY.SetUint64(2) - - for i := 0; i < nbConstraints; i++ { - expectedY.Mul(&expectedY, &expectedY) - } - - good.Y = (expectedY) - srs, err := kzg.NewSRS(ecc.NextPowerOfTwo(nbConstraints) + 3, new(big.Int).SetUint64(42)) - if err != nil { - panic(err) - } - - return ccs, &good, srs -} - -func BenchmarkSetup(b *testing.B) { - ccs, _, srs := referenceCircuit() - - b.ResetTimer() - - b.Run("setup", func(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _ = {{toLower .CurveID}}plonk.Setup(ccs.(*cs.SparseR1CS), srs) - } - }) -} - -func BenchmarkProver(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution,fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := {{toLower .CurveID}}plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = {{toLower .CurveID}}plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector),backend.ProverConfig{}) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkVerifier(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution,fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - publicWitness, err := frontend.NewWitness(_solution,fr.Modulus(), frontend.PublicOnly()) - if err != nil { - b.Fatal(err) - } - - pk, vk, err := {{toLower .CurveID}}plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := {{toLower .CurveID}}plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{}) - if err != nil { - panic(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = {{toLower .CurveID}}plonk.Verify(proof, vk, publicWitness.Vector().(fr.Vector)) - } -} - - - -func BenchmarkSerialization(b *testing.B) { - ccs, _solution, srs := referenceCircuit() - fullWitness, err := frontend.NewWitness(_solution,fr.Modulus()) - if err != nil { - b.Fatal(err) - } - - pk, _, err := {{toLower .CurveID}}plonk.Setup(ccs.(*cs.SparseR1CS), srs) - if err != nil { - b.Fatal(err) - } - - proof, err := {{toLower .CurveID}}plonk.Prove(ccs.(*cs.SparseR1CS), pk, fullWitness.Vector().(fr.Vector), backend.ProverConfig{} ) - if err != nil { - b.Fatal(err) - } - - b.ReportAllocs() - - {{ $base := toLower .CurveID }} - - {{ template "benchBinarySerialization" dict "Type" (print $base "plonk.ProvingKey") "Name" "pk" }} - {{ template "benchBinarySerialization" dict "Type" (print $base "plonk.Proof") "Name" "proof" }} - - -} - -{{ define "benchBinarySerialization" }} - // --------------------------------------------------------------------------------------------- - // {{$.Type}} binary serialization - b.Run("{{$.Name}}: binary serialization ({{$.Type}})", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - var buf bytes.Buffer - _, _ = {{- $.Name}}.WriteTo(&buf) - } - }) - b.Run("{{$.Name}}: binary deserialization ({{$.Type}})", func(b *testing.B) { - var buf bytes.Buffer - _, _ = {{$.Name}}.WriteTo(&buf) - var {{ $.Name}}Reconstructed {{$.Type}} - b.ResetTimer() - for i := 0; i < b.N; i++ { - buf := bytes.NewBuffer(buf.Bytes()) - _, _ = {{- $.Name}}Reconstructed.ReadFrom(buf) - } - }) - { - var buf bytes.Buffer - _, _ = {{$.Name}}.WriteTo(&buf) - } - -{{ end }} - - - - - - -var tVariable reflect.Type - -func init() { - tVariable = reflect.ValueOf(struct{ A frontend.Variable }{}).FieldByName("A").Type() -} From 2753ae60ed8f6de4fc793d4ec3cbcb9291d52236 Mon Sep 17 00:00:00 2001 From: Gautam Botrel Date: Tue, 14 Feb 2023 14:33:34 -0600 Subject: [PATCH 3/3] feat: update gnark version to v0.8.0 --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 84ae2e9545..697944b7d7 100644 --- a/doc.go +++ b/doc.go @@ -22,7 +22,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" ) -var Version = semver.MustParse("0.8.0-alpha") +var Version = semver.MustParse("0.8.0") // Curves return the curves supported by gnark func Curves() []ecc.ID {